Skip to content

Commit c1b7a35

Browse files
committed
MB-69894: Add cb::human2size
Convert a textual representation of a size (generated from cb::size2human) back to a numeric value Change-Id: I12a68a27667e25e4cfdfa4f2cfd36e62e01fd1f5 Reviewed-on: https://review.couchbase.org/c/platform/+/237825 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Paolo Cocchi <paolo.cocchi@couchbase.com>
1 parent 8b89210 commit c1b7a35

3 files changed

Lines changed: 82 additions & 2 deletions

File tree

include/platform/string_utilities.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,20 @@ namespace cb {
1919
* "k", "M" etc
2020
*
2121
* @param value the value to convert
22-
* @param an optional "suffix" to add to the string. By default adding 'b' to
23-
* return sizes like: 10Mb etc
22+
* @param suffix n optional "suffix" to add to the string. By default adding
23+
* 'b' to return sizes like: 10Mb etc
2424
* @return the size in a format easier to read for a human
2525
*/
2626
std::string size2human(std::size_t value,
2727
const std::optional<std::string>& suffix = "B");
28+
29+
/**
30+
* Convert a human readable size (with an optional suffix) into a size_t.
31+
* Suffix may be one of: k, M, G, T, P (case insensitive) with an optional
32+
* 'b'/'B'
33+
*
34+
* @param text The textual representation to convert
35+
* @return the value in bytes
36+
*/
37+
std::size_t human2size(std::string_view text);
2838
} // namespace cb

src/string_utilities.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
* the file licenses/APL2.txt.
99
*/
1010
#include <fmt/format.h>
11+
#include <platform/byte_literals.h>
1112
#include <platform/string_utilities.h>
1213
#include <array>
14+
#include <charconv>
1315

1416
std::string cb::size2human(std::size_t value,
1517
const std::optional<std::string>& suffix) {
@@ -24,3 +26,57 @@ std::string cb::size2human(std::size_t value,
2426
size_suffix[index],
2527
suffix.has_value() ? *suffix : "");
2628
}
29+
30+
std::size_t cb::human2size(std::string_view text) {
31+
size_t value = 0;
32+
33+
const auto [ptr, ec]{
34+
std::from_chars(text.data(), text.data() + text.size(), value)};
35+
if (ec != std::errc()) {
36+
if (ec == std::errc::invalid_argument) {
37+
throw std::invalid_argument("human2size: no conversion");
38+
}
39+
if (ec == std::errc::result_out_of_range) {
40+
throw std::out_of_range("human2size: value exceeds std::size_t");
41+
}
42+
throw std::system_error(std::make_error_code(ec));
43+
}
44+
45+
// trim off what we've parsed
46+
text.remove_prefix(ptr - text.data());
47+
if (!text.empty()) {
48+
switch (std::toupper(text.front())) {
49+
case 'P':
50+
value *= 1024_TiB;
51+
break;
52+
case 'T':
53+
value *= 1_TiB;
54+
break;
55+
case 'G':
56+
value *= 1_GiB;
57+
break;
58+
case 'M':
59+
value *= 1_MiB;
60+
break;
61+
case 'K':
62+
value *= 1_KiB;
63+
break;
64+
case 'B':
65+
break;
66+
default:
67+
throw std::invalid_argument("human2size: invalid size specifier");
68+
}
69+
text.remove_prefix(1);
70+
if (!text.empty()) {
71+
if (text.front() == 'b' || text.front() == 'B') {
72+
text.remove_prefix(1);
73+
}
74+
if (!text.empty()) {
75+
throw std::invalid_argument(
76+
"human2size: Additional characters found");
77+
}
78+
}
79+
}
80+
81+
return value;
82+
}

tests/unit_tests/string_utilities_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* the file licenses/APL2.txt.
99
*/
1010
#include <folly/portability/GTest.h>
11+
#include <platform/byte_literals.h>
1112
#include <platform/string_utilities.h>
1213

1314
TEST(Size2Human, NoConversion) {
@@ -18,3 +19,16 @@ TEST(Size2Human, NoConversion) {
1819
TEST(Size2Human, Conversion) {
1920
EXPECT_EQ("20MB", cb::size2human(20 * 1024 * 1024));
2021
}
22+
23+
TEST(Human2Size, NoConversion) {
24+
EXPECT_EQ(2, cb::human2size("2"));
25+
EXPECT_EQ(2, cb::human2size("2B"));
26+
}
27+
28+
TEST(Human2Size, Conversion) {
29+
EXPECT_EQ(32_KiB, cb::human2size("32k"));
30+
EXPECT_EQ(20_MiB, cb::human2size("20Mb"));
31+
EXPECT_EQ(3_GiB, cb::human2size("3G"));
32+
EXPECT_EQ(7_TiB, cb::human2size("7T"));
33+
EXPECT_EQ(8_TiB * 1024, cb::human2size("8PB"));
34+
}

0 commit comments

Comments
 (0)