aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.core/parse_size.ccm
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-07-02 04:00:06 +0100
committerLexi Winter <lexi@le-fay.org>2025-07-02 04:00:06 +0100
commit5adeb648f74c1771164c0686d6e0fc584cf36d9e (patch)
tree060cd918d3dd9e931a1541a43c9edff1a404ff47 /nihil.core/parse_size.ccm
parent06fafff8e9e9c096cc39bde0306caa53ad3a2351 (diff)
downloadnihil-5adeb648f74c1771164c0686d6e0fc584cf36d9e.tar.gz
nihil-5adeb648f74c1771164c0686d6e0fc584cf36d9e.tar.bz2
move everything from util to core
Diffstat (limited to 'nihil.core/parse_size.ccm')
-rw-r--r--nihil.core/parse_size.ccm90
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