diff options
| author | 2026-10-26 09:03:57 +0000 | |
|---|---|---|
| committer | 2026-11-06 10:29:06 +0100 | |
| commit | 1d3b54c784da260f71d885f5f560d69c211a645e (patch) | |
| tree | 6aa7b84ae67249d740bd4c43500a349e403ca2e6 /test/date_val.c | |
| parent | 9fdbf585f47501ba19a53fd4bb382d5b533e3fdd (diff) | |
| download | libplist-1d3b54c784da260f71d885f5f560d69c211a645e.tar.gz libplist-1d3b54c784da260f71d885f5f560d69c211a645e.tar.bz2 | |
plist: fix the (sec, usec) date API round trip
plist_set_date_val stores val = sec + usec / 1000000, but
plist_get_date_val splits it with a truncating cast and fabs():
*sec = (int32_t)val;
val = fabs((val - (int64_t)val) * 1000000);
*usec = (int32_t)val;
Two results are wrong. The product is truncated, so set(5, 6) reads
back as (5, 5) - 29.3% of round trips over sec in [0, 3] come back a
microsecond short. And for a negative value, truncation toward zero
plus fabs() give a pair that does not denote the same instant:
set(-3, 250000) stores -2.75 and reads back as (-2, 750000), which the
setter would store as -1.25. Every round trip with sec < 0 and
usec != 0 is wrong.
(int32_t)val is also undefined when the stored value is outside the
int32_t range.
Round the seconds down, round the fraction to microseconds carrying
1000000 into the seconds, and clamp the seconds before converting, so
sec + usec / 1000000 == val to the microsecond with 0 <= usec <
1000000 - the convention the setter uses.
Add test/date_val.c covering fixed cases and a sweep over sec in
[-2, 2]. It fails on an unmodified tree - set(5, 6), set(-3, 250000)
and set(-1, 1) all come back as different instants - and passes with
this change, with make check at 44/44.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Arn4YaWtcQBDsQuJtyBmFP
Diffstat (limited to 'test/date_val.c')
| -rw-r--r-- | test/date_val.c | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/test/date_val.c b/test/date_val.c new file mode 100644 index 0000000..127e194 --- /dev/null +++ b/test/date_val.c @@ -0,0 +1,42 @@ +#include <stdio.h> +#include <stdint.h> +#include <plist/plist.h> + +/* plist_get_date_val must return exactly what plist_set_date_val stored, + * including dates before the epoch (negative seconds). */ + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +static int check(int32_t sec, int32_t usec) +{ + int32_t s = 0, u = 0; + plist_t d = plist_new_date(0, 0); + plist_set_date_val(d, sec, usec); + plist_get_date_val(d, &s, &u); + plist_free(d); + if (s != sec || u != usec) { + printf("set(%d, %d) -> get(%d, %d)\n", sec, usec, s, u); + return 1; + } + return 0; +} + +int main(int argc, char** argv) +{ + int err = 0; + int32_t sec, usec; + + err |= check(5, 6); + err |= check(0, 999999); + err |= check(-3, 250000); + err |= check(-1, 1); + err |= check(1000000000, 123456); + for (sec = -2; sec <= 2 && !err; sec++) { + for (usec = 0; usec < 1000000 && !err; usec += 7) { + err |= check(sec, usec); + } + } + return err; +} |
