aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pack.c81
-rw-r--r--pack.h1
-rw-r--r--test.c54
-rw-r--r--test.do2
4 files changed, 137 insertions, 1 deletions
diff --git a/pack.c b/pack.c
new file mode 100644
index 0000000..55fc224
--- /dev/null
+++ b/pack.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 Tomasz Kramkowski <tk@the-tk.com>
+ * SPDX-License-Identifier: MIT
+ */
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "common.h"
+#include "pack.h"
+#include "trace.h"
+
+static void write_val(unsigned char *buf, size_t size, enum endian e, uintmax_t val)
+{
+ for (size_t i = 0; i < size; i++)
+ buf[i] = (val >> (e == LITTLE ? i : size - i - 1) * 8) & 0xff;
+}
+
+enum pack_status pack(void *buf_, size_t size, const char *fmt, ...)
+{
+ enum endian endianness = BIG;
+ unsigned char *buf = buf_;
+ size_t offset = 0;
+ va_list ap;
+
+ tr_call("pack(%p, %zu, %s, ...)", buf, size, fmt);
+
+ va_start(ap, fmt);
+
+ for (int i = 0; fmt[i] != '\0'; i++) {
+ bool sign;
+ size_t s;
+ union { uintmax_t u; intmax_t s; } val;
+ tr_debug("i: %d, fmt[i]: %c", i, fmt[i]);
+ sign = islower(fmt[i]);
+ switch (fmt[i]) {
+ case '>': endianness = BIG; continue;
+ case '<': endianness = LITTLE; continue;
+ case 'b': val.s = va_arg(ap, int ); break;
+ case 'B': val.u = va_arg(ap, unsigned ); break;
+ case 'h': val.s = va_arg(ap, int ); break;
+ case 'H': val.u = va_arg(ap, unsigned ); break;
+ case 'i': val.s = va_arg(ap, int ); break;
+ case 'I': val.u = va_arg(ap, unsigned ); break;
+ case 'l': val.s = va_arg(ap, long ); break;
+ case 'L': val.u = va_arg(ap, unsigned long ); break;
+ case 'q': val.s = va_arg(ap, long long); break;
+ case 'Q': val.u = va_arg(ap, unsigned long long); break;
+ case 'x': val.u = 0; sign = false; break;
+ default: return PACK_FMTINVAL;
+ }
+ tr_debug("i: %d, fmt[i]: %c, sign: %ssigned", i, fmt[i], sign ? "" : "un");
+
+ s = getsize(fmt[i]);
+ tr_debug("s: %zu", s);
+ if (s == (size_t)-1) return PACK_FMTINVAL;
+
+ if (size - offset < s) return PACK_TOOSMALL;
+
+ if (sign) {
+ intmax_t n = val.s;
+ tr_debug("val.s: %" PRIdMAX, val.s);
+ if (val.s >= 0) {
+ val.u = n;
+ } else {
+ uintmax_t offt = BITMASK(s * 8);
+ n += 1;
+ val.u = offt + n;
+ }
+ }
+ tr_debug("val.u: %" PRIuMAX, val.u);
+ write_val(&buf[offset], s, endianness, val.u);
+ offset += s;
+ }
+
+ va_end(ap);
+
+ return PACK_OK;
+}
diff --git a/pack.h b/pack.h
index c68b861..b8139d5 100644
--- a/pack.h
+++ b/pack.h
@@ -20,6 +20,7 @@ extern enum pack_trace {
PACK_TRACE_ALL,
} pack_trace;
+enum pack_status pack(void *dest, size_t size, const char *fmt, ...);
enum pack_status unpack(const void *buf, size_t size, const char *fmt, ...);
const char *pack_strerror(enum pack_status status);
diff --git a/test.c b/test.c
index 35813d0..adc20a1 100644
--- a/test.c
+++ b/test.c
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: MIT
*/
#include <stdint.h>
+#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdbool.h>
@@ -32,8 +33,47 @@ struct test {
return false; \
} \
} while (0)
+
#define CHECK_EQUAL(f, a, b) if (a != b) { printf(__FILE__ ":%d %"f" != %"f"\n", __LINE__, a, b); return false; }
+#define CHECK_PACK(dest, fmt, ...) do { \
+ enum pack_status CHECK_PACK_s = pack(dest, sizeof (dest), fmt, __VA_ARGS__); \
+ if (CHECK_PACK_s != PACK_OK) { \
+ printf(__FILE__ ":%d pack(%p, %zu, " #fmt ", ...) -> %s (%d)\n", __LINE__, dest, sizeof (dest), pack_strerror(CHECK_PACK_s), CHECK_PACK_s); \
+ return false; \
+ } \
+} while (0)
+
+#define CHECK_BUFEQUAL(a, b) \
+ static_assert(sizeof (a) == sizeof (b), "CHECK_BUFEQUAL - buffer sizes don't match"); \
+ if (!do_check_bufequal(__FILE__, __LINE__, (a), (b), sizeof (a))) return false;
+
+static bool do_check_bufequal(const char *file, int line, void *a_, void *b_, size_t s)
+{
+ unsigned char *a = a_, *b = b_;
+ bool mismatch = false;
+ size_t start, end;
+ for (size_t i = 0; i < s; i++) {
+ if (a[i] == b[i]) continue;
+ if (!mismatch) {
+ start = i;
+ mismatch = true;
+ }
+ end = i + 1;
+ }
+ if (!mismatch) return true;
+ if (start > 0) start--;
+ if (end < s) end++;
+ printf("%s:%d buffers unequal\n idx", file, line);
+ for (size_t i = start; i < end; i++) printf(" %02zu", i);
+ printf("\n a =");
+ for (size_t i = start; i < end; i++) printf(" %02x", a[i]);
+ printf("\n b =");
+ for (size_t i = start; i < end; i++) printf(" %02x", b[i]);
+ putchar('\n');
+ return false;
+}
+
#include "test.inc"
TEST(unpack_simple0_float, "unpack simple float")
@@ -85,6 +125,20 @@ TEST(unpack_simple_padding, "unpack simple padding")
return true;
}
+TEST(pack_simple_general, "pack simple general")
+{
+ unsigned char b[12] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+
+ CHECK_PACK(b, ">ixxxBxxl", -123, 22, -12648430l);
+ CHECK_BUFEQUAL(b, DATA(0xff, 0x85,
+ 0x00, 0x00, 0x00,
+ 0x16,
+ 0x00, 0x00,
+ 0xff, 0x3f, 0x00, 0x12));
+
+ return true;
+}
+
int main(void)
{
extern struct test __start_tests, __stop_tests;
diff --git a/test.do b/test.do
index 23f1a0d..f599d5f 100644
--- a/test.do
+++ b/test.do
@@ -1,3 +1,3 @@
#!/bin/bash
-deps=(test.o common.o unpack.o trace.o)
+deps=(test.o common.o pack.o unpack.o trace.o)
. ./link