aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.core/parse_size.ccm
blob: 5f80755a80868ca03567591207a35bd6167865aa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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