aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pack.h13
-rw-r--r--unpack.c60
2 files changed, 73 insertions, 0 deletions
diff --git a/pack.h b/pack.h
index 7fd54a5..d43bdcf 100644
--- a/pack.h
+++ b/pack.h
@@ -42,8 +42,21 @@ enum pack_endian {
PACK_ENDIAN_LITTLE
};
+struct pack_field {
+ enum pack_type type;
+ size_t count;
+ size_t offset;
+};
+
+struct pack_args {
+ enum pack_endian endian;
+ struct pack_field *fields;
+ size_t num_fields;
+};
+
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, ...);
+enum pack_status unpack_struct(const void *buf, size_t size, const struct pack_args *args, void *dest);
const char *pack_strerror(enum pack_status status);
diff --git a/unpack.c b/unpack.c
index 8fd1d94..995eac8 100644
--- a/unpack.c
+++ b/unpack.c
@@ -165,3 +165,63 @@ stop:
return ret;
}
+
+enum pack_status unpack_struct(const void *buf_, size_t size,
+ const struct pack_args *args, void *dest_)
+{
+ const unsigned char *buf = buf_;
+ char *dest = dest_;
+ size_t offset = 0;
+
+ tr_call("unpack_struct(%p, %" PRIuSIZE ", %p, %p)", \
+ (const void *)buf, size, (const void *)args, (const void *)dest);
+
+ tr_debug("args->endian: %s, args->fields: %p, args->num_fields: %" PRIuSIZE,
+ args->endian == PACK_ENDIAN_BIG ? "big" : "little",
+ (const void *)args->fields, args->num_fields);
+
+ for (size_t i = 0; i < args->num_fields; i++) {
+ #define FIELD (void *)&dest[f->offset]
+ const struct pack_field *f = &args->fields[i];
+ struct pack_field indirect;
+ struct dest field;
+ size_t s;
+
+ if (f->type == PACK_TYPE_COUNT) {
+ if (i + 1 >= args->num_fields) return PACK_FMTINVAL;
+ indirect = args->fields[++i];
+ indirect.count = *(size_t *)FIELD;
+ f = &indirect;
+ }
+
+ s = getsize(f->type);
+ if (s == (size_t)-1) return PACK_FMTINVAL;
+
+ tr_debug("i: %" PRIuSIZE ", f->type: %c, f->count: %" PRIuSIZE ", "
+ "f->offset: %" PRIuSIZE ", s: %" PRIuSIZE,
+ i, (char)f->type, f->count, f->offset, s);
+
+ field.type = f->type;
+ switch (field.type) {
+ #define T(type, sign, c_type, va_type) \
+ case PACK_TYPE_##type: field.ptr.type = FIELD; break;
+ ITYPE_MACROS
+ #undef T
+ case PACK_TYPE_FLOAT: field.ptr.f = FIELD; break;
+ case PACK_TYPE_DOUBLE: field.ptr.d = FIELD; break;
+ case PACK_TYPE_PADDING: break;
+ default: return PACK_FMTINVAL;
+ }
+ field.count = f->count;
+
+ if (size - offset < s * field.count) return PACK_TOOSMALL;
+
+ if (f->type != PACK_TYPE_PADDING)
+ read_fields(field, &buf[offset], args->endian);
+
+ offset += s * field.count;
+ #undef FIELD
+ }
+
+ return PACK_OK;
+}