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
This commit is contained in:
Douglas Chimento
2021-11-17 12:47:30 +02:00
parent 94db963de5
commit 21b58f430f
6 changed files with 138 additions and 4 deletions

View File

@@ -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;
}

View File

@@ -29,6 +29,23 @@ enum SafeChars
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
};
/**
* 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 >
@@ -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