Re: problem with detzcode on machines w/64 bit longs

Chris Demetriou, who maintains the NetBSD DEC Alpha port, reported a problem in localtime.c's detzcode() function that occurs because the value being extracted from the string in that function is not sign extended properly.
He says: The problem is that if you do what the current version does, then you can end up with numbers like 0xffffyyyy being returned from detzcode(), i.e. no sign extension into the upper 32 bits of the long. Because of this, among other things, 'date' will say that it's some time in the year 2025...
Using the following detzcode() implementation (which unrolls the loop and doesn't mask the first character) works, but it suffers from a small defect in that it assumes signed characters.
static long detzcode(codep) const char * const codep; { register long result;
result = (codep[0] << 24) \ | (codep[1] & 0xff) << 16 \ | (codep[2] & 0xff) << 8 | (codep[3] & 0xff); return result; }
casting codep[0] to signed char would fix that, but would require a ANSI compiler.
How about this simple change? *** tz95b/localtime.c Sat Mar 4 11:22:26 1995 --- localtime.c Fri Mar 10 08:51:53 1995 *************** *** 186,200 **** static long detzcode(codep) const char * const codep; { register long result; register int i; ! result = 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } static void settzname P((void)) --- 186,200 ---- static long detzcode(codep) const char * const codep; { register long result; register int i; ! result = ~0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } static void settzname P((void))

[ for once i wish for a unidiff... 8-]
How about this simple change?
[ OLD ]
! result = 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result;
[ NEW ]
! result = ~0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result;
right, but then you _always_ have the top 32 bits set. the point is to get the effect of sign extension on the MSB. You could also do something like: result = (int)result; right before you return the result. however, _that_ depends on the assumption that "int" is 32 bits, which definitely isn't true on all of the machines out there (unless i'm mistaken, it's not true on macintoshes with certain C compilers, and on crays)... cgd

How about this? result = 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return((result >> 31) ? (result | (~0L << 32)) : result); This works on our Alpha's. - Tom

How about this?
result = 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return((result >> 31) ? (result | (~0L << 32)) : result);
yeah, that should work as desired, though i forget if "0L" is an ansi-C-ism, and don't have a book around to check... you definitely _do_ need the cast to long, in some form, otherwise it'll lose. cgd

yeah, that should work as desired, though i forget if "0L" is an ansi-C-ism, and don't have a book around to check...
Yep, it's in there. Section 3.1.3.2 of the spec I have dated 1990. I think this has been around for a while, probably in K&R too. - Tom
participants (3)
-
Bradley White
-
Chris G Demetriou
-
Tom Peterson