diff options
-rw-r--r-- | pack.c | 81 | ||||
-rw-r--r-- | pack.h | 1 | ||||
-rw-r--r-- | test.c | 54 | ||||
-rw-r--r-- | test.do | 2 |
4 files changed, 137 insertions, 1 deletions
@@ -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; +} @@ -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); @@ -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; @@ -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 |