diff options
author | Douglas Chimento <dchimento@gmail.com> | 2021-11-17 12:47:30 +0200 |
---|---|---|
committer | Douglas Chimento <dchimento@gmail.com> | 2021-11-17 12:47:30 +0200 |
commit | 21b58f430fa05fdb7c5db79b545302417a5dbceb (patch) | |
tree | 7998abbdb8b958e8b3c35812a12e1333a98c8270 /src/util | |
parent | 94db963de501e4aba6e5d8150a01ceb85753dee1 (diff) |
util: ParseByteUnits - Parse a string with suffix unit [k|K|m|M|g|G|t|T]
A convenience utility for human readable arguments/config e.g. -maxuploadtarget=500g
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/strencodings.cpp | 46 | ||||
-rw-r--r-- | src/util/strencodings.h | 30 |
2 files changed, 76 insertions, 0 deletions
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 15bd07b374..5e7c99f140 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -11,6 +11,7 @@ #include <algorithm> #include <cstdlib> #include <cstring> +#include <limits> #include <optional> static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -526,3 +527,48 @@ std::string HexStr(const Span<const uint8_t> s) assert(it == rv.end()); return rv; } + +std::optional<uint64_t> ParseByteUnits(const std::string& str, ByteUnit default_multiplier) +{ + if (str.empty()) { + return std::nullopt; + } + auto multiplier = default_multiplier; + char unit = str.back(); + switch (unit) { + case 'k': + multiplier = ByteUnit::k; + break; + case 'K': + multiplier = ByteUnit::K; + break; + case 'm': + multiplier = ByteUnit::m; + break; + case 'M': + multiplier = ByteUnit::M; + break; + case 'g': + multiplier = ByteUnit::g; + break; + case 'G': + multiplier = ByteUnit::G; + break; + case 't': + multiplier = ByteUnit::t; + break; + case 'T': + multiplier = ByteUnit::T; + break; + default: + unit = 0; + break; + } + + uint64_t unit_amount = static_cast<uint64_t>(multiplier); + auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str); + if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow + return std::nullopt; + } + return *parsed_num * unit_amount; +} diff --git a/src/util/strencodings.h b/src/util/strencodings.h index eedb5ec2f8..bc0a6233ab 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -30,6 +30,23 @@ enum SafeChars }; /** + * Used by ParseByteUnits() + * Lowercase base 1000 + * Uppercase base 1024 +*/ +enum class ByteUnit : uint64_t { + NOOP = 1ULL, + k = 1000ULL, + K = 1024ULL, + m = 1'000'000ULL, + M = 1ULL << 20, + g = 1'000'000'000ULL, + G = 1ULL << 30, + t = 1'000'000'000'000ULL, + T = 1ULL << 40, +}; + +/** * Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email * addresses, but avoid anything even possibly remotely dangerous like & or > * @param[in] str The string to sanitize @@ -305,4 +322,17 @@ std::string ToUpper(const std::string& str); */ std::string Capitalize(std::string str); +/** + * Parse a string with suffix unit [k|K|m|M|g|G|t|T]. + * Must be a whole integer, fractions not allowed (0.5t), no whitespace or +- + * Lowercase units are 1000 base. Uppercase units are 1024 base. + * Examples: 2m,27M,19g,41T + * + * @param[in] str the string to convert into bytes + * @param[in] default_multiplier if no unit is found in str use this unit + * @returns optional uint64_t bytes from str or nullopt + * if ToIntegral is false, str is empty, trailing whitespace or overflow + */ +std::optional<uint64_t> ParseByteUnits(const std::string& str, ByteUnit default_multiplier); + #endif // BITCOIN_UTIL_STRENCODINGS_H |