aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2020-08-20 21:34:34 +0100
committerTomasz Kramkowski <tk@the-tk.com>2020-08-20 21:34:34 +0100
commit2e1ff80dab0cee0f197dccc9c5dd2f3b9afdb168 (patch)
treebfe7ad4524a3ae1665e30c2ccb1ce838fbabf0d9
downloadpack-2e1ff80dab0cee0f197dccc9c5dd2f3b9afdb168.tar.gz
pack-2e1ff80dab0cee0f197dccc9c5dd2f3b9afdb168.tar.xz
pack-2e1ff80dab0cee0f197dccc9c5dd2f3b9afdb168.zip
init
-rw-r--r--.gitignore2
-rw-r--r--Makefile1
-rw-r--r--common.c33
-rw-r--r--common.h8
-rw-r--r--pack.h16
-rw-r--r--unpack.c123
-rw-r--r--unpack_test.c110
7 files changed, 293 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..64c75c2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+*_test
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5d2ed5d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1 @@
+unpack_test: unpack_test.o unpack.o common.o
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..106fc7a
--- /dev/null
+++ b/common.c
@@ -0,0 +1,33 @@
+#include <stddef.h>
+
+#include "common.h"
+#include "pack.h"
+
+size_t getsize(char c)
+{
+ switch (c) {
+ case 'b': case 'B':
+ case 'x':
+ return 1;
+ case 'h': case 'H':
+ case 'i': case 'I':
+ return 2;
+ case 'l': case 'L':
+ case 'f':
+ return 4;
+ case 'q': case 'Q':
+ case 'd':
+ return 8;
+ case 's': default: return (size_t)-1;
+ }
+}
+
+const char *pack_strerror(enum pack_status status)
+{
+ switch (status) {
+ case PACK_OK: return "Success";
+ case PACK_FMTINVAL: return "Invalid format parameter";
+ case PACK_TOOSMALL: return "Buffer too small";
+ default: return "Invalid Status";
+ }
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..f5a39c9
--- /dev/null
+++ b/common.h
@@ -0,0 +1,8 @@
+#ifndef PACK_INTERNAL_H
+#define PACK_INTERNAL_H
+
+#include <stddef.h>
+
+size_t getsize(char c);
+
+#endif // !PACK_INTERNAL_H
diff --git a/pack.h b/pack.h
new file mode 100644
index 0000000..7a8fc69
--- /dev/null
+++ b/pack.h
@@ -0,0 +1,16 @@
+#ifndef PACK_H
+#define PACK_H
+
+#include <stddef.h>
+
+enum pack_status {
+ PACK_OK,
+ PACK_FMTINVAL,
+ PACK_TOOSMALL,
+};
+
+enum pack_status unpack(void *buf, size_t size, const char *fmt, ...);
+
+const char *pack_strerror(enum pack_status status);
+
+#endif // !PACK_H
diff --git a/unpack.c b/unpack.c
new file mode 100644
index 0000000..2617e9a
--- /dev/null
+++ b/unpack.c
@@ -0,0 +1,123 @@
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <stdio.h>
+
+#include "common.h"
+#include "pack.h"
+
+typedef enum pack_status unpacker(void *buf, size_t size, va_list ap);
+
+enum endian { BIG, LITTLE };
+
+uintmax_t read_val(unsigned char *buf, size_t size, enum endian e)
+{
+ unsigned long long val = 0;
+
+ for (size_t i = 0; i < size; i++)
+ val |= (buf[i] & 0xff) << (e == LITTLE ? i : size - i - 1) * 8;
+
+ return val;
+}
+#define X(M) \
+ M(b, signed char ) \
+ M(B, unsigned char ) \
+ M(h, short ) \
+ M(H, unsigned short ) \
+ M(i, int ) \
+ M(I, unsigned ) \
+ M(l, long ) \
+ M(L, unsigned long ) \
+ M(q, long long) \
+ M(Q, unsigned long long)
+
+enum pack_status unpack(void *buf_, size_t size, const char *fmt, ...)
+{
+ enum endian endianness = BIG;
+ unsigned char *buf = buf_;
+ size_t offset = 0;
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ for (int i = 0; fmt[i] != '\0'; i++) {
+ bool sign = islower(fmt[i]);
+ size_t s;
+ union {
+ signed char *b;
+ unsigned char *B;
+ short *h;
+ unsigned short *H;
+ int *i;
+ unsigned int *I;
+ long *l;
+ unsigned long *L;
+ long long *q;
+ unsigned long long *Q;
+ } arg;
+ /*void *arg;*/
+ union { uintmax_t u; intmax_t s; } val;
+ switch (fmt[i]) {
+ case '>': endianness = BIG; continue;
+ case '<': endianness = LITTLE; continue;
+ case 'b': arg.b = va_arg(ap, signed char *);
+ case 'B': arg.B = va_arg(ap, unsigned char *);
+ case 'h': arg.h = va_arg(ap, short *);
+ case 'H': arg.H = va_arg(ap, unsigned short *);
+ case 'i': arg.i = va_arg(ap, int *);
+ case 'I': arg.I = va_arg(ap, unsigned *);
+ case 'l': arg.l = va_arg(ap, long *);
+ case 'L': arg.L = va_arg(ap, unsigned long *);
+ case 'q': arg.q = va_arg(ap, long long *);
+ case 'Q': arg.Q = va_arg(ap, unsigned long long *);
+ case 'x': break;
+ return PACK_FMTINVAL;
+ }
+
+ s = getsize(fmt[i]);
+ if (s == (size_t)-1) return PACK_FMTINVAL;
+
+ if (size - offset < s) return PACK_TOOSMALL;
+
+ if (fmt[i] == 'x') goto skip;
+
+ val.u = read_val(buf, s, endianness);
+
+ fprintf(stderr, "%zu, %llu\n", s, val.u);
+
+ if (sign) {
+ intmax_t vals;
+ if (!(val.u & (1llu << (s * 8 - 1)))) {
+ vals = val.u;
+ } else {
+ uintmax_t offt = UINTMAX_MAX >> (sizeof offt * CHAR_BIT - s * 8);
+ vals = val.u - offt - 1;
+ }
+ val.s = vals;
+ fprintf(stderr, "signed %lld\n", val.s);
+ }
+
+ switch (fmt[i]) {
+#define D(t, f) fprintf(stderr, "(%p) "#t " = %" #f "\n", (void *)arg.t, *arg.t)
+ case 'b': *arg.b = val.s; D(b, d); break;
+ case 'B': *arg.B = val.u; D(B, u); break;
+ case 'h': *arg.h = val.s; D(h, d); break;
+ case 'H': *arg.H = val.u; D(H, u); break;
+ case 'i': *arg.i = val.s; D(i, d); break;
+ case 'I': *arg.I = val.u; D(I, u); break;
+ case 'l': *arg.l = val.s; D(l, ld); break;
+ case 'L': *arg.L = val.u; D(L, lu); break;
+ case 'q': *arg.q = val.s; D(q, lld); break;
+ case 'Q': *arg.Q = val.u; D(Q, llu); break;
+ }
+skip:
+ offset += s;
+ }
+
+ va_end(ap);
+
+ return PACK_OK;
+}
diff --git a/unpack_test.c b/unpack_test.c
new file mode 100644
index 0000000..8eee6b3
--- /dev/null
+++ b/unpack_test.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "pack.h"
+
+typedef bool test_func(void);
+
+struct test {
+ test_func *func;
+ char *desc;
+};
+
+#define TEST(name) bool test_##name(void)
+#define TEST_ENTRY(name, description) { test_##name, description }
+#define TEST_ENTRY_END { NULL, NULL }
+#define DATA(...) (unsigned char []){ __VA_ARGS__ }, sizeof (unsigned char []){ __VA_ARGS__ }
+#define CHECK(test) if (!(test)) { puts("! " #test); return false; }
+
+#define CHECK_UNPACK(data, fmt, ...) do { \
+ enum pack_status s = unpack(data, fmt, __VA_ARGS__); \
+ if (s != PACK_OK) { \
+ printf(__FILE__ ":%d unpack(" #data ", " #fmt ", ...) -> %s (%d)\n", __LINE__, pack_strerror(s), s); \
+ return false; \
+ } \
+} while (0)
+#define CHECK_EQUAL(a, b) if (a != b) { printf(__FILE__ ":%d %d != %d\n", __LINE__, a, b); return false; }
+
+TEST(schar)
+{
+ signed char c = __LINE__;
+ fprintf(stderr, "Address of c: %p\n", (void *)&c);
+ CHECK_UNPACK(DATA(0), "b", &c);
+ CHECK_EQUAL(c, 0);
+ CHECK_UNPACK(DATA(1), "b", &c);
+ CHECK_EQUAL(c, 1);
+ CHECK_UNPACK(DATA(127), "b", &c);
+ CHECK_EQUAL(c, 127);
+ CHECK_UNPACK(DATA(255), "b", &c);
+ CHECK_EQUAL(c, -1);
+ CHECK_UNPACK(DATA(128), "b", &c);
+ CHECK_EQUAL(c, -128);
+ return true;
+}
+
+TEST(uchar)
+{
+ unsigned char c = __LINE__;
+ fprintf(stderr, "Address of c: %p\n", (void *)&c);
+ CHECK_UNPACK(DATA(0), "B", &c);
+ CHECK_EQUAL(c, 0);
+ CHECK_UNPACK(DATA(1), "B", &c);
+ CHECK_EQUAL(c, 1);
+ CHECK_UNPACK(DATA(255), "B", &c);
+ CHECK_EQUAL(c, 255);
+ return true;
+}
+
+TEST(sshort)
+{
+ short s = __LINE__;
+ fprintf(stderr, "Address of s: %p\n", (void *)&s);
+ CHECK_UNPACK(DATA(0, 0), "h", &s);
+ CHECK_EQUAL(s, 0);
+ CHECK_UNPACK(DATA(0, 0), ">h", &s);
+ CHECK_EQUAL(s, 0);
+ CHECK_UNPACK(DATA(0, 0), "<h", &s);
+ CHECK_EQUAL(s, 0);
+ CHECK_UNPACK(DATA(0, 1), "h", &s);
+ CHECK_EQUAL(s, 1);
+ CHECK_UNPACK(DATA(0, 1), ">h", &s);
+ CHECK_EQUAL(s, 1);
+ CHECK_UNPACK(DATA(1, 0), "<h", &s);
+ CHECK_EQUAL(s, 1);
+ CHECK_UNPACK(DATA(0x7f, 0xff), "h", &s);
+ CHECK_EQUAL(s, 32767);
+ CHECK_UNPACK(DATA(0x7f, 0xff), ">h", &s);
+ CHECK_EQUAL(s, 32767);
+ CHECK_UNPACK(DATA(0xff, 0x7f), "<h", &s);
+ CHECK_EQUAL(s, 32767);
+ CHECK_UNPACK(DATA(0xff, 0xff), "h", &s);
+ CHECK_EQUAL(s, -1);
+ CHECK_UNPACK(DATA(0xff, 0xff), ">h", &s);
+ CHECK_EQUAL(s, -1);
+ CHECK_UNPACK(DATA(0xff, 0xff), "<h", &s);
+ CHECK_EQUAL(s, -1);
+ CHECK_UNPACK(DATA(0x80, 0x00), "h", &s);
+ CHECK_EQUAL(s, -32768);
+ CHECK_UNPACK(DATA(0x80, 0x00), ">h", &s);
+ CHECK_EQUAL(s, -32768);
+ CHECK_UNPACK(DATA(0x00, 0x80), "<h", &s);
+ CHECK_EQUAL(s, -32768);
+}
+
+TEST(ushort)
+{
+ unsigned short s;
+}
+
+int main(void)
+{
+ struct test tests[] = {
+ TEST_ENTRY(schar, "schar unpacking"),
+ TEST_ENTRY(uchar, "uchar unpacking"),
+ TEST_ENTRY(sshort, "sshort unpacking"),
+ TEST_ENTRY_END
+ };
+
+ for (int i = 0; tests[i].func != NULL; i++)
+ printf("%s %s\n", tests[i].func() ? " OK " : "FAIL", tests[i].desc);
+}