]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
macro: handle DECIMAL_STR_MAX() special cases more accurately
authorLennart Poettering <lennart@poettering.net>
Mon, 14 Mar 2022 13:44:54 +0000 (14:44 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 14 Mar 2022 17:18:24 +0000 (18:18 +0100)
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
src/test/test-format-util.c

index 214654386c9ae2f04e7769ebed24511210f13278..fb1ae65d282dce8537586b1734f409377b5eee52 100644 (file)
@@ -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
index 3e640a84b32512bfc46b08d7696bb26ab6bf0692..ee74d1c8981e04b0fb054906614cb4bfcd956a47 100644 (file)
@@ -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,