diff options
author | Tomasz Kramkowski <tk@the-tk.com> | 2021-05-31 15:50:48 +0100 |
---|---|---|
committer | Tomasz Kramkowski <tk@the-tk.com> | 2021-05-31 17:38:33 +0100 |
commit | 13ceac75a8ed37ddc02df2b41627329c4da24b04 (patch) | |
tree | f3b8117821cc804282f02559f226af2275720d94 | |
parent | a1aec78f10f8821b5d6b29b664c40bb7f02758a5 (diff) | |
download | pack-13ceac75a8ed37ddc02df2b41627329c4da24b04.tar.gz pack-13ceac75a8ed37ddc02df2b41627329c4da24b04.tar.xz pack-13ceac75a8ed37ddc02df2b41627329c4da24b04.zip |
unpack: Fix negative signed integer unpacking calculation
Currently the calculation is just wrong since it effectively relies on
the widths of the signed and unsigned integer types and on
wrapping-on-assignment behavior. I have no idea why I wrote it like
this, it seems obviously wrong in retrospect.
The new behavior ensure that this succeeds in situations where type
widths are not correct.
The new code sets vals to the minimum possible two's complement signed
value (I just realised this code now assumes two's complement) and then
offsets it with the value part of the signed integer.
-rw-r--r-- | unpack.c | 16 |
1 files changed, 13 insertions, 3 deletions
@@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Tomasz Kramkowski <tk@the-tk.com> + * Copyright (C) 2020-2021 Tomasz Kramkowski <tk@the-tk.com> * SPDX-License-Identifier: MIT */ #include <ctype.h> @@ -60,6 +60,16 @@ static uintmax_t read_val(const unsigned char *buf, size_t size, enum endian e) return val; } +static intmax_t minval(size_t s) +{ + switch (s) { + case 1: return INTMAX_C(-128); + case 2: return INTMAX_C(-32768); + case 4: return INTMAX_C(-2147483648); + default: return -INTMAX_C(9223372036854775807) - 1; + } +} + enum pack_status unpack(const void *buf_, size_t size, const char *fmt, ...) { enum endian endianness = BIG; @@ -151,8 +161,8 @@ enum pack_status unpack(const void *buf_, size_t size, const char *fmt, ...) if (!(val.u & (UINTMAX_C(1) << (s * 8 - 1)))) { vals = val.u; } else { - uintmax_t offt = BITMASK(s * 8); - vals = val.u - offt - 1; + vals = minval(s); + vals += val.u ^ (UINTMAX_C(1) << (s * 8 - 1)); } val.s = vals; tr_debug("val.s: %" PRIdMAX, val.s); |