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