Implicit assumptions (was: Re: Some fun with -O2)
Walter von Entferndt
walter.von.entferndt at posteo.net
Sun Jan 17 18:58:56 UTC 2021
At Samstag, 16. Januar 2021, 23:08:30 CET, Mark Millard wrote:
> [... on POSIX ...]
> You can use that to help judge if only supporting time_t as an
> integer type is worthwhile in your view.
>
Yes, I think it's worth it. I'll try to find out if there's some
implementation around with a floating point *time_t*, or with a *time_t*
longer than 8 bytes or shorter than 4. I'd strongly assume there's
(currently) not, because < 32 bit or > 8 byte *time_t* support an unreasonably
short (or long, resp.) time frame. Special applications will very likely use
their own data format and not *time_t*. With an 8-byte *time_t* you could
even represent the time of big bang with a 1-second precision, right?
This is a version with explicit assignments (and thus type conversions) in the
routine /guess_time_t_max ()/ and another loop susceptible for overflow fixed,
and the formatting done according to the genuine file. I hope this is maximal
portable, /but/ I guess it will not compile on old compilers due to the use of
the (new?) *uint_max_t* and *uint64_t* types.
--- check_mktime.c.patch ---
--- check_mktime.c.orig 2021-01-15 03:19:33.962253000 +0100
+++ check_mktime.c 2021-01-17 19:33:55.695872000 +0100
@@ -3,6 +3,11 @@
# include <sys/time.h>
# include <time.h>
# include <unistd.h>
+# include <stdlib.h> /* putenv(), exit(), EXIT_xxx */
+# include <stdint.h> /* uintmax_t */
+# include <stdio.h> /* printf() */
+# include <limits.h> /* CHAR_BIT, INT_MAX */
+# include <inttypes.h> /* format spec PRIX64: ll/l + X on 32/64-bit arch */
/* Work around redefinition to rpl_putenv by other config tests. */
#undef putenv
@@ -16,6 +21,80 @@
};
#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))
+/* The following two routines (should) work for integer representation
+ in either 2's or 1's complement (else it's a compiler bug). */
+
+/* Count the bits set in any integer type.
+ Returns the precision (width - padding bits - sign bit) iff given
+ the xxx_MAX value of any integer type, signed or unsigned.
+ From SEI CERT C Coding Standard:
+ Rules for Developing Safe, Reliable, and Secure Systems (2016) */
+size_t
+popcount (num)
+uintmax_t num;
+{
+ size_t cnt = 0;
+
+ while (num != 0)
+ {
+ if (num % 2 == 1)
+ cnt++;
+ num >>= 1;
+ }
+ return cnt;
+}
+#define PRECISION(max_value) popcount(max_value)
+#define SIGN_BIT (1)
+
+/* Guess the maximum value of a time_t from it's storage width.
+ ASSERT time_t is not a floating point, or of any arcane width,
+ or unsigned (i.e. the bit pattern of (-1) treated special).
+ Only 4...8 byte width of a time_t are tested.
+ On error: returns (time_t)(-1) */
+time_t
+guess_time_t_max ()
+{
+ time_t t0, t1 = (time_t)(-1);
+ size_t size, prec = 1;
+ uint64_t u64;
+
+ switch ((size = sizeof(time_t)))
+ {
+ case 4:
+ t0 = (time_t) 0xFFFFFFFF;
+ break;
+ case 5:
+ t0 = (time_t) 0xFFFFFFFFFF;
+ break;
+ case 6:
+ t0 = (time_t) 0xFFFFFFFFFFFF;
+ break;
+ case 7:
+ t0 = (time_t) 0xFFFFFFFFFFFFFF;
+ break;
+ case 8:
+ t0 = (time_t) 0xFFFFFFFFFFFFFFFF;
+ break;
+ default:
+ prec = 0;
+ break;
+ }
+ if (prec)
+ {
+ prec = PRECISION(t0) - SIGN_BIT;
+ t0 = (time_t) 1 << (prec - 1);
+ t1 = t0|(t0 - 1);
+ }
+#ifdef DEBUG
+ fprintf (stderr, "time_t_max\t= 0x%"PRIX64"\n", (u64 = t1));
+ fprintf (stderr, "sizeof(time_t)\t= %2zd byte\n", size);
+ fprintf (stderr, "precision\t= %2zd bit\n", prec);
+ fprintf (stderr, "padding\t\t= %2zd bit\n", size*CHAR_BIT - prec -
SIGN_BIT);
+#endif /* DEBUG */
+ return t1;
+}
+#undef SIGN_BIT
+
/* Fail if mktime fails to convert a date in the spring-forward gap.
Based on a problem report from Andreas Jaeger. */
static void
@@ -37,8 +116,8 @@
tm.tm_min = 0;
tm.tm_sec = 0;
tm.tm_isdst = -1;
- if (mktime (&tm) == (time_t)-1)
- exit (1);
+ if (mktime (&tm) == (time_t)(-1))
+ exit (EXIT_FAILURE);
}
static void
@@ -47,10 +126,10 @@
{
struct tm *lt;
if ((lt = localtime (&now)) && mktime (lt) != now)
- exit (1);
+ exit (EXIT_FAILURE);
now = time_t_max - now;
if ((lt = localtime (&now)) && mktime (lt) != now)
- exit (1);
+ exit (EXIT_FAILURE);
}
static void
@@ -67,7 +146,7 @@
tm.tm_isdst = -1;
mktime (&tm);
if (tm.tm_mon != 2 || tm.tm_mday != 31)
- exit (1);
+ exit (EXIT_FAILURE);
}
static void
@@ -82,7 +161,7 @@
alarm (10);
now = mktime (&tm);
alarm (0);
- if (now != (time_t) -1)
+ if (now != (time_t)(-1))
{
struct tm *lt = localtime (&now);
if (! (lt
@@ -96,7 +175,7 @@
&& lt->tm_wday == tm.tm_wday
&& ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
== (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
- exit (1);
+ exit (EXIT_FAILURE);
}
}
@@ -106,9 +185,10 @@
time_t t, delta;
int i, j;
- for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2)
- continue;
- time_t_max--;
+ time_t_max = guess_time_t_max ();
+ if (time_t_max == (time_t)(-1))
+ exit (EXIT_FAILURE);
+
delta = time_t_max / 997; /* a suitable prime number */
for (i = 0; i < N_STRINGS; i++)
{
@@ -120,11 +200,15 @@
mktime_test ((time_t) 60 * 60);
mktime_test ((time_t) 60 * 60 * 24);
- for (j = 1; 0 < j; j *= 2)
+ /* NOTE This does not reach very large values > time_t_max/2
+ neither the (susceptible for overflow) version
+ for (j = 1; 0 < j; j *= 2) does */
+ for (i = j = 1; i < PRECISION(INT_MAX); i++, j *= 2)
bigtime_test (j);
bigtime_test (j - 1);
}
irix_6_4_bug ();
spring_forward_gap ();
- exit (0);
+ exit (EXIT_SUCCESS);
}
+/*! vi: set ai tabstop=8 shiftwidth=2: */
---
--
=|o) "Stell' Dir vor es geht und keiner kriegt's hin." (Wolfgang Neuss)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: check_mktime.c.patch
Type: text/x-patch
Size: 4613 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20210117/bb81ebb0/attachment.bin>
More information about the freebsd-hackers
mailing list