diff --git a/src/string-test.cc b/src/string-test.cc index 099421f..40611ca 100644 --- a/src/string-test.cc +++ b/src/string-test.cc @@ -99,6 +99,19 @@ TEST(ToHexTest, Simple) { EXPECT_EQ("12 34 de ad be ef", ToHex("\x12\x34\xde\xad\xbe\xef", true)); } +TEST(HumanizeTest, Simple) { + EXPECT_EQ("1.0 B", HumanizeWithBinaryPrefix(1.f, "B")); + EXPECT_EQ("1.0 KiB", HumanizeWithBinaryPrefix(UINT64_C(1) << 10, "B")); + EXPECT_EQ("1.0 EiB", HumanizeWithBinaryPrefix(UINT64_C(1) << 60, "B")); + EXPECT_EQ("1.5 EiB", HumanizeWithBinaryPrefix( + (UINT64_C(1) << 60) + (UINT64_C(1) << 59), "B")); + EXPECT_EQ("16.0 EiB", HumanizeWithBinaryPrefix( + std::numeric_limits::max(), "B")); + + EXPECT_EQ("1.0 Mbps", HumanizeWithDecimalPrefix(1e6f, "bps")); + EXPECT_EQ("1000.0 Ebps", HumanizeWithDecimalPrefix(1e21, "bps")); +} + } // namespace } // namespace moonfire_nvr diff --git a/src/string.cc b/src/string.cc index dbb7f75..999a0c4 100644 --- a/src/string.cc +++ b/src/string.cc @@ -45,6 +45,15 @@ char HexDigit(unsigned int i) { return (i < 16) ? kHexadigits[i] : 'x'; } +std::string Humanize(std::initializer_list prefixes, + float f, float n, re2::StringPiece suffix) { + size_t i; + for (i = 0; i < prefixes.size() - 1 && n >= f; ++i) n /= f; + char buf[64]; + snprintf(buf, sizeof(buf), "%.1f", n); + return StrCat(buf, *(prefixes.begin() + i), suffix); +} + } // namespace namespace internal { @@ -124,6 +133,18 @@ std::string ToHex(re2::StringPiece in, bool pad) { return out; } +std::string HumanizeWithDecimalPrefix(float n, re2::StringPiece suffix) { + static const std::initializer_list kPrefixes = { + " ", " k", " M", " G", " T", " P", " E"}; + return Humanize(kPrefixes, 1000., n, suffix); +} + +std::string HumanizeWithBinaryPrefix(float n, re2::StringPiece suffix) { + static const std::initializer_list kPrefixes = { + " ", " Ki", " Mi", " Gi", " Ti", " Pi", " Ei"}; + return Humanize(kPrefixes, 1024., n, suffix); +} + bool strto64(const char *str, int base, const char **endptr, int64_t *value) { static_assert(sizeof(int64_t) == sizeof(long long int), "unknown memory model"); diff --git a/src/string.h b/src/string.h index 99dd9f7..fffd0e8 100644 --- a/src/string.h +++ b/src/string.h @@ -122,6 +122,11 @@ std::string EscapeHtml(const std::string &input); // For example, ToHex("\xde\xad\xbe\xef", true) returns "de ad be ef". std::string ToHex(re2::StringPiece in, bool pad = false); +// Return a human-friendly approximation of the given non-negative value, using +// SI (base-10) or IEC (base-2) prefixes. +std::string HumanizeWithDecimalPrefix(float n, re2::StringPiece suffix); +std::string HumanizeWithBinaryPrefix(float n, re2::StringPiece suffix); + // Wrapper around ::strtol that returns true iff valid and corrects // constness. bool strto64(const char *str, int base, const char **endptr, int64_t *value);