aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--unpack.c146
1 files changed, 77 insertions, 69 deletions
diff --git a/unpack.c b/unpack.c
index 3143931..836faf3 100644
--- a/unpack.c
+++ b/unpack.c
@@ -15,6 +15,18 @@
#include "pack.h"
#include "trace.h"
+struct dest {
+ enum pack_type type;
+ union ptr {
+ #define T(type, sign, c_type, va_type) sign c_type *type;
+ ITYPE_MACROS
+ #undef T
+ float *f;
+ double *d;
+ } ptr;
+ size_t count;
+};
+
static uintmax_t read_val(const unsigned char *buf, size_t size, enum pack_endian e)
{
uintmax_t val = 0;
@@ -36,6 +48,57 @@ static intmax_t minval(size_t s)
}
}
+static void read_fields(struct dest dest, const void *src_, enum pack_endian endianness)
+{
+ union {
+ uintmax_t unsigned_;
+ intmax_t signed_;
+ float f;
+ double d;
+ } val;
+ const unsigned char *src = src_;
+ size_t s = getsize(dest.type);
+
+ for (size_t i = 0; i < dest.count; i++) {
+ val.unsigned_ =
+ read_val(&src[s * i], s, endianness);
+ tr_debug("val.u: %" PRIuMAX ", at: %" PRIuSIZE,
+ val.unsigned_, s * i);
+
+ if (dest.type == PACK_TYPE_FLOAT) {
+ float f = ieee754b32_deserialise(val.unsigned_);
+ val.f = f;
+ tr_debug("val.f: %f", val.f);
+ } else if (dest.type == PACK_TYPE_DOUBLE) {
+ double d = ieee754b64_deserialise(val.unsigned_);
+ val.d = d;
+ tr_debug("val.d: %f", val.d);
+ } else if (islower((char)dest.type)) {
+ intmax_t vals;
+ if (!(val.unsigned_ & (UINTMAX_C(1) << (s * 8 - 1)))) {
+ vals = val.unsigned_;
+ } else {
+ vals = minval(s);
+ vals += val.unsigned_ ^ (UINTMAX_C(1) << (s * 8 - 1));
+ }
+ val.signed_ = vals;
+ tr_debug("val.s: %" PRIdMAX, val.signed_);
+ }
+
+ switch (dest.type) {
+ #define T(type, sign, c_type, va_type) \
+ case (char)PACK_TYPE_##type: \
+ dest.ptr.type[i] = val.sign##_; \
+ break;
+ ITYPE_MACROS
+ #undef T
+ case PACK_TYPE_FLOAT: dest.ptr.f[i] = val.f; break;
+ case PACK_TYPE_DOUBLE: dest.ptr.d[i] = val.d; break;
+ case PACK_TYPE_PADDING: break;
+ }
+ }
+}
+
enum pack_status unpack(const void *buf_, size_t size, const char *fmt, ...)
{
enum pack_endian endianness = PACK_ENDIAN_BIG;
@@ -49,23 +112,8 @@ enum pack_status unpack(const void *buf_, size_t size, const char *fmt, ...)
va_start(ap, fmt);
for (int i = 0; fmt[i] != '\0'; i++) {
- bool sign;
- size_t count = 1, s;
- enum pack_type t;
- union {
- #define T(type, sign, c_type, va_type) \
- sign c_type *type;
- ITYPE_MACROS
- #undef T
- float *f;
- double *d;
- } arg;
- union {
- uintmax_t unsigned_;
- intmax_t signed_;
- float f;
- double d;
- } val;
+ size_t s;
+ struct dest dest = { .count = 1 };
tr_debug("i: %d, fmt[i]: %c", i, fmt[i]);
if (isdigit(fmt[i])) {
unsigned long long c;
@@ -75,80 +123,40 @@ enum pack_status unpack(const void *buf_, size_t size, const char *fmt, ...)
c = strtoull(&fmt[i], &end, 10);
if ((c == ULLONG_MAX && errno == ERANGE) || c > SIZE_MAX)
SET_AND_GOTO(ret, PACK_FMTINVAL, stop);
- count = c;
+ dest.count = c;
i += end - &fmt[i];
} else if (fmt[i] == '*') {
- count = va_arg(ap, size_t);
+ dest.count = va_arg(ap, size_t);
i++;
}
- sign = islower(fmt[i]);
- tr_debug("count: %" PRIuSIZE ", i: %d, fmt[i]: %c, sign: %ssigned",
- count, i, fmt[i], sign ? "" : "un");
+ tr_debug("dest.count: %" PRIuSIZE ", i: %d, fmt[i]: %c", dest.count, i, fmt[i]);
switch (fmt[i]) {
case '>': endianness = PACK_ENDIAN_BIG; continue;
case '<': endianness = PACK_ENDIAN_LITTLE; continue;
#define T(type, sign, c_type, va_type) \
case (char)PACK_TYPE_##type: \
- arg.type = va_arg(ap, sign c_type *); \
+ dest.ptr.type = va_arg(ap, sign c_type *); \
break;
ITYPE_MACROS
#undef T
- case PACK_TYPE_FLOAT: arg.f = va_arg(ap, float *); break;
- case PACK_TYPE_DOUBLE: arg.d = va_arg(ap, double *); break;
+ case PACK_TYPE_FLOAT: dest.ptr.f = va_arg(ap, float *); break;
+ case PACK_TYPE_DOUBLE: dest.ptr.d = va_arg(ap, double *); break;
case PACK_TYPE_PADDING: break;
default: SET_AND_GOTO(ret, PACK_FMTINVAL, stop);
}
- t = fmt[i];
+ dest.type = fmt[i];
- s = getsize(t);
+ s = getsize(dest.type);
tr_debug("s: %" PRIuSIZE, s);
if (s == (size_t)-1) SET_AND_GOTO(ret, PACK_FMTINVAL, stop);
- if (size - offset < s * count)
+ if (size - offset < s * dest.count)
SET_AND_GOTO(ret, PACK_TOOSMALL, stop);
- if (t == PACK_TYPE_PADDING) goto skip;
-
- for (size_t j = 0; j < count; j++) {
- val.unsigned_ =
- read_val(buf + offset + s * j, s, endianness);
- tr_debug("val.u: %" PRIuMAX ", at: %" PRIuSIZE,
- val.unsigned_, offset + s * j);
-
- if (t == PACK_TYPE_FLOAT) {
- float f = ieee754b32_deserialise(val.unsigned_);
- val.f = f;
- tr_debug("val.f: %f", val.f);
- } else if (t == PACK_TYPE_DOUBLE) {
- double d = ieee754b64_deserialise(val.unsigned_);
- val.d = d;
- tr_debug("val.d: %f", val.d);
- } else if (sign) {
- intmax_t vals;
- if (!(val.unsigned_ & (UINTMAX_C(1) << (s * 8 - 1)))) {
- vals = val.unsigned_;
- } else {
- vals = minval(s);
- vals += val.unsigned_ ^ (UINTMAX_C(1) << (s * 8 - 1));
- }
- val.signed_ = vals;
- tr_debug("val.s: %" PRIdMAX, val.signed_);
- }
+ if (dest.type != PACK_TYPE_PADDING)
+ read_fields(dest, &buf[offset], endianness);
- switch (t) {
- #define T(type, sign, c_type, va_type) \
- case (char)PACK_TYPE_##type: \
- arg.type[j] = val.sign##_; \
- break;
- ITYPE_MACROS
- #undef T
- case PACK_TYPE_FLOAT: arg.f[j] = val.f; break;
- case PACK_TYPE_DOUBLE: arg.d[j] = val.d; break;
- case PACK_TYPE_PADDING: break;
- }
- }
-skip:
- offset += s * count;
+ offset += s * dest.count;
}
stop: