]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add separate size printing/parsing functions
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 15 Jun 2022 16:14:38 +0000 (11:14 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 15 Jun 2022 16:15:48 +0000 (11:15 -0500)
src/lib/util/all.mk
src/lib/util/math.h [new file with mode: 0644]
src/lib/util/misc.h
src/lib/util/size.c
src/lib/util/size.h
src/lib/util/size_tests.c [new file with mode: 0644]
src/lib/util/size_tests.mk [new file with mode: 0644]
src/lib/util/time.c
src/lib/util/time.h

index 75f25718769cfb5d70a2e8d11ce38b53d0b725db..dcda8e040a8beb93b443bf9ed70e2f03c56aff77 100644 (file)
@@ -14,5 +14,6 @@ SUBMAKEFILES := \
        pair_tests.mk \
        rb_tests.mk \
        sbuff_tests.mk \
+       size_tests.mk \
        strerror_tests.mk
 
diff --git a/src/lib/util/math.h b/src/lib/util/math.h
new file mode 100644 (file)
index 0000000..ddf80a6
--- /dev/null
@@ -0,0 +1,157 @@
+#pragma once
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Various miscellaneous utility functions
+ *
+ * @file src/lib/util/misc.h
+ *
+ * @copyright 2000,2006 The FreeRADIUS server project
+ */
+RCSIDH(math_h, "$Id$")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Find the highest order high bit in an unsigned 64 bit integer
+ *
+ * @return 0-64 indicating the position of the highest bit,
+ *     with 0 indicating no high bits, 1 indicating the 1st
+ *     bit and 64 indicating the last bit.
+ */
+static inline uint8_t fr_high_bit_pos(uint64_t num)
+{
+       if (num == 0) return 0; /* num being zero is undefined behaviour for __builtin_clzll */
+
+#ifdef HAVE_BUILTIN_CLZLL
+       return (64 - __builtin_clzll(num));
+#else
+       uint8_t ret = 1;
+       while (num >>= 1) ret++;
+       return ret;
+#endif
+}
+
+/** Find the lowest order high bit in an unsigned 64 bit integer
+ *
+ * @return 0-64 indicating the position of the lowest bit,
+ *     with 0 indicating no high bits, 1 indicating the 1st
+ *     bit and 64 indicating the last bit.
+ */
+static inline uint8_t fr_low_bit_pos(uint64_t num)
+{
+       if (num == 0) return 0;
+
+#ifdef HAVE_BUILTIN_CLZLL
+       return __builtin_ctzll(num) + 1;
+#else
+       uint8_t ret = 1;
+
+       do {
+               if (num & 0x01) break;
+               ret++;
+       } while (num >>= 1);
+
+       return ret;
+#endif
+}
+
+/** Efficient calculation of log10 of a unsigned 64bit integer
+ *
+ * @param[in] num      to calculate log10 of.
+ * @return log10 of the integer
+ */
+static inline uint8_t fr_log10(uint64_t num)
+{
+       static uint64_t const pow_of_10[] =
+       {
+               1ULL,
+               10ULL,
+               100ULL,
+               1000ULL,
+               10000ULL,
+               100000ULL,
+               1000000ULL,
+               10000000ULL,
+               100000000ULL,
+               1000000000ULL,
+               10000000000ULL,
+               100000000000ULL,
+               1000000000000ULL,
+               10000000000000ULL,
+               100000000000000ULL,
+               1000000000000000ULL,
+               10000000000000000ULL,
+               100000000000000000ULL,
+               1000000000000000000ULL,
+               10000000000000000000ULL
+       };
+       uint64_t tmp;
+
+       tmp = (fr_high_bit_pos(num) * 1233) >> 12;
+       return tmp - (num < pow_of_10[tmp]);
+}
+
+/** Multiplies two integers together
+ *
+ * @param[in] _out     Where to store the result.
+ * @param[in] _a       first argument to multiply.
+ * @param[in] _b       second argument to multiply.
+ * @return
+ *      - false on overflow.
+ *      - true if there was no overflow.
+ */
+#define fr_multiply(_out, _a, _b) !__builtin_mul_overflow(_a, _b, _out)
+
+/** Adds two integers
+ *
+ * @param[in] _out     Where to store the result.
+ * @param[in] _a       first argument to add.
+ * @param[in] _b       second argument to add.
+ * @return
+ *      - false on overflow.
+ *      - true if there was no overflow.
+ */
+#define fr_add(_out, _a, _b) !__builtin_add_overflow(_a, _b, _out)
+
+/** Subtracts two integers
+ *
+ * @param[in] _out     Where to store the result.
+ * @param[in] _a       first argument to subtract.
+ * @param[in] _b       second argument to subtract.
+ * @return
+ *      - false on overflow.
+ *      - true if there was no overflow.
+ */
+#define fr_sub(_out, _a, _b) !__builtin_sub_overflow(_a, _b, _out)
+
+/** Round up - Only works if _mul is a power of 2 but avoids division
+ */
+#define ROUND_UP_POW2(_num, _mul)      (((_num) + ((_mul) - 1)) & ~((_mul) - 1))
+
+/** Round up - Works in all cases, but is slower
+ */
+#define ROUND_UP(_num, _mul)           (((((_num) + ((_mul) - 1))) / (_mul)) * (_mul))
+
+/** Get the ceiling value of integer division
+ *
+ */
+#define ROUND_UP_DIV(_x, _y)           (1 + (((_x) - 1) / (_y)))
+
+#ifdef __cplusplus
+}
+#endif
index 0ccb4299e4b2329d146a2f1b9912b089d4204543..fabf69ac8bf11360707425731d0f5a51c89b1845 100644 (file)
@@ -27,39 +27,6 @@ RCSIDH(misc_h, "$Id$")
 extern "C" {
 #endif
 
-/** Multiplies two integers together
- *
- * @param[in] _out     Where to store the result.
- * @param[in] _a       first argument to multiply.
- * @param[in] _b       second argument to multiply.
- * @return
- *      - false on overflow.
- *      - true if there was no overflow.
- */
-#define fr_multiply(_out, _a, _b) !__builtin_mul_overflow(_a, _b, _out)
-
-/** Adds two integers
- *
- * @param[in] _out     Where to store the result.
- * @param[in] _a       first argument to add.
- * @param[in] _b       second argument to add.
- * @return
- *      - false on overflow.
- *      - true if there was no overflow.
- */
-#define fr_add(_out, _a, _b) !__builtin_add_overflow(_a, _b, _out)
-
-/** Subtraces two integers
- *
- * @param[in] _out     Where to store the result.
- * @param[in] _a       first argument to subtract.
- * @param[in] _b       second argument to subtract.
- * @return
- *      - false on overflow.
- *      - true if there was no overflow.
- */
-#define fr_sub(_out, _a, _b) !__builtin_sub_overflow(_a, _b, _out)
-
 #include <freeradius-devel/build.h>
 #include <freeradius-devel/missing.h>
 #include <freeradius-devel/util/print.h>
@@ -85,19 +52,6 @@ void         fr_talloc_verify_cb(const void *ptr, int depth,
 #  define VERIFY_ALL_TALLOC
 #endif
 
-/** Round up - Only works if _mul is a power of 2 but avoids division
- */
-#define ROUND_UP_POW2(_num, _mul)      (((_num) + ((_mul) - 1)) & ~((_mul) - 1))
-
-/** Round up - Works in all cases, but is slower
- */
-#define ROUND_UP(_num, _mul)           (((((_num) + ((_mul) - 1))) / (_mul)) * (_mul))
-
-/** Get the ceiling value of integer division
- *
- */
-#define ROUND_UP_DIV(_x, _y)   (1 + (((_x) - 1) / (_y)))
-
 /** Skip whitespace ('\\t', '\\n', '\\v', '\\f', '\\r', ' ')
  *
  * @param[in,out] _p   string to skip over.
@@ -198,49 +152,6 @@ static inline bool is_zero(char const *value)
        return true;
 }
 
-/** Find the highest order high bit in an unsigned 64 bit integer
- *
- * @return 0-64 indicating the position of the highest bit,
- *     with 0 indicating no high bits, 1 indicating the 1st
- *     bit and 64 indicating the last bit.
- */
-static inline uint8_t fr_high_bit_pos(uint64_t num)
-{
-       if (num == 0) return 0; /* num being zero is undefined behaviour for __builtin_clzll */
-
-#ifdef HAVE_BUILTIN_CLZLL
-       return (64 - __builtin_clzll(num));
-#else
-       uint8_t ret = 1;
-       while (num >>= 1) ret++;
-       return ret;
-#endif
-}
-
-/** Find the lowest order high bit in an unsigned 64 bit integer
- *
- * @return 0-64 indicating the position of the lowest bit,
- *     with 0 indicating no high bits, 1 indicating the 1st
- *     bit and 64 indicating the last bit.
- */
-static inline uint8_t fr_low_bit_pos(uint64_t num)
-{
-       if (num == 0) return 0;
-
-#ifdef HAVE_BUILTIN_CLZLL
-       return __builtin_ctzll(num) + 1;
-#else
-       uint8_t ret = 1;
-
-       do {
-               if (num & 0x01) break;
-               ret++;
-       } while (num >>= 1);
-
-       return ret;
-#endif
-}
-
 int            fr_set_signal(int sig, sig_t func);
 int            fr_unset_signal(int sig);
 int            rad_lockfd(int fd, int lock_len);
index 2e599b77311e5a451ea5dbb4faf034834421b3f0..e071d243098e3bb098421ffadebd29f9b0ff777f 100644 (file)
@@ -22,7 +22,7 @@
  */
 RCSID("$Id$")
 
-#include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/math.h>
 #include <freeradius-devel/util/sbuff.h>
 
 #include "size.h"
@@ -39,6 +39,26 @@ RCSID("$Id$")
  */
 fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
 {
+       static uint64_t base2_units[]= {
+               ['k'] = (uint64_t)1024,
+               ['m'] = (uint64_t)1024 * 1024,
+               ['g'] = (uint64_t)1024 * 1024 * 1024,
+               ['t'] = (uint64_t)1024 * 1024 * 1024 * 1024,
+               ['p'] = (uint64_t)1024 * 1024 * 1024 * 1024 * 1024,
+               ['e'] = (uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024,
+       };
+       static size_t base2_units_len = NUM_ELEMENTS(base2_units);
+
+       static uint64_t base10_units[] = {
+               ['k'] = (uint64_t)1000,
+               ['m'] = (uint64_t)1000 * 1000,
+               ['g'] = (uint64_t)1000 * 1000 * 1000,
+               ['t'] = (uint64_t)1000 * 1000 * 1000 * 1000,
+               ['p'] = (uint64_t)1000 * 1000 * 1000 * 1000 * 1000,
+               ['e'] = (uint64_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000,
+       };
+       static size_t base10_units_len = NUM_ELEMENTS(base10_units);
+
        fr_sbuff_t      our_in = FR_SBUFF(in);
        char            c = '\0';
        uint64_t        size;
@@ -47,8 +67,13 @@ fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
 
        if (fr_sbuff_out(NULL, &size, &our_in) < 0) return fr_sbuff_error(&our_in);
        c = tolower(*fr_sbuff_current(&our_in));
+
+       /*
+        *      Special cases first...
+        */
        switch (c) {
        case 'n':               /* nibble */
+               fr_sbuff_next(&our_in);
                if (size & 0x01) {
                        fr_strerror_const("Sizes specified in nibbles must be an even number");
                        fr_sbuff_set_to_start(&our_in);
@@ -58,47 +83,42 @@ fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
                break;
 
        case '\0':
+               break;
+
        case 'b':               /* byte */
+               fr_sbuff_next(&our_in);
                break;
 
-       case 'k':               /* kibibyte */
-               if (!fr_multiply(&size, size, 1024)) {
+       default:
+       {
+               uint64_t        *units;
+               size_t          units_len;
+               bool            is_base2;
+
+               fr_sbuff_next(&our_in);
+               is_base2 = fr_sbuff_next_if_char(&our_in, 'i') || fr_sbuff_next_if_char(&our_in, 'I');
+               fr_sbuff_next_if_char(&our_in, 'b') || fr_sbuff_next_if_char(&our_in, 'B');     /* Optional */
+
+               if (is_base2) {
+                       units = base2_units;
+                       units_len = base2_units_len;
+               } else {
+                       units = base10_units;
+                       units_len = base10_units_len;
+               }
+
+               if (((size_t)c >= units_len) || units[(uint8_t)c] == 0) {
+                       fr_strerror_printf("Unknown unit '%c'", c);
+                       return fr_sbuff_error(&our_in);
+               }
+
+               if (!fr_multiply(&size, size, units[(uint8_t)c])) {
                overflow:
                        fr_strerror_printf("Value must be less than %zu", (size_t)SIZE_MAX);
                        fr_sbuff_set_to_start(&our_in);
                        return fr_sbuff_error(&our_in);
                }
-               (void)fr_sbuff_next_if_char(&our_in, 'b');
-               break;
-
-       case 'm':               /* mebibyte */
-               if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024))) goto overflow;
-               (void)fr_sbuff_next_if_char(&our_in, 'b');
-               break;
-
-       case 'g':               /* gibibyte */
-               if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024))) goto overflow;
-               (void)fr_sbuff_next_if_char(&our_in, 'b');
-               break;
-
-       case 't':               /* tebibyte */
-               if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024))) goto overflow;
-               (void)fr_sbuff_next_if_char(&our_in, 'b');
-               break;
-
-       case 'p':               /* pebibyte */
-               if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024))) goto overflow;
-               (void)fr_sbuff_next_if_char(&our_in, 'b');
-               break;
-
-       case 'e':               /* ebibyte */
-               if (!fr_multiply(&size, size, ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024))) goto overflow;
-               (void)fr_sbuff_next_if_char(&our_in, 'b');
-               break;
-
-       default:
-               fr_strerror_printf("Unknown unit '%c'", c);
-               return fr_sbuff_error(&our_in);
+       }
        }
 
        if (size > SIZE_MAX) {
@@ -113,6 +133,11 @@ fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
        return fr_sbuff_set(in, &our_in);
 }
 
+typedef struct {
+       char const *suffix;
+       uint64_t mul;
+} fr_size_unit_t;
+
 /** Print a size string with unit
  *
  * Suffix is the largest unit possible without losing precision.
@@ -125,46 +150,51 @@ fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
  */
 fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in)
 {
-       fr_sbuff_t      our_out = FR_SBUFF(out);
-       uint8_t         pos = fr_low_bit_pos((uint64_t)in);
+       fr_sbuff_t              our_out = FR_SBUFF(out);
+
+       static fr_size_unit_t const     base2_units[] = {
+                                       { "B",          (uint64_t)1 },
+                                       { "KiB",        (uint64_t)1024 },
+                                       { "MiB",        (uint64_t)1024 * 1024 },
+                                       { "GiB",        (uint64_t)1024 * 1024 * 1024},
+                                       { "TiB",        (uint64_t)1024 * 1024 * 1024 * 1024},
+                                       { "PiB",        (uint64_t)1024 * 1024 * 1024 * 1024 * 1024},
+                                       { "EiB",        (uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024},
+                               };
+       static fr_size_unit_t const     base10_units[] = {
+                                       { "B",          (uint64_t)1 },
+                                       { "KB",         (uint64_t)1000 },
+                                       { "MB",         (uint64_t)1000 * 1000 },
+                                       { "GB",         (uint64_t)1000 * 1000 * 1000},
+                                       { "TB",         (uint64_t)1000 * 1000 * 1000 * 1000},
+                                       { "PB",         (uint64_t)1000 * 1000 * 1000 * 1000 * 1000},
+                                       { "EB",         (uint64_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000},
+                               };
+       fr_size_unit_t const *unit = &base10_units[0];
+
+       uint8_t pos = fr_low_bit_pos(in);
 
        /*
-        *      Precision is greater than a kb (byte)
-        */
-       if (pos <= 10) {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zub", in);
-       /*
-        *      Precision is greater than a mb (kibibyte)
-        */
-       } else if (pos <= 20) {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zuk", in / 1024);
-       /*
-        *      Precision is greater than a gb (mebibyte)
-        */
-       } else if (pos <= 30) {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zum", in / ((uint64_t)1024 * 1024));
-       /*
-        *      Precision is greater than a tb (gibibyte)
-        */
-       } else if (pos <= 40) {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zug", in / ((uint64_t)1024 * 1024 * 1024));
-       /*
-        *      Precision is greater than a pb (tebibyte)
-        */
-       } else if (pos <= 50) {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zut", in / ((uint64_t)1024 * 1024 * 1024 * 1024));
-       /*
-        *      Precision is greater than a eb (pebibyte)
+        *      Fast path - Won't be divisible by 1000
         */
-       } else if (pos <= 60) {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zup", in / ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024));
+       if (pos < 3) {
+               goto done;
+       } else if ((in % 1000) == 0) {
+                /*
+                 *     For powers of 10, exp equals the
+                 *     number of contiguous zero low order bits.
+                 */
+               unit = &base10_units[(pos - 1) / 3];
 
        /*
-        *      Precision is greater than a zb (exibyte)
+        *      Fast path - Won't be divisible by 1024
         */
-       } else {
-               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zue", in / ((uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024));
+       } else if (pos < 10) {
+               goto done;
+       } else if ((in % 1024) == 0) {
+               unit = &base2_units[(pos - 1) / 10];
        }
 
-       return fr_sbuff_set(out, &our_out);
+done:
+       return fr_sbuff_in_sprintf(&our_out, "%zu%s", in / unit->mul, unit->suffix);
 }
index a337215ea4705b47d9f3bcd7b7e1df52693a43af..a2893957a0e977deb679a95e2b7fff6260655e8f 100644 (file)
  */
 RCSIDH(size_h, "$Id$")
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <freeradius-devel/util/sbuff.h>
 
 fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in);
 
 fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/lib/util/size_tests.c b/src/lib/util/size_tests.c
new file mode 100644 (file)
index 0000000..5f64da4
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Size printing/parsing
+ *
+ * @file src/lib/util/size_tests.c
+ * @copyright Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+
+#include <freeradius-devel/util/acutest.h>
+#include <freeradius-devel/util/acutest_helpers.h>
+#include <freeradius-devel/util/size.h>
+
+#ifdef HAVE_GPERFTOOLS_PROFILER_H
+#  include <gperftools/profiler.h>
+#endif
+
+/*
+fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in);
+
+fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in);
+*/
+
+
+#define test_str(_str) &FR_SBUFF_IN(_str, strlen(_str))
+#define test_out(_buff)        &FR_SBUFF_OUT(_buff, sizeof(_buff))
+
+static char buff[sizeof("18446744073709551615") + 3];
+
+static void test_size_parse_bytes(void)
+{
+       size_t size;
+
+       TEST_MSG("Parse zero b");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("0")), 1);
+       TEST_CHECK_LEN(size, 0);
+
+       TEST_MSG("Parse one b");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1")), 1);
+       TEST_CHECK_LEN(size, 1);
+
+       TEST_MSG("Parse ten b");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("10")), 2);
+       TEST_CHECK_LEN(size, 10);
+
+       TEST_MSG("Parse max b");
+       snprintf(buff, sizeof(buff), "%zu", SIZE_MAX);
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str(buff)), (ssize_t)strlen(buff));
+       TEST_CHECK_LEN(size, SIZE_MAX);
+
+       TEST_MSG("Allow suffix b");
+       snprintf(buff, sizeof(buff), "%zub", SIZE_MAX);
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str(buff)), (ssize_t)strlen(buff));
+       TEST_CHECK_LEN(size, SIZE_MAX);
+
+       TEST_MSG("Allow trailing none-int");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1b_")), 2);
+       TEST_CHECK_LEN(size, 1);
+
+       TEST_MSG("Fail on negative");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("-10")), -1);
+
+/*
+       TEST_MSG("Fail on trailing");
+       TEST_CHECK_RET(fr_size_from_str(&size, test_str("1a0")), -2);
+*/
+}
+
+static void test_size_parse_suffix_base2(void)
+{
+       size_t size;
+
+       TEST_MSG("Parse zero ki");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("0ki")), 3);
+       TEST_CHECK_LEN(size, 0);
+
+       TEST_MSG("Parse zero kib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("0kib")), 4);
+       TEST_CHECK_LEN(size, 0);
+
+       TEST_MSG("Parse one ki");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1ki")), 3);
+       TEST_CHECK_LEN(size, 1024ULL);
+
+       TEST_MSG("Parse one kib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1kib")), 4);
+       TEST_CHECK_LEN(size, 1024ULL);
+
+       TEST_MSG("Parse one KIB");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1KIB")), 4);
+       TEST_CHECK_LEN(size, 1024ULL);
+
+       TEST_MSG("Parse one mib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1mib")), 4);
+       TEST_CHECK_LEN(size, 1024ULL * 1024);
+
+       TEST_MSG("Parse one gib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1gib")), 4);
+       TEST_CHECK_LEN(size, 1024ULL * 1024 * 1024);
+
+       TEST_MSG("Parse one tib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1tib")), 4);
+       TEST_CHECK_LEN(size, 1024ULL * 1024 * 1024 * 1024);
+
+#if SIZE_MAX > UINT32_MAX
+       TEST_MSG("Parse one pib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1pib")), 4);
+       TEST_CHECK_LEN(size, 1024ULL * 1024 * 1024 * 1024 * 1024);
+
+       TEST_MSG("Parse one eib");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1eib")), 4);
+       TEST_CHECK_LEN(size, 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024);
+#endif
+
+       TEST_MSG("Overflow");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("100000000eib")), -1);
+}
+
+static void test_size_parse_suffix_base10(void)
+{
+       size_t size;
+
+       TEST_MSG("Parse zero k");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("0k")), 2);
+       TEST_CHECK_LEN(size, 0);
+
+       TEST_MSG("Parse zero kb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("0kb")), 3);
+       TEST_CHECK_LEN(size, 0);
+
+       TEST_MSG("Parse one k");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1k")), 2);
+       TEST_CHECK_LEN(size, 1000ULL);
+
+       TEST_MSG("Parse one K");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1K")), 2);
+       TEST_CHECK_LEN(size, 1000ULL);
+
+       TEST_MSG("Parse one KB");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1KB")), 3);
+       TEST_CHECK_LEN(size, 1000ULL);
+
+       TEST_MSG("Parse one kb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1kb")), 3);
+       TEST_CHECK_LEN(size, 1000ULL);
+
+       TEST_MSG("Parse one mb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1mb")), 3);
+       TEST_CHECK_LEN(size, 1000ULL * 1000);
+
+       TEST_MSG("Parse one gb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1gb")), 3);
+       TEST_CHECK_LEN(size, 1000ULL * 1000 * 1000);
+
+       TEST_MSG("Parse one tb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1tb")), 3);
+       TEST_CHECK_LEN(size, 1000ULL * 1000 * 1000 * 1000);
+
+#if SIZE_MAX > UINT32_MAX
+       TEST_MSG("Parse one pb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1pb")), 3);
+       TEST_CHECK_LEN(size, 1000ULL * 1000 * 1000 * 1000 * 1000);
+
+       TEST_MSG("Parse one eb");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("1eb")), 3);
+       TEST_CHECK_LEN(size, 1000ULL * 1000 * 1000 * 1000 * 1000 * 1000);
+#endif
+
+       TEST_MSG("Overflow");
+       TEST_CHECK_SLEN(fr_size_from_str(&size, test_str("100000000eb")), -1);
+}
+
+static void test_size_print_bytes(void)
+{
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1), 2);
+       TEST_CHECK_STRCMP(buff, "1B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)2), 2);
+       TEST_CHECK_STRCMP(buff, "2B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)3), 2);
+       TEST_CHECK_STRCMP(buff, "3B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)4), 2);
+       TEST_CHECK_STRCMP(buff, "4B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)5), 2);
+       TEST_CHECK_STRCMP(buff, "5B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)6), 2);
+       TEST_CHECK_STRCMP(buff, "6B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)7), 2);
+       TEST_CHECK_STRCMP(buff, "7B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)8), 2);
+       TEST_CHECK_STRCMP(buff, "8B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)9), 2);
+       TEST_CHECK_STRCMP(buff, "9B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)10), 3);
+       TEST_CHECK_STRCMP(buff, "10B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)100), 4);
+       TEST_CHECK_STRCMP(buff, "100B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)999), 4);
+       TEST_CHECK_STRCMP(buff, "999B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1001), 5);
+       TEST_CHECK_STRCMP(buff, "1001B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1020), 5);
+       TEST_CHECK_STRCMP(buff, "1020B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1021), 5);
+       TEST_CHECK_STRCMP(buff, "1021B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1022), 5);
+       TEST_CHECK_STRCMP(buff, "1022B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1023), 5);
+       TEST_CHECK_STRCMP(buff, "1023B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1025), 5);
+       TEST_CHECK_STRCMP(buff, "1025B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1026), 5);
+       TEST_CHECK_STRCMP(buff, "1026B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1027), 5);
+       TEST_CHECK_STRCMP(buff, "1027B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1028), 5);
+       TEST_CHECK_STRCMP(buff, "1028B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1029), 5);
+       TEST_CHECK_STRCMP(buff, "1029B");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1030), 5);
+       TEST_CHECK_STRCMP(buff, "1030B");
+}
+
+static void test_size_print_base2(void)
+{
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1024), 4);
+       TEST_CHECK_STRCMP(buff, "1KiB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1024 * 1024), 4);
+       TEST_CHECK_STRCMP(buff, "1MiB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1024 * 1024 * 1024), 4);
+       TEST_CHECK_STRCMP(buff, "1GiB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1024 * 1024 * 1024 * 1024), 4);
+       TEST_CHECK_STRCMP(buff, "1TiB");
+
+#if SIZE_MAX > UINT32_MAX
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1024 * 1024 * 1024 * 1024 * 1024), 4);
+       TEST_CHECK_STRCMP(buff, "1PiB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024), 4);
+       TEST_CHECK_STRCMP(buff, "1EiB");
+#endif
+
+       TEST_MSG("Fall back to KiB");
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), ((size_t)1024 * 1024 * 1024 * 1024) + 1024), 13);
+       TEST_CHECK_STRCMP(buff, "1073741825KiB");
+
+       TEST_MSG("Fall back to B");
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), ((size_t)1024 * 1024 * 1024 * 1024) + 1025), 14);
+       TEST_CHECK_STRCMP(buff, "1099511628801B");
+}
+
+static void test_size_print_base10(void)
+{
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1000), 3);
+       TEST_CHECK_STRCMP(buff, "1KB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1000 * 1000), 3);
+       TEST_CHECK_STRCMP(buff, "1MB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1000 * 1000 * 1000), 3);
+       TEST_CHECK_STRCMP(buff, "1GB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1000 * 1000 * 1000 * 1000), 3);
+       TEST_CHECK_STRCMP(buff, "1TB");
+
+#if SIZE_MAX > UINT32_MAX
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1000 * 1000 * 1000 * 1000 * 1000), 3);
+       TEST_CHECK_STRCMP(buff, "1PB");
+
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), (size_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000), 3);
+       TEST_CHECK_STRCMP(buff, "1EB");
+#endif
+
+       TEST_MSG("Fall back to KB");
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), ((size_t)1000 * 1000 * 1000 * 1000) + 1000), 12);
+       TEST_CHECK_STRCMP(buff, "1000000001KB");
+
+       TEST_MSG("Fall back to B");
+       TEST_CHECK_SLEN(fr_size_to_str(test_out(buff), ((size_t)1000 * 1000 * 1000 * 1000) + 1025), 14);
+       TEST_CHECK_STRCMP(buff, "1000000001025B");
+}
+
+TEST_LIST = {
+       /*
+        *      Allocation and management
+        */
+       { "parse_bytes",                        test_size_parse_bytes },
+       { "parse_suffix_base2",                 test_size_parse_suffix_base2 },
+       { "parse_suffix_base10",                test_size_parse_suffix_base10 },
+
+       { "print_bytes",                        test_size_print_bytes },
+       { "print_base2",                        test_size_print_base2 },
+       { "print_base10",                       test_size_print_base10 },
+
+       { NULL }
+};
diff --git a/src/lib/util/size_tests.mk b/src/lib/util/size_tests.mk
new file mode 100644 (file)
index 0000000..2f5aa55
--- /dev/null
@@ -0,0 +1,6 @@
+TARGET         := size_tests$(E)
+SOURCES                := size_tests.c
+
+TGT_LDLIBS     := $(LIBS) $(GPERFTOOLS_LIBS)
+TGT_LDFLAGS    := $(LDFLAGS) $(GPERFTOOLS_LDFLAGS)
+TGT_PREREQS    := libfreeradius-util$(L)
index 95af7d1ab46e45d768df39f003f8a754b9052302..ead6f9c3f9d947cfb9c58d82449d25d7ffe95f2d 100644 (file)
@@ -27,6 +27,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/autoconf.h>
 #include <freeradius-devel/util/time.h>
+#include <freeradius-devel/util/misc.h>
 
 /*
  *     Avoid too many ifdef's later in the code.
index 8ca4e26966555e5ce6b8380209e0d12dd34b1be3..73ebd2c402597dfdffe39b3c74afcc91057185fb 100644 (file)
@@ -96,7 +96,7 @@ typedef struct fr_unix_time_s {
 #include <freeradius-devel/missing.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/sbuff.h>
-#include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/math.h>
 
 #ifdef __cplusplus
 extern "C" {