#include #include #include #include #include "toolbox.hpp" namespace asp { /** * @brief Convert a given number to string * * @param num Number to convert * @param offset of the string location to append * @param str String to append the number to * @return number of written bytes */ constexpr size_t ullstr(uint64_t num, const size_t offset, char* const str) noexcept { size_t i = 0; for (; num > 0; num /= 10) str[offset + i++] = char(num % 10) + '0'; str[offset + i] = '\0'; for (size_t j = 0; j < i / 2; ++j) std::swap(str[offset + j], str[offset + i - j - 1]); return i; } /** * @brief Cast a variable to an unsigned 64 bit integer * * @tparam T Type of the variable to cast * @param vae Variable to cast * @return Casted variable */ template static constexpr uint64_t u64(const T var) noexcept { return uint64_t(var); } static constexpr const size_t STR_BUFFER_SIZE = 64; static constexpr const int8_t N_TIMES = 11; static constexpr const std::array time_formats = { "ns", "us", "ms", "s", "m", "h", "j", "w", "M", "y", "c" }; static constexpr const std::array time_numbers = { 1, 1000, 1000000, u64(1e9), u64(6e10), u64(36e11), u64(864e11), u64(6048e11), u64(26298e11), u64(315576e11), u64(315576e13) }; static constexpr size_t N_BYTES = 7; static constexpr const std::array format_prefix = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; /** * @brief Convert a given numbers of bytes to a human readable format * * @param n Number of bytes * @return Human readable format of the numbers of bytes */ std::string format_byte_size(uint64_t n) noexcept { char s[STR_BUFFER_SIZE] = { 0 }; if(n == 0){ sprintf(s, "0%s", format_prefix[0]); return s; } size_t j = 0; for(uint8_t i = uint8_t(format_prefix.size() - 1) * 10; i > 0; i -= 10){ const uint64_t nsi = n >> i; if(nsi > 0){ const uint8_t idx = i / 10; j += ullstr(nsi, j, s); for(int k = 0; format_prefix[idx][k] > 0; ++k) s[j++] = format_prefix[idx][k]; s[j++] = ' '; n &= u64(-1) >> (64 - i); } } if(n > 0){ j += ullstr(n, j, s); for(int k = 0; format_prefix[0][k] > 0; ++k) s[j++] = format_prefix[0][k]; s[j++] = ' '; } /* Remove trailing character */ s[j - 1] = '\0'; return std::string(s); } /** * @brief Convert a given number of nanoseconds to a human readable format * * @param time Number of nanoseconds * @return Human readable formatted string */ std::string format_time_ns(uint64_t time) noexcept { char s[STR_BUFFER_SIZE] = {0}; size_t j = 0; if (time == 0){ sprintf(s, "0%s", time_formats[0]); return s; } uint64_t res; for (uint8_t i = time_numbers.size() - 1; i > 0; --i) { if (time >= time_numbers[i]) { res = time / time_numbers[i]; time %= time_numbers[i]; j += ullstr(res, j, s); for(int k = 0; time_formats[i][k] > 0; ++k) s[j++] = time_formats[i][k]; s[j++] = ' '; } } if (time >= time_numbers[0]) { res = time / time_numbers[0]; time %= time_numbers[0]; j += ullstr(res, j, s); for(int k = 0; time_formats[0][k] > 0; ++k) s[j++] = time_formats[0][k]; s[j++] = ' '; } /* Remove trailing character */ s[j - 1] = '\0'; std::string ss(s); return ss; } struct separate_thousands : std::numpunct { char sep_; separate_thousands(const char sep) noexcept : sep_(sep) {} char_type do_thousands_sep() const override { return sep_; } // separate with commas string_type do_grouping() const override { return "\3"; } // groups of 3 digit }; /** * @brief Format a given integer with a given seperator for each thousands. * * @param k Integer * @param sep Thousand seperator * @return Formatted integer */ std::string thousand_sep(const int64_t k, const char sep) noexcept { std::ostringstream out; out.imbue(std::locale(out.getloc(), new separate_thousands(sep))); out << k; return out.str(); } /** * @brief Format a given integer with ',' for each thousands. * * @param k Integer * @return Formatted integer */ std::string thousand_sep(const int64_t k) noexcept { return thousand_sep(k, ','); } /** * @brief Run some unitary tests on format functions * */ void toolbox_unit_test(void) noexcept { assert(std::string("0B") == format_byte_size(0)); assert(std::string("1B") == format_byte_size(1)); assert(std::string("1KB") == format_byte_size(1 << 10)); assert(std::string("1MB") == format_byte_size(1 << 20)); assert(std::string("1GB") == format_byte_size(1 << 30)); assert(std::string("1TB") == format_byte_size(u64(1) << 40)); assert(std::string("1PB") == format_byte_size(u64(1) << 50)); assert(std::string("1EB") == format_byte_size(u64(1) << 60)); assert(std::string("15EB 1023PB 1023TB 1023GB 1023MB 1023KB 1023B") == format_byte_size(std::numeric_limits::max())); assert(std::string("15EB 1023PB 1023TB 1023GB 1023MB 1023KB 1023B") == format_byte_size(18446744073709551615ull)); assert(std::string("10EB 1000PB 1000TB 1000GB 1000MB 1000KB 1000B") == format_byte_size(12656215539330294760ull)); assert(std::string("0ns") == format_time_ns(0)); assert(std::string("1ns") == format_time_ns(1)); assert(std::string("10ns") == format_time_ns(10)); assert(std::string("1us") == format_time_ns(1e3)); assert(std::string("1ms") == format_time_ns(1e6)); assert(std::string("10ms") == format_time_ns(1e7)); assert(std::string("100ms") == format_time_ns(1e8)); assert(std::string("1s") == format_time_ns(1e9)); assert(std::string("10s") == format_time_ns(1e10)); assert(std::string("1m") == format_time_ns(6e10)); assert(std::string("1m 26s 400ms") == format_time_ns(864e8)); assert(std::string("1m 40s") == format_time_ns(1e11)); assert(std::string("16m 40s") == format_time_ns(1e12)); assert(std::string("1h") == format_time_ns(36e11)); assert(std::string("1j") == format_time_ns(864e11)); assert(std::string("1w") == format_time_ns(6048e11)); assert(std::string("1w 4j 13h 46m 40s") == format_time_ns(1e15)); assert(std::string("2w") == format_time_ns(12096e11)); assert(std::string("3w 6j 5h 5m 35s 800ms") == format_time_ns(23511358e8)); assert(std::string("3w 6j 7h 43m 4s 700ms") == format_time_ns(23605847e8)); assert(std::string("3w 6j 7h 43m 11s 600ms") == format_time_ns(23605916e8)); assert(std::string("3w 6j 13h 18m 33s 200ms") == format_time_ns(23807132e8)); assert(std::string("4w 1j 12h 44m 2s 900ms") == format_time_ns(25514429e8)); assert(std::string("1M") == format_time_ns(26298e11)); assert(std::string("1M 1w 2j 13h 30m") == format_time_ns(3456e12)); assert(std::string("4M 4j 6h") == format_time_ns(108864e11)); assert(std::string("11M 2w 5j 13h 22m 48s") == format_time_ns(30617568e9)); assert(std::string("11M 4w 2j 4h 30m") == format_time_ns(31536e12)); assert(std::string("1y") == format_time_ns(315576e11)); assert(std::string("11M 4w 2j 10h 18m 45s") == format_time_ns(31556925e9)); assert(std::string("11M 4w 2j 10h 19m 12s") == format_time_ns(31556952e9)); assert(std::string("1y 9m 9s") == format_time_ns(31558149e9)); assert(std::string("1y 18h") == format_time_ns(316224e11)); assert(std::string("4y") == format_time_ns(1262304e11)); assert(std::string("5y") == format_time_ns(157788e12)); assert(std::string("10y") == format_time_ns(315576e12)); assert(std::string("15y") == format_time_ns(473364e12)); assert(std::string("20y") == format_time_ns(631152e12)); assert(std::string("31y 8M 1w 19h 46m 40s") == format_time_ns(1e18)); assert(std::string("50y") == format_time_ns(157788e13)); assert(std::string("1c") == format_time_ns(315576e13)); assert(std::string("5c 84y 6M 2w 1j 8h 34m 33s 709ms 551us 615ns") == format_time_ns(std::numeric_limits::max())); assert(std::string("5c 84y 6M 2w 1j 8h 34m 33s 709ms 551us 615ns") == format_time_ns(18446744073709551615ull)); assert(std::string("1c 10y 10M 3w 6j 10h 10m 10s 100ms 100us 100ns") == format_time_ns(3500003410100100100)); assert(std::string("0") == thousand_sep(0)); assert(std::string("1") == thousand_sep(1)); assert(std::string("10") == thousand_sep(10)); assert(std::string("100") == thousand_sep(100)); assert(std::string("1,000") == thousand_sep(1e3)); assert(std::string("10,000") == thousand_sep(1e4)); assert(std::string("100,000") == thousand_sep(1e5)); assert(std::string("1,000,000") == thousand_sep(1e6)); assert(std::string("10,000,000") == thousand_sep(1e7)); assert(std::string("100,000,000") == thousand_sep(1e8)); assert(std::string("1,000,000,000") == thousand_sep(1e9)); assert(std::string("10,000,000,000") == thousand_sep(1e10)); assert(std::string("100,000,000,000") == thousand_sep(1e11)); assert(std::string("1,000,000,000,000") == thousand_sep(1e12)); assert(std::string("10,000,000,000,000") == thousand_sep(1e13)); assert(std::string("100,000,000,000,000") == thousand_sep(1e14)); assert(std::string("1,000,000,000,000,000") == thousand_sep(1e15)); assert(std::string("10,000,000,000,000,000") == thousand_sep(1e16)); assert(std::string("100,000,000,000,000,000") == thousand_sep(1e17)); assert(std::string("1,000,000,000,000,000,000") == thousand_sep(1e18)); assert(std::string("1,234,567,890") == thousand_sep(1234567890)); assert(std::string("9,876,543,210") == thousand_sep(9876543210)); assert(std::string("9,223,372,036,854,775,807") == thousand_sep(std::numeric_limits::max())); assert(std::string("-1") == thousand_sep(-1)); assert(std::string("-10") == thousand_sep(-10)); assert(std::string("-100") == thousand_sep(-100)); assert(std::string("-1,000") == thousand_sep(-1e3)); assert(std::string("-10,000") == thousand_sep(-1e4)); assert(std::string("-100,000") == thousand_sep(-1e5)); assert(std::string("-1,000,000") == thousand_sep(-1e6)); assert(std::string("-10,000,000") == thousand_sep(-1e7)); assert(std::string("-100,000,000") == thousand_sep(-1e8)); assert(std::string("-1,000,000,000") == thousand_sep(-1e9)); assert(std::string("-10,000,000,000") == thousand_sep(-1e10)); assert(std::string("-100,000,000,000") == thousand_sep(-1e11)); assert(std::string("-1,000,000,000,000") == thousand_sep(-1e12)); assert(std::string("-10,000,000,000,000") == thousand_sep(-1e13)); assert(std::string("-100,000,000,000,000") == thousand_sep(-1e14)); assert(std::string("-1,000,000,000,000,000") == thousand_sep(-1e15)); assert(std::string("-10,000,000,000,000,000") == thousand_sep(-1e16)); assert(std::string("-100,000,000,000,000,000") == thousand_sep(-1e17)); assert(std::string("-1,000,000,000,000,000,000") == thousand_sep(-1e18)); assert(std::string("-1,234,567,890") == thousand_sep(-1234567890)); assert(std::string("-9,876,543,210") == thousand_sep(-9876543210)); assert(std::string("-9,223,372,036,854,775,808") == thousand_sep(std::numeric_limits::min())); } }