diff options
Diffstat (limited to 'nihil.core/parse_size.ccm')
| -rw-r--r-- | nihil.core/parse_size.ccm | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/nihil.core/parse_size.ccm b/nihil.core/parse_size.ccm new file mode 100644 index 0000000..5f80755 --- /dev/null +++ b/nihil.core/parse_size.ccm @@ -0,0 +1,90 @@ +// This source code is released into the public domain. +export module nihil.core:parse_size; + +import nihil.std; + +import :ctype; +import :errc; +import :error; +import :monad; + +namespace nihil { + +export template <typename Char> +auto get_multiplier(Char c) -> std::expected<std::uint64_t, error> +{ + auto ret = std::uint64_t{1}; + + // clang-format off + switch (c) { + case 'p': case 'P': ret *= 1024; // NOLINT + case 't': case 'T': ret *= 1024; // NOLINT + case 'g': case 'G': ret *= 1024; // NOLINT + case 'm': case 'M': ret *= 1024; // NOLINT + case 'k': case 'K': ret *= 1024; // NOLINT + return ret; + + default: + return error(errc::invalid_unit); + } + // clang-format on +} + +// Parse a string containing a human-formatted size, such as "1024" +// or "4g". Parsing is always done in the "C" locale and does not +// recognise thousands separators or negative numbers. +export template <typename T, typename Char> +[[nodiscard]] +auto parse_size(std::basic_string_view<Char> str) -> std::expected<T, error> +{ + // Extract the numeric part of the string. + auto it = std::ranges::find_if_not(str, is_c_digit); + auto num_str = std::basic_string_view<Char>(std::ranges::begin(str), it); + + if (num_str.empty()) + co_return error(errc::empty_string); + + auto ret = T{0}; + + for (auto c : num_str) { + if (ret > (std::numeric_limits<T>::max() / 10)) + co_return error(std::errc::result_out_of_range); + ret *= 10; + + auto digit = static_cast<T>(c - '0'); + if ((std::numeric_limits<T>::max() - digit) < ret) + co_return error(std::errc::result_out_of_range); + ret += digit; + } + + if (it == str.end()) + // No multiplier. + co_return ret; + + auto mchar = *it++; + + if (it != str.end()) + // Multiplier is more than one character. + co_return error(errc::invalid_unit); + + auto mult = co_await get_multiplier(mchar); + + if (std::cmp_greater(ret, std::numeric_limits<T>::max() / mult)) + co_return error(std::errc::result_out_of_range); + + co_return ret *mult; +} + +export template <typename T> +[[nodiscard]] auto parse_size(char const *s) +{ + return parse_size<T>(std::string_view(s)); +} + +export template <typename T> +[[nodiscard]] auto parse_size(wchar_t const *s) +{ + return parse_size<T>(std::wstring_view(s)); +} + +} // namespace nihil |
