21namespace jam::units::detail {
22[[noreturn]]
inline void overflow() noexcept {
23 std::fputs(
"jam: space arithmetic overflow or invalid conversion\n", stderr);
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>) {
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);
36 auto const scaled =
static_cast<long double>(value) * Scale::num / Scale::den;
37 if constexpr (std::integral<To>) {
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);
47 return static_cast<To
>(scaled);
51template<
char Op,
class R>
52constexpr R arithmetic(R a, R b)
noexcept {
53 if constexpr (std::integral<R>) {
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 ==
'%') {
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;
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;
75export namespace jam::units {
81template<
class Rep,
class Ratio = std::ratio<1>>
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);
89 using ratio =
typename Ratio::type;
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))
101 : value(detail::convert<Rep, R, std::ratio_divide<P, ratio>>(other.
count())) {}
103 [[nodiscard]]
constexpr rep count() const noexcept {
return value; }
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()}; }
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; }
137export namespace std {
139template<
class R1,
class P1,
class R2,
class P2>
141 using duration = common_type_t<chrono::duration<R1, P1>, chrono::duration<R2, P2>>;
146export namespace jam::units {
148template<
class To,
class R,
class P>
150 return To{detail::convert<typename To::rep, R, std::ratio_divide<P, typename To::ratio>>(value.count())};
153template<
class R1,
class P1,
class R2,
class P2>
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);
160 using C = std::common_type_t<
decltype(a),
decltype(b)>;
161 return C{a}.count() <=> C{b}.count();
165template<
class R1,
class P1,
class R2,
class P2>
168template<
class R1,
class P1,
class R2,
class P2>
170 using C = std::common_type_t<
decltype(a),
decltype(b)>;
174template<
class R1,
class P1,
class R2,
class P2>
176 using C = std::common_type_t<
decltype(a),
decltype(b)>;
180template<
class R,
class P,
class S>
requires std::is_arithmetic_v<S>
185template<
class R,
class P,
class S>
requires std::is_arithmetic_v<S>
188template<
class R,
class P,
class S>
requires std::is_arithmetic_v<S>
193template<
class R1,
class P1,
class R2,
class P2>
195 using C = std::common_type_t<
decltype(a),
decltype(b)>;
196 return detail::arithmetic<
'/'>(C{a}.count(), C{b}.count());
199template<std::
integral R,
class P, std::
integral S>
202template<std::
integral R1,
class P1, std::
integral R2,
class P2>
204 using C = std::common_type_t<
decltype(a),
decltype(b)>;
208template<
class To,
class R,
class P>
211 if (result > value) --result;
215template<
class To,
class R,
class P>
218 if (result < value) ++result;
222template<
class To,
class R,
class P>
requires std::integral<typename To::rep>
225 if (lo == value)
return lo;
227 auto const down = value - lo, up = hi - value;
228 return down < up || (down == up && lo.count() % 2 == 0) ? lo : hi;
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; }
235using mebi = std::ratio<1048576>;
236using gibi = std::ratio<1073741824>;
247export namespace jam {
A count of storage units; Ratio measures one unit in bytes. Integer arithmetic and conversions termin...
constexpr space()=default
Default-initialize the representation, as with chrono durations.
typename Ratio::type ratio
constexpr space & operator++() noexcept
Add one unit.
constexpr space operator-() const noexcept
Negate the count; unrepresentable negation terminates.
constexpr space & operator*=(rep scalar) noexcept
Multiply by a scalar count.
constexpr space & operator+=(space other) noexcept
Add an exactly convertible amount.
static constexpr space zero() noexcept
Zero units.
constexpr space(space< R, P > other) noexcept
Implicit conversion when the destination can express every source tick.
constexpr space & operator/=(rep scalar) noexcept
Divide by a scalar count.
constexpr space & operator-=(space other) noexcept
Subtract an exactly convertible amount.
constexpr space operator--(int) noexcept
Subtract one unit, returning the previous count.
static constexpr space min() noexcept
Least representable count.
constexpr rep count() const noexcept
static constexpr space max() noexcept
Greatest representable count.
constexpr space operator+() const noexcept
Preserve the count.
constexpr space(R count) noexcept
Explicitly construct from a count, without implicit float-to-integer loss.
constexpr space operator++(int) noexcept
Add one unit, returning the previous count.
constexpr space & operator%=(space other) noexcept
Remainder after whole multiples of an amount.
Rep rep
Count representation.
constexpr space & operator%=(rep scalar) noexcept
Remainder of an integral count divided by a scalar.
constexpr space & operator--() noexcept
Subtract one unit.
Decimal and binary byte literals; counts retain their units.
space< std::size_t, std::mega > megabytes
Millions of bytes.
std::ratio< 1024 > kibi
Binary kilo: 1024.
constexpr auto operator*(space< R, P > a, S b) noexcept
Multiply an amount by a scalar, preserving its unit.
constexpr bool operator==(space< R1, P1 > a, space< R2, P2 > b) noexcept
Equal amounts may have different units or representations.
constexpr auto operator%(space< R, P > a, S b) noexcept
Integral remainder in the original unit.
constexpr To round(space< R, P > value) noexcept
Nearest integral destination count; ties round to even.
space< std::size_t, std::giga > gigabytes
Billions of bytes.
space< std::size_t, mebi > mebibytes
Groups of 1048576 bytes.
constexpr auto operator+(space< R1, P1 > a, space< R2, P2 > b) noexcept
Add amounts in their common unit.
std::ratio< 1048576 > mebi
Binary mega: 1024 squared.
constexpr auto operator/(space< R, P > a, S b) noexcept
Divide an amount by a scalar, preserving its unit.
constexpr To ceil(space< R, P > value) noexcept
Least whole destination count not less than the source.
constexpr To floor(space< R, P > value) noexcept
Greatest whole destination count not exceeding the source.
constexpr auto operator<=>(space< R1, P1 > a, space< R2, P2 > b) noexcept
Compare amounts without overflowing an integral common-unit conversion.
std::ratio< 1073741824 > gibi
Binary giga: 1024 cubed.
space< std::size_t, std::ratio< JAM_PAGE_BYTES > > pages
Platform pages, fixed at build time.
space< std::size_t, gibi > gibibytes
Groups of 1073741824 bytes.
space< std::size_t, std::kilo > kilobytes
Thousands of bytes.
space< std::size_t, kibi > kibibytes
Groups of 1024 bytes.
constexpr auto operator-(space< R1, P1 > a, space< R2, P2 > b) noexcept
Subtract amounts in their common unit.
constexpr To space_cast(space< R, P > value) noexcept
Explicit conversion; fractional integral ticks truncate toward zero.
space< std::size_t > bytes
Whole bytes.
constexpr auto abs(space< R, P > value) noexcept
Absolute value of a signed amount.