diff options
-rw-r--r-- | pack.h | 13 | ||||
-rw-r--r-- | unpack.c | 60 |
2 files changed, 73 insertions, 0 deletions
@@ -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); @@ -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; +} |