From 56da8d5af37bda6950f058066fa05c5fdcc20f6e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 14 Mar 2022 14:44:54 +0100 Subject: macro: handle DECIMAL_STR_MAX() special cases more accurately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far DECIMAL_STR_MAX() overestimated the types in two ways: it would also adds space for a "-" for unsigned types. And it would always return the same size for 64bit values regardless of signedness, even though the longest maximum numbers for signed and unsigned differ in length by one digit. i.e. 2^64-1 (i.e. UINT64_MAX) is one decimal digit longer than -2^63 (INT64_MIN) - for the other integer widths the number of digits in the "longest" decimal value is always the same, regardless of signedness. by example: strlen("65535") == strlen("32768") (i.e. the relevant 16 bit limits) holds — and similar for 8bit and 32bit integer width limits — but strlen("18446744073709551615") > strlen("9223372036854775808") (i.e. the relevant 64 bit limits). Let's fix both misestimations. --- src/basic/macro.h | 11 +++++------ src/test/test-format-util.c | 10 ++++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/basic/macro.h b/src/basic/macro.h index 214654386c..fb1ae65d28 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -309,15 +309,14 @@ static inline int __coverity_check_and_return__(int condition) { #define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ +/* Returns the number of chars needed to format variables of the specified type as a decimal string. Adds in + * extra space for a negative '-' prefix for signed types. Includes space for the trailing NUL. */ #define DECIMAL_STR_MAX(type) \ - (2U+(sizeof(type) <= 1 ? 3U : \ + ((size_t) IS_SIGNED_INTEGER_TYPE(type) + 1U + \ + (sizeof(type) <= 1 ? 3U : \ sizeof(type) <= 2 ? 5U : \ sizeof(type) <= 4 ? 10U : \ - sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)]))) + sizeof(type) <= 8 ? (IS_SIGNED_INTEGER_TYPE(type) ? 19U : 20U) : sizeof(int[-2*(sizeof(type) > 8)]))) /* Returns the number of chars needed to format the specified integer value. It's hence more specific than * DECIMAL_STR_MAX() which answers the same question for all possible values of the specified type. Does diff --git a/src/test/test-format-util.c b/src/test/test-format-util.c index 3e640a84b3..ee74d1c898 100644 --- a/src/test/test-format-util.c +++ b/src/test/test-format-util.c @@ -10,10 +10,12 @@ assert_cc(STRLEN("xxx") == 3); assert_cc(STRLEN("") == 0); assert_cc(STRLEN(L"xxx") == 3 * sizeof(wchar_t)); assert_cc(STRLEN(L"") == 0); -assert_cc(DECIMAL_STR_MAX(uint8_t) == 5); -assert_cc(DECIMAL_STR_MAX(int8_t) == 5); -assert_cc(DECIMAL_STR_MAX(uint64_t) == 22); -assert_cc(DECIMAL_STR_MAX(char) == 5); +assert_cc(DECIMAL_STR_MAX(uint8_t) == STRLEN("255")+1); +assert_cc(DECIMAL_STR_MAX(int8_t) == STRLEN("-127")+1); +assert_cc(DECIMAL_STR_MAX(uint64_t) == STRLEN("18446744073709551615")+1); +assert_cc(DECIMAL_STR_MAX(int64_t) == CONST_MAX(STRLEN("-9223372036854775808"), STRLEN("9223372036854775807"))+1); +assert_cc(DECIMAL_STR_MAX(signed char) == STRLEN("-127")+1); +assert_cc(DECIMAL_STR_MAX(unsigned char) == STRLEN("255")+1); assert_cc(CONST_MAX(DECIMAL_STR_MAX(int8_t), STRLEN("xxx")) == 5); static void test_format_bytes_one(uint64_t val, bool trailing_B, const char *iec_with_p, const char *iec_without_p, -- cgit v1.2.1