Add a helper function to normalize struct timespec values so that the nanoseconds part is less than 1 second and has the same sign as the seconds part (if the seconds part is not 0).
Normalization is required to ensure we can safely convert timespec values to wayland protocol data, i.e, to tv_sec_hi, tv_sec_lo, tv_sec_nsec triplets, and will be used in upcoming commits. Signed-off-by: Alexandros Frantzis <[email protected]> --- shared/timespec-util.h | 28 ++++++++++++++++++++++ tests/timespec-test.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/shared/timespec-util.h b/shared/timespec-util.h index f9736c27..a10edf5b 100644 --- a/shared/timespec-util.h +++ b/shared/timespec-util.h @@ -33,6 +33,34 @@ #define NSEC_PER_SEC 1000000000 +/* Normalize a timespec + * + * \param r[out] normalized timespec + * \param a[in] timespec to normalize + * + * Normalize a timespec so that tv_nsec is less than 1 second + * and has the same sign as tv_sec (if tv_sec is non-zero). + * + */ +static inline void +timespec_normalize(struct timespec *r, const struct timespec *a) +{ + if (a->tv_nsec >= NSEC_PER_SEC || a->tv_nsec <= -NSEC_PER_SEC) { + r->tv_sec = a->tv_sec + a->tv_nsec / NSEC_PER_SEC; + r->tv_nsec = a->tv_nsec % NSEC_PER_SEC; + } else { + *r = *a; + } + + if (r->tv_sec > 0 && r->tv_nsec < 0) { + r->tv_sec -= 1; + r->tv_nsec += NSEC_PER_SEC; + } else if (r->tv_sec < 0 && r->tv_nsec > 0) { + r->tv_sec += 1; + r->tv_nsec -= NSEC_PER_SEC; + } +} + /* Subtract timespecs * * \param r[out] result: a - b diff --git a/tests/timespec-test.c b/tests/timespec-test.c index f127bcee..8c2296d1 100644 --- a/tests/timespec-test.c +++ b/tests/timespec-test.c @@ -38,6 +38,71 @@ #include "shared/helpers.h" #include "zunitc/zunitc.h" +ZUC_TEST(timespec_test, timespec_normalize) +{ + struct timespec a, r; + + a.tv_sec = 0; + a.tv_nsec = 0; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, 0); + ZUC_ASSERT_EQ(r.tv_nsec, 0); + + a.tv_sec = 1; + a.tv_nsec = NSEC_PER_SEC - 1; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, 1); + ZUC_ASSERT_EQ(r.tv_nsec, NSEC_PER_SEC - 1); + + a.tv_sec = 1; + a.tv_nsec = NSEC_PER_SEC; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, 2); + ZUC_ASSERT_EQ(r.tv_nsec, 0); + + a.tv_sec = 1; + a.tv_nsec = NSEC_PER_SEC + 1; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, 2); + ZUC_ASSERT_EQ(r.tv_nsec, 1); + + a.tv_sec = -1; + a.tv_nsec = -NSEC_PER_SEC; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, -2); + ZUC_ASSERT_EQ(r.tv_nsec, 0); + + a.tv_sec = -1; + a.tv_nsec = -(NSEC_PER_SEC + 1); + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, -2); + ZUC_ASSERT_EQ(r.tv_nsec, -1); + + a.tv_sec = -3; + a.tv_nsec = NSEC_PER_SEC + 1; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, -1); + ZUC_ASSERT_EQ(r.tv_nsec, -(NSEC_PER_SEC - 1)); + + a.tv_sec = 3; + a.tv_nsec = -(NSEC_PER_SEC + 1); + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, 1); + ZUC_ASSERT_EQ(r.tv_nsec, NSEC_PER_SEC - 1); + + a.tv_sec = -1; + a.tv_nsec = 2 * NSEC_PER_SEC + 1; + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, 1); + ZUC_ASSERT_EQ(r.tv_nsec, 1); + + a.tv_sec = 1; + a.tv_nsec = -(2 * NSEC_PER_SEC + 1); + timespec_normalize(&r, &a); + ZUC_ASSERT_EQ(r.tv_sec, -1); + ZUC_ASSERT_EQ(r.tv_nsec, -1); +} + ZUC_TEST(timespec_test, timespec_sub) { struct timespec a, b, r; -- 2.14.1 _______________________________________________ wayland-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/wayland-devel
