Proposed 64-bit changes

Paul Eggert eggert at CS.UCLA.EDU
Fri Apr 22 21:37:17 UTC 2005


Here are a few detailed comments about porting issues involved in the
current draft.  To be honest I don't really follow the code, and I
haven't had time to check it out with outlandish time stamps, but I
figure I should send what I have.

First, the type name "signed64_t" is a bit misleading, since long long
is not necessarily a 64-bit type (it might be longer).  I suggest
using the standard name "int_fast64_t" instead: this is the type of
an integer that is at least 64 bits wide, and may be wider, and is
reasonably fast.  C99 defines this type, and the code can use that
if available, falling back to long long (or long) otherwise.

In a few places in localtime.c, the code uses signed64_t when it could
use time_t (even if time_t is floating).

"#define SECSPERREPEAT 12622780800" doesn't work on 32-bit C89
compilers: they report a compile-time error when that constant is
used, since it's out of range.

gcc sometimes warns if you compare an integer value to an unsigned value.
(A cast silences this.)

zic.c's wild-compilation check assumes CHAR_BIT == 8.

The call to qsort uses types that don't conform to C89, because the
comparison function is supposed to take const void * args, not void *
args.  (This is a problem in the current code too, come to think of
it.)

is32 assumes that integers use two's complement representation.  It's
easy to remove this assumption here.  (I realize the assumption is
probably elsewhere too.)

There's a missing cast in the call to puttzcode, that would be
necessary on K&R compilers.  (Are we still worried about K&R
compilers?)

Here's a proposed patch:

===================================================================
RCS file: RCS/Makefile,v
retrieving revision 2005.8
diff -pu -r2005.8 Makefile
--- Makefile	2005/04/04 15:24:31	2005.8
+++ Makefile	2005/04/22 19:36:45
@@ -95,6 +95,7 @@ LDLIBS=
 #  -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4)
 #  -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD)
 #  -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD)
+#  -DHAVE_STDINT_H=1 if you have a pre-C99 compiler with "stdint.h"
 #  -DHAVE_STRERROR=0 if your system lacks the strerror function
 #  -DHAVE_SYMLINK=0 if your system lacks the symlink function
 #  -DHAVE_SYS_STAT_H=0 if your compiler lacks a "sys/stat.h"
===================================================================
RCS file: RCS/private.h,v
retrieving revision 2005.5.1.1
diff -pu -r2005.5.1.1 private.h
--- private.h	2005/04/21 19:24:10	2005.5.1.1
+++ private.h	2005/04/22 20:58:10
@@ -25,12 +25,6 @@ static char	privatehid[] = "@(#)private.
 #endif /* !defined NOID */
 #endif /* !defined lint */
 
-#ifndef SIGNED64_T
-#define SIGNED64_T	long long
-#endif /* !defined SIGNED64_T */
-
-typedef SIGNED64_T	signed64_t;
-
 /*
 ** Defaults for preprocessor symbols.
 ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
@@ -52,6 +46,10 @@ typedef SIGNED64_T	signed64_t;
 #define HAVE_SETTIMEOFDAY	3
 #endif /* !defined HAVE_SETTIMEOFDAY */
 
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H		(199901 <= __STDC_VERSION__)
+#endif /* !defined HAVE_STDINT_H */
+
 #ifndef HAVE_STRERROR
 #define HAVE_STRERROR		1
 #endif /* !defined HAVE_STRERROR */
@@ -93,7 +91,7 @@ typedef SIGNED64_T	signed64_t;
 #include "stdio.h"
 #include "errno.h"
 #include "string.h"
-#include "limits.h"	/* for CHAR_BIT */
+#include "limits.h"
 #include "time.h"
 #include "stdlib.h"
 
@@ -128,6 +126,25 @@ typedef SIGNED64_T	signed64_t;
 /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
 #define is_digit(c) ((unsigned)(c) - '0' <= 9)
 
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+#ifdef LLONG_MAX
+#define int_fast64_t long long int
+#else /* !defined LLONG_MAX */
+#define int_fast64_t long int
+#endif /* !defined LLONG_MAX */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - 2147483647)
+#endif /* !defined INT32_MIN */
+#ifndef INT32_MAX
+#define INT32_MAX 2147483647
+#endif /* !defined INT32_MAX */
+
 /*
 ** Workarounds for compilers/systems.
 */
@@ -318,10 +335,21 @@ char *ctime_r P((time_t const *, char *)
 #define YEARSPERREPEAT		400	/* years before a Gregorian repeat */
 #endif /* !defined YEARSPERREPEAT */
 
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR		31556952
+#endif /* !defined AVGSECSPERYEAR */
+
 #ifndef SECSPERREPEAT
-#define SECSPERREPEAT		12622780800
+#define SECSPERREPEAT		(YEARSPERREPEAT * (time_t) AVGSECSPERYEAR)
 #endif /* !defined SECSPERREPEAT */
 
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined LG_SECSPERREPEAT */
+
 /*
 ** UNIX was a registered trademark of The Open Group in 2003.
 */
===================================================================
RCS file: RCS/localtime.c,v
retrieving revision 2005.5.1.1
diff -pu -r2005.5.1.1 localtime.c
--- localtime.c	2005/04/21 19:24:10	2005.5.1.1
+++ localtime.c	2005/04/22 20:46:19
@@ -126,6 +126,7 @@ struct rule {
 
 static long		detzcode P((const char * codep));
 static time_t		detzcode64 P((const char * codep));
+static int		differ_by_repeat P((time_t t1, time_t t0));
 static const char *	getzname P((const char * strp));
 static const char *	getnum P((const char * strp, int * nump, int min,
 				int max));
@@ -220,7 +221,7 @@ const char * const	codep;
 
 	result = (codep[0] & 0x80) ? ~0L : 0L;
 	for (i = 0; i < 4; ++i)
-		result = (result << 8) | (codep[i] & 0xff);
+		result = 256 * result + (codep[i] & 0xff);
 	return result;
 }
 
@@ -228,13 +229,13 @@ static time_t
 detzcode64(codep)
 const char * const	codep;
 {
-	register signed64_t	result;
+	register time_t		result;
 	register int		i;
 
 	result = (codep[0] & 0x80) ? ~0L : 0L;
 	for (i = 0; i < 8; ++i)
-		result = (result << 8) | (codep[i] & 0xff);
-	return (time_t) result;
+		result = 256 * result + (codep[i] & 0xff);
+	return result;
 }
 
 static void
@@ -288,6 +289,17 @@ settzname P((void))
 }
 
 static int
+differ_by_repeat(t1, t0)
+time_t	t1;
+time_t	t0;
+{
+	return
+	  ! (TYPE_INTEGRAL(time_t) &&
+	     TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) && 
+	  t1 - t0 == SECSPERREPEAT;
+}
+
+static int
 tzload(name, sp)
 register const char *		name;
 register struct state * const	sp;
@@ -468,7 +480,7 @@ register struct state * const	sp;
 		/*
 		** If this is a narrow integer time_t system, we're done.
 		*/
-		if (stored >= sizeof(time_t) && TYPE_INTEGRAL(time_t))
+		if (stored >= (int)sizeof(time_t) && TYPE_INTEGRAL(time_t))
 			break;
 		nread -= p - u.buf;
 		for (i = 0; i < nread; ++i)
@@ -477,12 +489,12 @@ register struct state * const	sp;
 	i = 2 * YEARSPERREPEAT;
 	sp->goback = sp->goahead = sp->timecnt > i;
 	sp->goback &= sp->types[i] == sp->types[0] &&
-		sp->ats[i] - sp->ats[0] == SECSPERREPEAT;
+		differ_by_repeat(sp->ats[i], sp->ats[0]);
 	sp->goahead &=
 		sp->types[sp->timecnt - 1] ==
 		sp->types[sp->timecnt - 1 - i] &&
-		sp->ats[sp->timecnt - 1] - sp->ats[sp->timecnt - 1 - i] ==
-		SECSPERREPEAT;
+		differ_by_repeat(sp->ats[sp->timecnt - 1],
+				 sp->ats[sp->timecnt - 1 - i]);
 	return 0;
 }
 
@@ -1095,19 +1107,20 @@ struct tm * const	tmp;
 			time_t			newt = t;
 			register time_t		seconds;
 			register time_t		tcycles;
-			register signed64_t	icycles;
+			register int_fast64_t	icycles;
 
 			if (t < sp->ats[0])
 				seconds = sp->ats[0] - t;
 			else	seconds = t - sp->ats[sp->timecnt - 1];
 			--seconds;
-			tcycles = seconds / SECSPERREPEAT;
+			tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
 			++tcycles;
 			icycles = tcycles;
 			if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
 				return NULL;
 			seconds = icycles;
-			seconds *= SECSPERREPEAT;
+			seconds *= YEARSPERREPEAT;
+			seconds *= AVGSECSPERYEAR;
 			if (t < sp->ats[0])
 				newt += seconds;
 			else	newt -= seconds;
===================================================================
RCS file: RCS/zic.c,v
retrieving revision 2005.5.1.1
diff -pu -r2005.5.1.1 zic.c
--- zic.c	2005/04/21 19:24:10	2005.5.1.1
+++ zic.c	2005/04/22 20:58:23
@@ -6,7 +6,7 @@ static char	elsieid[] = "@(#)zic.c	7.127
 
 #define	ZIC_VERSION	'2'
 
-typedef signed64_t	zic_t;
+typedef int_fast64_t	zic_t;
 
 #if HAVE_SYS_STAT_H
 #include "sys/stat.h"
@@ -485,9 +485,9 @@ char *	argv[];
 	(void) textdomain(TZ_DOMAIN);
 #endif /* HAVE_GETTEXT */
 	progname = argv[0];
-	if (8 > sizeof(signed64_t)) {
+	if (TYPE_BIT(zic_t) < 64) {
 		(void) fprintf(stderr,
-"%s: wild compilation-time specification of SIGNED64_T\n", progname);
+"%s: wild compilation-time specification of zic_t\n", progname);
 		exit(EXIT_FAILURE);
 	}
 	for (i = 1; i < argc; ++i)
@@ -1432,22 +1432,20 @@ FILE * const	fp;
 
 static int
 atcomp(avp, bvp)
-void *	avp;
-void *	bvp;
+const void *	avp;
+const void *	bvp;
 {
-	if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
-		return -1;
-	else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
-		return 1;
-	else	return 0;
+	zic_t a = ((const struct attype *) avp)->at;
+	zic_t b = ((const struct attype *) bvp)->at;
+
+	return a < b ? -1 : a > b;
 }
 
 static int
 is32(x)
 zic_t	x;
 {
-	x >>= 31;
-	return x == 0 || x == ~0;
+	return INT32_MIN <= x && x <= INT32_MAX;
 }
 
 static void
@@ -1638,7 +1636,7 @@ const char * const	name;
 				todo = tadd(trans[i], -gmtoffs[j]);
 			} else	todo = trans[i];
 			if (pass == 1)
-				puttzcode(todo, fp);
+				puttzcode((long) todo, fp);
 			else	puttzcode64(todo, fp);
 			puttzcode(corr[i], fp);
 		}



More information about the tz mailing list