diff options
-rw-r--r-- | unpack.c | 146 |
1 files changed, 77 insertions, 69 deletions
@@ -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: |