HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
safe_int.hpp
1// Copyright Take Vos 2019, 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "int_overflow.hpp"
8#include "utility/module.hpp"
9#include <system_error>
10#include <type_traits>
11#include <limits>
12#include <concepts>
13
14namespace hi::inline v1 {
15
16enum class on_overflow_t {
18 Throw,
22 Assert,
24 Axiom,
25};
26
30constexpr on_overflow_t operator|(on_overflow_t lhs, on_overflow_t rhs) noexcept
31{
32 if (lhs == on_overflow_t::Throw || rhs == on_overflow_t::Throw) {
33 return on_overflow_t::Throw;
34 } else if (lhs == on_overflow_t::Saturate || rhs == on_overflow_t::Saturate) {
35 return on_overflow_t::Saturate;
36 } else if (lhs == on_overflow_t::Assert || rhs == on_overflow_t::Assert) {
37 return on_overflow_t::Assert;
38 } else {
39 return on_overflow_t::Axiom;
40 }
41}
42
48template<typename T, on_overflow_t OnOverflow>
49T safe_handle_overflow(T value, bool overflow, bool is_positive) noexcept(OnOverflow != on_overflow_t::Throw)
50{
51 if constexpr (OnOverflow == on_overflow_t::Throw) {
52 if (overflow) {
53 throw std::overflow_error("integer overflow");
54 }
55 } else if constexpr (OnOverflow == on_overflow_t::Assert) {
56 hi_assert(!overflow);
57 } else if constexpr (OnOverflow == on_overflow_t::Axiom) {
58 hi_axiom(!overflow);
59 } else if constexpr (OnOverflow == on_overflow_t::Saturate) {
60 if (overflow) {
61 [[unlikely]] value = is_positive ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min();
62 }
63 }
64 return value;
65}
66
67template<typename T, on_overflow_t OnOverflow, typename U>
68T safe_convert(U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
69{
70 T r;
71 // Optimized away when is_same_v<T,U>
72 hilet overflow = convert_overflow(rhs, &r);
73 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs >= 0);
74}
75
76template<on_overflow_t OnOverflow, typename T, typename U>
77make_promote_t<T, U> safe_add(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
78{
79 make_promote_t<T, U> r;
80 hilet lhs_ = static_cast<make_promote_t<T, U>>(lhs);
81 hilet rhs_ = static_cast<make_promote_t<T, U>>(rhs);
82
83 hilet overflow = add_overflow(lhs_, rhs_, &r);
84 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs_ >= 0);
85}
86
87template<on_overflow_t OnOverflow, typename T, typename U>
88make_promote_t<T, U> safe_sub(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
89{
90 make_promote_t<T, U> r;
91 hilet lhs_ = static_cast<make_promote_t<T, U>>(lhs);
92 hilet rhs_ = static_cast<make_promote_t<T, U>>(rhs);
93
94 hilet overflow = sub_overflow(lhs_, rhs_, &r);
95 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs_ >= 0);
96}
97
98template<on_overflow_t OnOverflow, typename T, typename U>
99make_promote_t<T, U> safe_mul(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
100{
101 make_promote_t<T, U> r;
102 hilet lhs_ = static_cast<make_promote_t<T, U>>(lhs);
103 hilet rhs_ = static_cast<make_promote_t<T, U>>(rhs);
104
105 hilet overflow = mul_overflow(lhs_, rhs_, &r);
106 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs_ >= 0);
107}
108
109template<typename T, on_overflow_t OnOverflow = on_overflow_t::Assert>
110struct safe_int {
111 using value_type = T;
112 constexpr static on_overflow_t on_overflow = OnOverflow;
113
114 T value;
115
116 safe_int() : value(0) {}
117 ~safe_int() = default;
118 safe_int(safe_int const &) = default;
119 safe_int(safe_int &&) = default;
120 safe_int &operator=(safe_int const &) = default;
121 safe_int &operator=(safe_int &&) = default;
122
123 explicit safe_int(std::integral auto const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
124 value(safe_convert<T, OnOverflow>(other))
125 {
126 }
127
128 explicit safe_int(std::floating_point auto const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
129 value(safe_convert<T, OnOverflow>(other))
130 {
131 }
132
133 template<typename O, on_overflow_t OtherOnOverflow>
134 explicit safe_int(safe_int<O, OtherOnOverflow> const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
135 value(safe_convert<T, OnOverflow>(other.value))
136 {
137 }
138
139 safe_int &operator=(std::integral auto const &other) noexcept(OnOverflow != on_overflow_t::Throw)
140 {
141 value = safe_convert<T, OnOverflow>(other);
142 return *this;
143 }
144
145 template<typename O, on_overflow_t OtherOnOverflow>
146 safe_int &operator=(safe_int<O, OtherOnOverflow> const &other) noexcept(OnOverflow != on_overflow_t::Throw)
147 {
148 value = safe_convert<T, OnOverflow>(other.value);
149 return *this;
150 }
151
152 template<std::integral O>
153 explicit operator O() const noexcept(OnOverflow != on_overflow_t::Throw)
154 {
155 return safe_convert<O, OnOverflow>(value);
156 }
157
158 template<std::floating_point O>
159 explicit operator O() const noexcept
160 {
161 return static_cast<O>(value);
162 }
163};
164
165#define TEMPLATE(op) \
166 template<typename T, on_overflow_t TO, typename U, on_overflow_t UO> \
167 bool operator op(safe_int<T, TO> const &lhs, safe_int<U, UO> const &rhs) noexcept \
168 { \
169 return lhs.value op rhs.value; \
170 } \
171\
172 template<typename T, on_overflow_t TO, typename U> \
173 bool operator op(safe_int<T, TO> const &lhs, U const &rhs) noexcept \
174 { \
175 return lhs.value op rhs; \
176 } \
177 template<typename T, typename U, on_overflow_t UO> \
178 bool operator op(T const &lhs, safe_int<U, UO> const &rhs) noexcept \
179 { \
180 return lhs op rhs.value; \
181 }
182
183TEMPLATE(==)
184TEMPLATE(!=)
185TEMPLATE(<)
186TEMPLATE(>)
187TEMPLATE(<=)
188TEMPLATE(>=)
189#undef TEMPLATE
190
191#define TEMPLATE(op, func) \
192 template<typename T, on_overflow_t TO, typename U, on_overflow_t UO> \
193 safe_int<make_promote_t<T, U>, TO | UO> operator op(safe_int<T, TO> const &lhs, safe_int<U, UO> const &rhs) noexcept( \
194 (TO | UO) != on_overflow_t::Throw) \
195 { \
196 return safe_int<make_promote_t<T, U>, TO | UO>{func<TO | UO>(lhs.value, rhs.value)}; \
197 } \
198\
199 template<typename T, on_overflow_t TO, typename U> \
200 safe_int<make_promote_t<T, U>, TO> operator op(safe_int<T, TO> const &lhs, U const &rhs) noexcept( \
201 TO != on_overflow_t::Throw) \
202 { \
203 return safe_int<make_promote_t<T, U>, TO>{func<TO>(lhs.value, rhs)}; \
204 } \
205\
206 template<typename T, typename U, on_overflow_t UO> \
207 safe_int<make_promote_t<T, U>, UO> operator op(T const &lhs, safe_int<U, UO> const &rhs) noexcept( \
208 UO != on_overflow_t::Throw) \
209 { \
210 return safe_int<make_promote_t<T, U>, UO>{func<UO>(lhs, rhs.value)}; \
211 }
212
213TEMPLATE(+, safe_add)
214TEMPLATE(-, safe_sub)
215TEMPLATE(*, safe_mul)
216#undef TEMPLATE
217
226
235
244
253
254} // namespace hi::inline v1
255
256template<>
257class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Saturate>> :
258 public std::numeric_limits<signed long long> {
259};
260template<>
261class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed long> {
262};
263template<>
264class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed int> {
265};
266template<>
267class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed short> {
268};
269template<>
270class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed char> {
271};
272template<>
273class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Saturate>> :
274 public std::numeric_limits<unsigned long long> {
275};
276template<>
277class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Saturate>> : public std::numeric_limits<unsigned long> {
278};
279template<>
280class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Saturate>> : public std::numeric_limits<unsigned int> {
281};
282template<>
283class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Saturate>> :
284 public std::numeric_limits<unsigned short> {
285};
286template<>
287class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Saturate>> : public std::numeric_limits<unsigned char> {
288};
289
290template<>
291class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Assert>> :
292 public std::numeric_limits<signed long long> {
293};
294template<>
295class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed long> {
296};
297template<>
298class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed int> {
299};
300template<>
301class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed short> {
302};
303template<>
304class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed char> {
305};
306template<>
307class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Assert>> :
308 public std::numeric_limits<unsigned long long> {
309};
310template<>
311class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned long> {
312};
313template<>
314class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned int> {
315};
316template<>
317class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned short> {
318};
319template<>
320class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned char> {
321};
322
323template<>
324class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Throw>> :
325 public std::numeric_limits<signed long long> {
326};
327template<>
328class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed long> {
329};
330template<>
331class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed int> {
332};
333template<>
334class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed short> {
335};
336template<>
337class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed char> {
338};
339template<>
340class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Throw>> :
341 public std::numeric_limits<unsigned long long> {
342};
343template<>
344class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned long> {
345};
346template<>
347class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned int> {
348};
349template<>
350class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned short> {
351};
352template<>
353class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned char> {
354};
355
356template<>
357class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Axiom>> :
358 public std::numeric_limits<signed long long> {
359};
360template<>
361class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed long> {
362};
363template<>
364class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed int> {
365};
366template<>
367class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed short> {
368};
369template<>
370class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed char> {
371};
372template<>
373class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Axiom>> :
374 public std::numeric_limits<unsigned long long> {
375};
376template<>
377class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned long> {
378};
379template<>
380class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned int> {
381};
382template<>
383class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned short> {
384};
385template<>
386class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned char> {
387};
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:184
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
T safe_handle_overflow(T value, bool overflow, bool is_positive) noexcept(OnOverflow !=on_overflow_t::Throw)
Definition safe_int.hpp:49
constexpr bool mul_overflow(T lhs, T rhs, T *r) noexcept
Multiply with overflow detection.
Definition int_overflow.hpp:95
on_overflow_t
Definition safe_int.hpp:16
@ Axiom
On overflow assert and teminate in debug, assume in release.
@ Throw
On overflow throw an exception.
@ Assert
On overflow assert and terminate.
@ Saturate
On overflow saturate the result in the appropiate direction.
geometry/margins.hpp
Definition cache.hpp:11
Definition safe_int.hpp:110
T max(T... args)
T min(T... args)