jam 0.0.1
A compacting generational garbage collector for C++26
Loading...
Searching...
No Matches
units.ccm
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
3
6module;
7#include <chrono>
8#include <cmath>
9#include <compare>
10#include <concepts>
11#include <cstddef>
12#include <cstdint>
13#include <cstdio>
14#include <cstdlib>
15#include <limits>
16#include <ratio>
17#include <type_traits>
18
19export module jam:units;
20
21namespace jam::units::detail {
22[[noreturn]] inline void overflow() noexcept {
23 std::fputs("jam: space arithmetic overflow or invalid conversion\n", stderr);
24 std::abort();
25}
26
27template<class To, class From, class Scale = std::ratio<1>>
28constexpr To convert(From value) noexcept {
29 if constexpr (std::integral<To> && std::integral<From>) {
30 // A 64-bit count times an intmax_t ratio fits this signed intermediate.
31 auto const scaled = static_cast<__int128>(value) * Scale::num / Scale::den;
32 if (scaled < static_cast<__int128>(std::numeric_limits<To>::lowest()) ||
33 scaled > static_cast<__int128>(std::numeric_limits<To>::max())) overflow();
34 return static_cast<To>(scaled);
35 } else {
36 auto const scaled = static_cast<long double>(value) * Scale::num / Scale::den;
37 if constexpr (std::integral<To>) {
38 // Truncate before checking the destination, including -0.5 -> unsigned 0.
39 // This loose exact bound makes the intermediate float-to-integer cast safe.
40 constexpr auto bound = static_cast<long double>(__int128{1} << 65);
41 if (!std::isfinite(scaled) || scaled <= -bound || scaled >= bound) overflow();
42 auto const integral = static_cast<__int128>(scaled);
43 if (integral < static_cast<__int128>(std::numeric_limits<To>::lowest()) ||
44 integral > static_cast<__int128>(std::numeric_limits<To>::max())) overflow();
45 return static_cast<To>(integral);
46 }
47 return static_cast<To>(scaled);
48 }
49}
50
51template<char Op, class R>
52constexpr R arithmetic(R a, R b) noexcept {
53 if constexpr (std::integral<R>) {
54 R result;
55 if constexpr (Op == '+') { if (__builtin_add_overflow(a, b, &result)) overflow(); }
56 if constexpr (Op == '-') { if (__builtin_sub_overflow(a, b, &result)) overflow(); }
57 if constexpr (Op == '*') { if (__builtin_mul_overflow(a, b, &result)) overflow(); }
58 if constexpr (Op == '/' || Op == '%') {
59 if (!b) overflow();
60 if constexpr (std::is_signed_v<R>)
61 if (a == std::numeric_limits<R>::lowest() && b == -1) overflow();
62 if constexpr (Op == '/') result = a / b;
63 else result = a % b;
64 }
65 return result;
66 } else {
67 if constexpr (Op == '+') return a + b;
68 if constexpr (Op == '-') return a - b;
69 if constexpr (Op == '*') return a * b;
70 if constexpr (Op == '/') return a / b;
71 }
72}
73}
74
75export namespace jam::units {
81template<class Rep, class Ratio = std::ratio<1>>
82class space {
83 static_assert(std::is_arithmetic_v<Rep> && !std::same_as<Rep, bool> && sizeof(Rep) <= sizeof(long double));
84 static_assert(!std::integral<Rep> || sizeof(Rep) <= sizeof(std::uint64_t));
85 static_assert(Ratio::num > 0);
86 Rep value;
87public:
88 using rep = Rep;
89 using ratio = typename Ratio::type;
91 constexpr space() = default;
93 template<class R>
94 requires std::is_arithmetic_v<R> && (std::floating_point<Rep> || !std::floating_point<R>)
95 constexpr explicit space(R count) noexcept : value(detail::convert<Rep>(count)) {}
97 template<class R, class P>
98 requires (std::floating_point<Rep> ||
99 (!std::floating_point<R> && std::ratio_divide<P, ratio>::den == 1))
100 constexpr space(space<R, P> other) noexcept
101 : value(detail::convert<Rep, R, std::ratio_divide<P, ratio>>(other.count())) {}
102
103 [[nodiscard]] constexpr rep count() const noexcept { return value; }
105 [[nodiscard]] static constexpr space zero() noexcept { return space{rep{0}}; }
107 [[nodiscard]] static constexpr space min() noexcept { return space{std::numeric_limits<rep>::lowest()}; }
109 [[nodiscard]] static constexpr space max() noexcept { return space{std::numeric_limits<rep>::max()}; }
111 [[nodiscard]] constexpr space operator+() const noexcept { return *this; }
113 [[nodiscard]] constexpr space operator-() const noexcept { return space{detail::arithmetic<'-'>(rep{0}, value)}; }
115 constexpr space & operator++() noexcept { return *this += space{1}; }
117 constexpr space operator++(int) noexcept { auto old = *this; ++*this; return old; }
119 constexpr space & operator--() noexcept { return *this -= space{1}; }
121 constexpr space operator--(int) noexcept { auto old = *this; --*this; return old; }
123 constexpr space & operator+=(space other) noexcept { value = detail::arithmetic<'+'>(value, other.value); return *this; }
125 constexpr space & operator-=(space other) noexcept { value = detail::arithmetic<'-'>(value, other.value); return *this; }
127 constexpr space & operator*=(rep scalar) noexcept { value = detail::arithmetic<'*'>(value, scalar); return *this; }
129 constexpr space & operator/=(rep scalar) noexcept { value = detail::arithmetic<'/'>(value, scalar); return *this; }
131 constexpr space & operator%=(rep scalar) noexcept requires std::integral<rep> { value = detail::arithmetic<'%'>(value, scalar); return *this; }
133 constexpr space & operator%=(space other) noexcept requires std::integral<rep> { return *this %= other.value; }
134};
135}
136
137export namespace std {
139template<class R1, class P1, class R2, class P2>
140struct common_type<jam::units::space<R1, P1>, jam::units::space<R2, P2>> {
141 using duration = common_type_t<chrono::duration<R1, P1>, chrono::duration<R2, P2>>;
143};
144}
145
146export namespace jam::units {
148template<class To, class R, class P>
149[[nodiscard]] constexpr To space_cast(space<R, P> value) noexcept {
150 return To{detail::convert<typename To::rep, R, std::ratio_divide<P, typename To::ratio>>(value.count())};
151}
152
153template<class R1, class P1, class R2, class P2>
154[[nodiscard]] constexpr auto operator<=>(space<R1, P1> a, space<R2, P2> b) noexcept {
155 if constexpr (std::integral<R1> && std::integral<R2>) {
156 using Scale = std::ratio_divide<P1, P2>;
157 return (static_cast<__int128>(a.count()) * Scale::num)
158 <=> (static_cast<__int128>(b.count()) * Scale::den);
159 } else {
160 using C = std::common_type_t<decltype(a), decltype(b)>;
161 return C{a}.count() <=> C{b}.count();
162 }
163}
164
165template<class R1, class P1, class R2, class P2>
166[[nodiscard]] constexpr bool operator==(space<R1, P1> a, space<R2, P2> b) noexcept { return (a <=> b) == 0; }
168template<class R1, class P1, class R2, class P2>
169[[nodiscard]] constexpr auto operator+(space<R1, P1> a, space<R2, P2> b) noexcept {
170 using C = std::common_type_t<decltype(a), decltype(b)>;
171 return C{a} += C{b};
172}
173
174template<class R1, class P1, class R2, class P2>
175[[nodiscard]] constexpr auto operator-(space<R1, P1> a, space<R2, P2> b) noexcept {
176 using C = std::common_type_t<decltype(a), decltype(b)>;
177 return C{a} -= C{b};
178}
179
180template<class R, class P, class S> requires std::is_arithmetic_v<S>
181[[nodiscard]] constexpr auto operator*(space<R, P> a, S b) noexcept {
182 return space<std::common_type_t<R, S>, P>{a} *= b;
183}
184
185template<class R, class P, class S> requires std::is_arithmetic_v<S>
186[[nodiscard]] constexpr auto operator*(S a, space<R, P> b) noexcept { return b * a; }
188template<class R, class P, class S> requires std::is_arithmetic_v<S>
189[[nodiscard]] constexpr auto operator/(space<R, P> a, S b) noexcept {
190 return space<std::common_type_t<R, S>, P>{a} /= b;
191}
192
193template<class R1, class P1, class R2, class P2>
194[[nodiscard]] constexpr auto operator/(space<R1, P1> a, space<R2, P2> b) noexcept {
195 using C = std::common_type_t<decltype(a), decltype(b)>;
196 return detail::arithmetic<'/'>(C{a}.count(), C{b}.count());
197}
198
199template<std::integral R, class P, std::integral S>
200[[nodiscard]] constexpr auto operator%(space<R, P> a, S b) noexcept { return space<std::common_type_t<R, S>, P>{a} %= b; }
202template<std::integral R1, class P1, std::integral R2, class P2>
203[[nodiscard]] constexpr auto operator%(space<R1, P1> a, space<R2, P2> b) noexcept {
204 using C = std::common_type_t<decltype(a), decltype(b)>;
205 return C{a} %= C{b};
206}
207
208template<class To, class R, class P>
209[[nodiscard]] constexpr To floor(space<R, P> value) noexcept {
210 auto result = space_cast<To>(value);
211 if (result > value) --result;
212 return result;
213}
214
215template<class To, class R, class P>
216[[nodiscard]] constexpr To ceil(space<R, P> value) noexcept {
217 auto result = space_cast<To>(value);
218 if (result < value) ++result;
219 return result;
220}
221
222template<class To, class R, class P> requires std::integral<typename To::rep>
223[[nodiscard]] constexpr To round(space<R, P> value) noexcept {
224 auto const lo = floor<To>(value);
225 if (lo == value) return lo;
226 auto hi = lo; ++hi;
227 auto const down = value - lo, up = hi - value;
228 return down < up || (down == up && lo.count() % 2 == 0) ? lo : hi;
229}
230
231template<class R, class P> requires std::is_signed_v<R>
232[[nodiscard]] constexpr auto abs(space<R, P> value) noexcept { return value < value.zero() ? -value : value; }
233
234using kibi = std::ratio<1024>;
235using mebi = std::ratio<1048576>;
236using gibi = std::ratio<1073741824>;
245}
246
247export namespace jam {
249inline namespace literals {
251[[nodiscard]] consteval units::bytes operator""_B(unsigned long long n) noexcept { return units::bytes{n}; }
253[[nodiscard]] consteval units::space<long double, std::ratio<1>> operator""_B(long double n) noexcept { return units::space<long double, std::ratio<1>>{n}; }
255[[nodiscard]] consteval units::kilobytes operator""_kB(unsigned long long n) noexcept { return units::kilobytes{n}; }
257[[nodiscard]] consteval units::space<long double, std::kilo> operator""_kB(long double n) noexcept { return units::space<long double, std::kilo>{n}; }
259[[nodiscard]] consteval units::megabytes operator""_MB(unsigned long long n) noexcept { return units::megabytes{n}; }
261[[nodiscard]] consteval units::space<long double, std::mega> operator""_MB(long double n) noexcept { return units::space<long double, std::mega>{n}; }
263[[nodiscard]] consteval units::gigabytes operator""_GB(unsigned long long n) noexcept { return units::gigabytes{n}; }
265[[nodiscard]] consteval units::space<long double, std::giga> operator""_GB(long double n) noexcept { return units::space<long double, std::giga>{n}; }
267[[nodiscard]] consteval units::kibibytes operator""_KiB(unsigned long long n) noexcept { return units::kibibytes{n}; }
269[[nodiscard]] consteval units::space<long double, units::kibi> operator""_KiB(long double n) noexcept { return units::space<long double, units::kibi>{n}; }
271[[nodiscard]] consteval units::mebibytes operator""_MiB(unsigned long long n) noexcept { return units::mebibytes{n}; }
273[[nodiscard]] consteval units::space<long double, units::mebi> operator""_MiB(long double n) noexcept { return units::space<long double, units::mebi>{n}; }
275[[nodiscard]] consteval units::gibibytes operator""_GiB(unsigned long long n) noexcept { return units::gibibytes{n}; }
277[[nodiscard]] consteval units::space<long double, units::gibi> operator""_GiB(long double n) noexcept { return units::space<long double, units::gibi>{n}; }
278}
279}
A count of storage units; Ratio measures one unit in bytes. Integer arithmetic and conversions termin...
Definition units.ccm:82
constexpr space()=default
Default-initialize the representation, as with chrono durations.
typename Ratio::type ratio
Definition units.ccm:89
constexpr space & operator++() noexcept
Add one unit.
Definition units.ccm:115
constexpr space operator-() const noexcept
Negate the count; unrepresentable negation terminates.
Definition units.ccm:113
constexpr space & operator*=(rep scalar) noexcept
Multiply by a scalar count.
Definition units.ccm:127
constexpr space & operator+=(space other) noexcept
Add an exactly convertible amount.
Definition units.ccm:123
static constexpr space zero() noexcept
Zero units.
Definition units.ccm:105
constexpr space(space< R, P > other) noexcept
Implicit conversion when the destination can express every source tick.
Definition units.ccm:100
constexpr space & operator/=(rep scalar) noexcept
Divide by a scalar count.
Definition units.ccm:129
constexpr space & operator-=(space other) noexcept
Subtract an exactly convertible amount.
Definition units.ccm:125
constexpr space operator--(int) noexcept
Subtract one unit, returning the previous count.
Definition units.ccm:121
static constexpr space min() noexcept
Least representable count.
Definition units.ccm:107
static constexpr space max() noexcept
Greatest representable count.
Definition units.ccm:109
constexpr space operator+() const noexcept
Preserve the count.
Definition units.ccm:111
constexpr space(R count) noexcept
Explicitly construct from a count, without implicit float-to-integer loss.
Definition units.ccm:95
constexpr space operator++(int) noexcept
Add one unit, returning the previous count.
Definition units.ccm:117
constexpr space & operator%=(space other) noexcept
Remainder after whole multiples of an amount.
Definition units.ccm:133
Rep rep
Count representation.
Definition units.ccm:88
constexpr space & operator%=(rep scalar) noexcept
Remainder of an integral count divided by a scalar.
Definition units.ccm:131
constexpr space & operator--() noexcept
Subtract one unit.
Definition units.ccm:119
Decimal and binary byte literals; counts retain their units.
Definition units.ccm:249
space< std::size_t, std::mega > megabytes
Millions of bytes.
Definition units.ccm:239
std::ratio< 1024 > kibi
Binary kilo: 1024.
Definition units.ccm:234
constexpr auto operator*(space< R, P > a, S b) noexcept
Multiply an amount by a scalar, preserving its unit.
Definition units.ccm:181
constexpr bool operator==(space< R1, P1 > a, space< R2, P2 > b) noexcept
Equal amounts may have different units or representations.
Definition units.ccm:166
constexpr auto operator%(space< R, P > a, S b) noexcept
Integral remainder in the original unit.
Definition units.ccm:200
constexpr To round(space< R, P > value) noexcept
Nearest integral destination count; ties round to even.
Definition units.ccm:223
space< std::size_t, std::giga > gigabytes
Billions of bytes.
Definition units.ccm:240
space< std::size_t, mebi > mebibytes
Groups of 1048576 bytes.
Definition units.ccm:242
constexpr auto operator+(space< R1, P1 > a, space< R2, P2 > b) noexcept
Add amounts in their common unit.
Definition units.ccm:169
std::ratio< 1048576 > mebi
Binary mega: 1024 squared.
Definition units.ccm:235
constexpr auto operator/(space< R, P > a, S b) noexcept
Divide an amount by a scalar, preserving its unit.
Definition units.ccm:189
constexpr To ceil(space< R, P > value) noexcept
Least whole destination count not less than the source.
Definition units.ccm:216
constexpr To floor(space< R, P > value) noexcept
Greatest whole destination count not exceeding the source.
Definition units.ccm:209
constexpr auto operator<=>(space< R1, P1 > a, space< R2, P2 > b) noexcept
Compare amounts without overflowing an integral common-unit conversion.
Definition units.ccm:154
std::ratio< 1073741824 > gibi
Binary giga: 1024 cubed.
Definition units.ccm:236
space< std::size_t, std::ratio< JAM_PAGE_BYTES > > pages
Platform pages, fixed at build time.
Definition units.ccm:244
space< std::size_t, gibi > gibibytes
Groups of 1073741824 bytes.
Definition units.ccm:243
space< std::size_t, std::kilo > kilobytes
Thousands of bytes.
Definition units.ccm:238
space< std::size_t, kibi > kibibytes
Groups of 1024 bytes.
Definition units.ccm:241
constexpr auto operator-(space< R1, P1 > a, space< R2, P2 > b) noexcept
Subtract amounts in their common unit.
Definition units.ccm:175
constexpr To space_cast(space< R, P > value) noexcept
Explicit conversion; fractional integral ticks truncate toward zero.
Definition units.ccm:149
space< std::size_t > bytes
Whole bytes.
Definition units.ccm:237
constexpr auto abs(space< R, P > value) noexcept
Absolute value of a signed amount.
Definition units.ccm:232