return true;
}
+static bool test_normalize_timespec(struct torture_context *tctx)
+{
+ const struct {
+ time_t in_s; long in_ns;
+ time_t out_s; long out_ns;
+ } data [] = {
+ { 0, 0, 0, 0 }
+ , { 1, 0, 1, 0 }
+ , { -1, 0, -1, 0 }
+ , { 0, 1000000000, 1, 0 }
+ , { 0, 2000000000, 2, 0 }
+ , { 0, 1000000001, 1, 1 }
+ , { 0, 2000000001, 2, 1 }
+ , { 0, -1000000000, -1, 0 }
+ , { 0, -2000000000, -2, 0 }
+ , { 0, -1000000001, -2, 999999999 }
+ , { 0, -2000000001, -3, 999999999 }
+ , { 0, -1, -1, 999999999 }
+ , { 1, -1, 0, 999999999 }
+ , { -1, -1, -2, 999999999 }
+ , { 0, 999999999, 0, 999999999 }
+ , { 0, 1999999999, 1, 999999999 }
+ , { 0, 2999999999, 2, 999999999 }
+ , { 0, -999999999, -1, 1 }
+ , { 0, -1999999999, -2, 1 }
+ , { 0, -2999999999, -3, 1 }
+ , { LONG_MAX, 1000000001, LONG_MAX, 999999999 } /* overflow */
+ , { LONG_MAX, 999999999, LONG_MAX, 999999999 } /* harmless */
+ , { LONG_MAX, -1, LONG_MAX-1, 999999999 } /* -1 */
+ , { LONG_MIN, -1000000001, LONG_MIN, 0 } /* overflow */
+ , { LONG_MIN, 0, LONG_MIN, 0 } /* harmless */
+ , { LONG_MIN, 1000000000, LONG_MIN+1, 0 } /* +1 */
+ };
+ int i;
+
+ for (i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
+ struct timespec ts = (struct timespec)
+ { .tv_sec = data[i].in_s
+ , .tv_nsec = data[i].in_ns };
+
+ normalize_timespec(&ts);
+
+ torture_assert_int_equal(tctx, ts.tv_sec, data[i].out_s,
+ "mismatch in tv_sec");
+ torture_assert_int_equal(tctx, ts.tv_nsec, data[i].out_ns,
+ "mismatch in tv_nsec");
+ }
+
+ return true;
+}
+
struct torture_suite *torture_local_util_time(TALLOC_CTX *mem_ctx)
{
struct torture_suite *suite = torture_suite_create(mem_ctx, "time");
test_http_timestring);
torture_suite_add_simple_test(suite, "timestring",
test_timestring);
+ torture_suite_add_simple_test(suite, "normalize_timespec",
+ test_normalize_timespec);
return suite;
}
#endif
+#define NSEC_PER_SEC 1000000000
/**
External access to time_t_min and time_t_max.
time_t convert_timespec_to_time_t(struct timespec ts)
{
/* Ensure tv_nsec is less than 1sec. */
- while (ts.tv_nsec > 1000000000) {
- ts.tv_sec += 1;
- ts.tv_nsec -= 1000000000;
- }
+ normalize_timespec(&ts);
/* 1 ns == 1,000,000,000 - one thousand millionths of a second.
increment if it's greater than 500 millionth of a second. */
{
struct timeval tv = convert_timespec_to_timeval(*ts);
*ts = convert_timeval_to_timespec(tv);
- while (ts->tv_nsec > 1000000000) {
- ts->tv_sec += 1;
- ts->tv_nsec -= 1000000000;
- }
+ normalize_timespec(ts);
}
/****************************************************************************
ret.tv_nsec = get_ctimensec(pst);
return ret;
}
+
+/****************************************************************************
+ Deal with nanoseconds overflow.
+****************************************************************************/
+
+void normalize_timespec(struct timespec *ts)
+{
+ lldiv_t dres;
+
+ /* most likely case: nsec is valid */
+ if ((unsigned long)ts->tv_nsec < NSEC_PER_SEC) {
+ return;
+ }
+
+ dres = lldiv(ts->tv_nsec, NSEC_PER_SEC);
+
+ /* if the operation would result in overflow, max out values and bail */
+ if (dres.quot > 0) {
+ if ((int64_t)LONG_MAX - dres.quot < ts->tv_sec) {
+ ts->tv_sec = LONG_MAX;
+ ts->tv_nsec = NSEC_PER_SEC - 1;
+ return;
+ }
+ } else {
+ if ((int64_t)LONG_MIN - dres.quot > ts->tv_sec) {
+ ts->tv_sec = LONG_MIN;
+ ts->tv_nsec = 0;
+ return;
+ }
+ }
+
+ ts->tv_nsec = dres.rem;
+ ts->tv_sec += dres.quot;
+
+ /* if the ns part was positive or a multiple of -1000000000, we're done */
+ if (ts->tv_nsec > 0 || dres.rem == 0) {
+ return;
+ }
+
+ ts->tv_nsec += NSEC_PER_SEC;
+ --ts->tv_sec;
+}