
* Makefile, NEWS: Mention this. * localtime.c (TZNAME_MAXIMUM): Default to 254, not 255. (desigidx_type): New type. (struct ttinfo, init_ttinfo): Use it. (struct ttinfo): Shrink tt_utoff from int_fast32_t to int_least32_t. On x86-64 with GCC 14 -O2 these changes shrink struct state from 25480 to 21384 bytes, and shrink the size of the localtime.o insns from 33285 to 32485 bytes. They also fix an unlikely bug if the builder compiles with a TZNAME_MAXIMUM value not less than INT_MAX. * private.h (int_least32_t) [!INT_LEAST32_MAX]: Define, for benefit of pre-C99 platforms. --- Makefile | 2 +- NEWS | 10 ++++++++++ localtime.c | 34 +++++++++++++++++++++++++--------- private.h | 4 ++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 01db50d6..e8574551 100644 --- a/Makefile +++ b/Makefile @@ -286,7 +286,7 @@ LDLIBS= # This mishandles some past timestamps, as US DST rules have changed. # It also mishandles settings like TZ='EET-2EEST' for eastern Europe, # as Europe and US DST rules differ. -# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 255) +# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 254) # -DUNINIT_TRAP if reading uninitialized storage can cause problems # other than simply getting garbage data # -DUSE_LTZ=0 to build zdump with the system time zone library diff --git a/NEWS b/NEWS index fb3d5609..a484ded2 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,16 @@ Unreleased, experimental changes struct tm component near INT_MIN or INT_MAX overflows when a lower-order component carries into it. + TZNAME_MAXIMUM, the maximum number of bytes in a proleptic TZ + string's time zone abbreviation, now defaults to 254 not 255. + This helps reduce the size of internal state from 25480 to 21384 + on common platforms. This change should not be a problem, as + nobody uses such long "abbreviations" and the longstanding tzcode + maximum was 16 until release 2023a. For those who prefer no + arbitrary limits, you can now specify TZNAME_MAXIMUM values up to + PTRDIFF_MAX, a limit forced by C anyway; formerly tzcode silently + misbehaved unless TZNAME_MAXIMUM was less than INT_MAX. + Changes to documentation Cite Internet RFC 9636, which obsoletes RFC 8536 for TZif format. diff --git a/localtime.c b/localtime.c index e6d79d05..492965d9 100644 --- a/localtime.c +++ b/localtime.c @@ -145,10 +145,31 @@ static char const *utc = etc_utc + sizeof "Etc/" - 1; # define TZDEFRULESTRING ",M3.2.0,M11.1.0" #endif +/* Limit to time zone abbreviation length in proleptic TZ strings. + This is distinct from TZ_MAX_CHARS, which limits TZif file contents. + It defaults to 254, not 255, so that desigidx_type can be an unsigned char. + unsigned char suffices for TZif files, so the only reason to increase + TZNAME_MAXIMUM is to support TZ strings specifying abbreviations + longer than 254 bytes. There is little reason to do that, though, + as strings that long are hardly "abbreviations". */ +#ifndef TZNAME_MAXIMUM +# define TZNAME_MAXIMUM 254 +#endif + +#if TZNAME_MAXIMUM < UCHAR_MAX +typedef unsigned char desigidx_type; +#elif TZNAME_MAXIMUM < INT_MAX +typedef int desigidx_type; +#elif TZNAME_MAXIMUM < PTRDIFF_MAX +typedef ptrdiff_t desigidx_type; +#else +# error "TZNAME_MAXIMUM too large" +#endif + struct ttinfo { /* time type information */ - int_fast32_t tt_utoff; /* UT offset in seconds */ + int_least32_t tt_utoff; /* UT offset in seconds */ + desigidx_type tt_desigidx; /* abbreviation list index */ bool tt_isdst; /* used to set tm_isdst */ - int tt_desigidx; /* abbreviation list index */ bool tt_ttisstd; /* transition is std time */ bool tt_ttisut; /* transition is UT */ }; @@ -167,12 +188,6 @@ static char const UNSPEC[] = "-00"; for ttunspecified to work without crashing. */ enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 }; -/* Limit to time zone abbreviation length in proleptic TZ strings. - This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */ -#ifndef TZNAME_MAXIMUM -# define TZNAME_MAXIMUM 255 -#endif - /* A representation of the contents of a TZif file. Ideally this would have no size limits; the following sizes should suffice for practical use. This struct should not be too large, as instances @@ -273,7 +288,8 @@ long altzone; /* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */ static void -init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx) +init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, + desigidx_type desigidx) { s->tt_utoff = utoff; s->tt_isdst = isdst; diff --git a/private.h b/private.h index 1e4e69a4..edc188a2 100644 --- a/private.h +++ b/private.h @@ -382,6 +382,10 @@ typedef int int_fast32_t; # endif #endif +#ifndef INT_LEAST32_MAX +typedef int_fast32_t int_least32_t; +#endif + #ifndef INTMAX_MAX # ifdef LLONG_MAX typedef long long intmax_t; -- 2.43.0