From: Arran Cudbard-Bell Date: Wed, 15 Jun 2022 16:14:38 +0000 (-0500) Subject: Add separate size printing/parsing functions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cb56cf76ee94ea6933ab35d515ce0920a849b169;p=thirdparty%2Ffreeradius-server.git Add separate size printing/parsing functions --- diff --git a/src/lib/util/all.mk b/src/lib/util/all.mk index 75f25718769..dcda8e040a8 100644 --- a/src/lib/util/all.mk +++ b/src/lib/util/all.mk @@ -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 index 00000000000..ddf80a670a6 --- /dev/null +++ b/src/lib/util/math.h @@ -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 diff --git a/src/lib/util/misc.h b/src/lib/util/misc.h index 0ccb4299e4b..fabf69ac8bf 100644 --- a/src/lib/util/misc.h +++ b/src/lib/util/misc.h @@ -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 #include #include @@ -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); diff --git a/src/lib/util/size.c b/src/lib/util/size.c index 2e599b77311..e071d243098 100644 --- a/src/lib/util/size.c +++ b/src/lib/util/size.c @@ -22,7 +22,7 @@ */ RCSID("$Id$") -#include +#include #include #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); } diff --git a/src/lib/util/size.h b/src/lib/util/size.h index a337215ea47..a2893957a0e 100644 --- a/src/lib/util/size.h +++ b/src/lib/util/size.h @@ -23,7 +23,17 @@ */ RCSIDH(size_h, "$Id$") +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include 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 index 00000000000..5f64da4ff2f --- /dev/null +++ b/src/lib/util/size_tests.c @@ -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 +#include +#include + +#ifdef HAVE_GPERFTOOLS_PROFILER_H +# include +#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 index 00000000000..2f5aa5502f8 --- /dev/null +++ b/src/lib/util/size_tests.mk @@ -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) diff --git a/src/lib/util/time.c b/src/lib/util/time.c index 95af7d1ab46..ead6f9c3f9d 100644 --- a/src/lib/util/time.c +++ b/src/lib/util/time.c @@ -27,6 +27,7 @@ RCSID("$Id$") #include #include +#include /* * Avoid too many ifdef's later in the code. diff --git a/src/lib/util/time.h b/src/lib/util/time.h index 8ca4e269665..73ebd2c4025 100644 --- a/src/lib/util/time.h +++ b/src/lib/util/time.h @@ -96,7 +96,7 @@ typedef struct fr_unix_time_s { #include #include #include -#include +#include #ifdef __cplusplus extern "C" {