// This source code is released into the public domain. export module nihil.util:parse_size; import nihil.std; import nihil.core; import nihil.error; import nihil.monad; import :ctype; namespace nihil { export template auto get_multiplier(Char c) -> std::expected { 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 [[nodiscard]] auto parse_size(std::basic_string_view str) -> std::expected { // 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(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::max() / 10)) co_return error(std::errc::result_out_of_range); ret *= 10; auto digit = static_cast(c - '0'); if ((std::numeric_limits::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::max() / mult)) co_return error(std::errc::result_out_of_range); co_return ret *mult; } export template [[nodiscard]] auto parse_size(char const *s) { return parse_size(std::string_view(s)); } export template [[nodiscard]] auto parse_size(wchar_t const *s) { return parse_size(std::wstring_view(s)); } } // namespace nihil