--- /dev/null
+#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
*/
RCSID("$Id$")
-#include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/math.h>
#include <freeradius-devel/util/sbuff.h>
#include "size.h"
*/
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;
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);
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) {
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.
*/
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);
}
--- /dev/null
+/*
+ * 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 }
+};