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