Nix 2.26.3
Nix, the purely functional package manager; unstable internal interfaces
 
Loading...
Searching...
No Matches
checked-arithmetic.hh
Go to the documentation of this file.
1#pragma once
7
8#include <compare>
9#include <concepts> // IWYU pragma: keep
10#include <exception>
11#include <ostream>
12#include <limits>
13#include <optional>
14#include <type_traits>
15
16namespace nix::checked {
17
18class DivideByZero : std::exception
19{};
20
25template<std::integral T>
26struct Checked
27{
28 using Inner = T;
29
30 // TODO: this must be a "trivial default constructor", which means it
31 // cannot set the value to NOT DO UB on uninit.
32 T value;
33
34 Checked() = default;
35 explicit Checked(T const value)
36 : value{value}
37 {
38 }
39 Checked(Checked<T> const & other) = default;
40 Checked(Checked<T> && other) = default;
41 Checked<T> & operator=(Checked<T> const & other) = default;
42
43 std::strong_ordering operator<=>(Checked<T> const & other) const = default;
44 std::strong_ordering operator<=>(T const & other) const
45 {
46 return value <=> other;
47 }
48
49 explicit operator T() const
50 {
51 return value;
52 }
53
54 enum class OverflowKind {
55 NoOverflow,
56 Overflow,
57 DivByZero,
58 };
59
60 class Result
61 {
62 T value;
63 OverflowKind overflowed_;
64
65 public:
66 Result(T value, bool overflowed)
67 : value{value}
68 , overflowed_{overflowed ? OverflowKind::Overflow : OverflowKind::NoOverflow}
69 {
70 }
71 Result(T value, OverflowKind overflowed)
72 : value{value}
73 , overflowed_{overflowed}
74 {
75 }
76
77 bool operator==(Result other) const
78 {
79 return value == other.value && overflowed_ == other.overflowed_;
80 }
81
82 std::optional<T> valueChecked() const
83 {
84 if (overflowed_ != OverflowKind::NoOverflow) {
85 return std::nullopt;
86 } else {
87 return value;
88 }
89 }
90
96 T valueWrapping() const
97 {
98 if (overflowed_ == OverflowKind::DivByZero) {
99 throw DivideByZero{};
100 }
101 return value;
102 }
103
104 bool overflowed() const
105 {
106 return overflowed_ == OverflowKind::Overflow;
107 }
108
109 bool divideByZero() const
110 {
111 return overflowed_ == OverflowKind::DivByZero;
112 }
113 };
114
115 Result operator+(Checked<T> const other) const
116 {
117 return (*this) + other.value;
118 }
119 Result operator+(T const other) const
120 {
121 T result;
122 bool overflowed = __builtin_add_overflow(value, other, &result);
123 return Result{result, overflowed};
124 }
125
126 Result operator-(Checked<T> const other) const
127 {
128 return (*this) - other.value;
129 }
130 Result operator-(T const other) const
131 {
132 T result;
133 bool overflowed = __builtin_sub_overflow(value, other, &result);
134 return Result{result, overflowed};
135 }
136
137 Result operator*(Checked<T> const other) const
138 {
139 return (*this) * other.value;
140 }
141 Result operator*(T const other) const
142 {
143 T result;
144 bool overflowed = __builtin_mul_overflow(value, other, &result);
145 return Result{result, overflowed};
146 }
147
148 Result operator/(Checked<T> const other) const
149 {
150 return (*this) / other.value;
151 }
158 Result operator/(T const other) const
159 {
160 constexpr T const minV = std::numeric_limits<T>::min();
161
162 // It's only possible to overflow with signed division since doing so
163 // requires crossing the two's complement limits by MIN / -1 (since
164 // two's complement has one more in range in the negative direction
165 // than in the positive one).
166 if (std::is_signed<T>() && (value == minV && other == -1)) {
167 return Result{minV, true};
168 } else if (other == 0) {
169 return Result{0, OverflowKind::DivByZero};
170 } else {
171 T result = value / other;
172 return Result{result, false};
173 }
174 }
175};
176
177template<std::integral T>
178std::ostream & operator<<(std::ostream & ios, Checked<T> v)
179{
180 ios << v.value;
181 return ios;
182}
183
184}
Definition lexer.l:4953
Definition checked-arithmetic.hh:61
T valueWrapping() const
Definition checked-arithmetic.hh:96
Definition checked-arithmetic.hh:19
Result operator/(T const other) const
Definition checked-arithmetic.hh:158