diff options
Diffstat (limited to 'nihil.util/parse_size.ccm')
| -rw-r--r-- | nihil.util/parse_size.ccm | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/nihil.util/parse_size.ccm b/nihil.util/parse_size.ccm new file mode 100644 index 0000000..c95ac50 --- /dev/null +++ b/nihil.util/parse_size.ccm @@ -0,0 +1,107 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <coroutine> +#include <cstdint> +#include <expected> +#include <ranges> +#include <string> +#include <system_error> +#include <utility> + +export module nihil.util:parse_size; + +import nihil.core; +import nihil.error; +import nihil.monad; + +import :ctype; + +namespace nihil { + +template<typename Char> +auto get_multiplier(Char c) -> std::expected<std::uint64_t, error> +{ + auto ret = std::uint64_t{1}; + + 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 std::unexpected(error(errc::invalid_unit)); + } +} + +/* + * 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 std::unexpected(error(errc::empty_string)); + + auto ret = T{0}; + + for (auto c : num_str) { + if (ret > (std::numeric_limits<T>::max() / 10)) + co_return std::unexpected(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 std::unexpected(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 std::unexpected(error(errc::invalid_unit)); + + auto mult = co_await get_multiplier(mchar); + + if (std::cmp_greater(ret, std::numeric_limits<T>::max() / mult)) + co_return std::unexpected(error( + std::errc::result_out_of_range)); + + co_return ret * mult; +} + +export template<typename T> +[[nodiscard]] inline auto parse_size(char const *s) +{ + return parse_size<T>(std::string_view(s)); +} + +export template<typename T> +[[nodiscard]] inline auto parse_size(wchar_t const *s) +{ + return parse_size<T>(std::wstring_view(s)); +} + +} |
