Skip site navigation (1)Skip section navigation (2)
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>