Date: Sun, 17 Jan 2021 19:58:36 +0100 From: Walter von Entferndt <walter.von.entferndt@posteo.net> To: Mark Millard <marklmi@yahoo.com> Cc: freebsd-hackers@freebsd.org Subject: Re: Implicit assumptions (was: Re: Some fun with -O2) Message-ID: <5658698.5PxWuGnUD2@t450s.local.lan> In-Reply-To: <ABC6559D-66E6-4671-A11A-FA225B55C9B6@yahoo.com> References: <mailman.29.1610625600.45116.freebsd-hackers@freebsd.org> <1648904.MsCH1bHPGx@t450s.local.lan> <ABC6559D-66E6-4671-A11A-FA225B55C9B6@yahoo.com>
next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format. --nextPart5028971.ppSauYnzuk Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" 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) --nextPart5028971.ppSauYnzuk Content-Disposition: attachment; filename="check_mktime.c.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="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: */ --nextPart5028971.ppSauYnzuk--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?5658698.5PxWuGnUD2>