sanisizer
Sanitize sizes to avoid integer overflow
Loading...
Searching...
No Matches
sum.hpp
Go to the documentation of this file.
1#ifndef SANISIZER_SUM_HPP
2#define SANISIZER_SUM_HPP
3
4#include <type_traits>
5#include <stdexcept>
6#include <optional>
7
8#include "generics.hpp"
9#include "Bounded.hpp"
10#include "Constant.hpp"
11#include "utils.hpp"
12#include "comparisons.hpp"
13
19namespace sanisizer {
20
24template<typename Dest_, typename First_, typename Second_>
25constexpr bool needs_sum_check() {
26 constexpr Dest_ dest_maxed = get_max<Dest_>();
27 constexpr auto first_maxed = get_max<First_>();
28 if constexpr(is_greater_than(first_maxed, dest_maxed)) {
29 return true;
30 } else {
31 constexpr Dest_ delta = dest_maxed - static_cast<Dest_>(first_maxed);
32 return is_greater_than(get_max<Second_>(), delta);
33 }
34}
35
36template<typename Dest_, typename First_, typename Second_>
37constexpr bool does_sum_overflow_ordered(First_ first, Second_ second) {
38 constexpr Dest_ dest_maxed = get_max<Dest_>();
39 const auto first_val = get_value(first);
40 if (is_greater_than(first_val, dest_maxed)) { // This check can be compiled out if First_ is not greater than Dest_.
41 return true;
42 } else {
43 const Dest_ delta = dest_maxed - static_cast<Dest_>(first_val); // If First_ is Constant, then the compiler could compute 'delta' at compile time.
44 return is_greater_than(get_value(second), delta);
45 }
46}
47
48template<typename Dest_, typename First_, typename Second_>
49constexpr bool does_sum_overflow(First_ first, Second_ second) {
50 if constexpr(is_Constant<First_>::value) {
51 // Shift threshold calculation to compile-time.
52 return does_sum_overflow_ordered<Dest_>(first, second);
53
54 } else if constexpr(is_Constant<Second_>::value) {
55 // Shift threshold calculation to compile-time.
56 return does_sum_overflow_ordered<Dest_>(second, first);
57
58 } else if constexpr(is_less_than_or_equal(get_max<First_>(), get_max<Dest_>())) {
59 // Skip overflow check when First_ is not greater than Dest_.
60 return does_sum_overflow_ordered<Dest_>(first, second);
61
62 } else {
63 // Skip overflow check if Second_ is not greater than Dest_.
64 // If not, the order doesn't matter w.r.t. the amount of work, so we might as well use this as the default case.
65 return does_sum_overflow_ordered<Dest_>(second, first);
66 }
67}
68
69template<typename Dest_, typename First_, typename Second_>
70constexpr auto sum_protected(First_ first, Second_ second) {
71 static_assert(std::is_integral<Dest_>::value);
72
73 if constexpr(needs_sum_check<Dest_, First_, Second_>()) {
74 if (does_sum_overflow<Dest_>(first, second)) {
75 return std::optional<Dest_>();
76 } else {
77 // Cast before sum to avoid overflow in the input types or whatever integer promotion decides to use.
78 return std::make_optional<Dest_>(static_cast<Dest_>(get_value(first)) + static_cast<Dest_>(get_value(second)));
79 }
80
81 } else {
82 constexpr Dest_ maxsum = static_cast<Dest_>(get_max<First_>()) + static_cast<Dest_>(get_max<Second_>());
84 // Return a Constant in case we can do more compile-time stuff in the next chained operation.
85 // No need to this when 'needs_sum_check' is true as it will always fail with two Constants.
86 return std::make_optional(Constant<Dest_, maxsum>());
87 } else {
88 return std::make_optional<Bounded<Dest_, maxsum> >(static_cast<Dest_>(get_value(first)) + static_cast<Dest_>(get_value(second)));
89 }
90 }
91}
92
93template<typename Dest_, typename First_, typename Second_, typename ... Args_>
94constexpr auto sum_protected(First_ first, Second_ second, Args_... more) {
95 const auto subsum = sum_protected<Dest_>(first, second);
96 if (subsum.has_value()) {
97 return sum_protected<Dest_>(*subsum, more...);
98 } else {
99 return I<decltype(sum_protected<Dest_>(*subsum, more...))>();
100 }
101}
102
124template<typename Dest_, typename First_, typename ... Args_>
125constexpr std::optional<Dest_> sum_maybe(First_ first, Args_... more) {
126 const auto res = sum_protected<Dest_>(first, more...);
127 if (res.has_value()) {
128 return std::make_optional<Dest_>(get_value(*res));
129 } else {
130 return std::optional<Dest_>();
131 }
132}
133
152template<typename Dest_, typename First_, typename ... Args_>
153constexpr Dest_ sum(First_ first, Args_... more) {
154 const auto res = sum_maybe<Dest_>(first, more...);
155 if (!res.has_value()) {
156 throw std::overflow_error("detected " + failure<Dest_>() + " during summation");
157 }
158 return *res;
159}
160
172template<typename Dest_, typename Value_>
173Dest_ plus_one(Value_ x) {
174 return sum<Dest_>(x, Constant<int, 1>());
175}
176
188template<typename Integer_>
189Integer_& increment(Integer_& x) {
190 x = plus_one<Integer_>(x);
191 return x;
192}
193
197template<typename Dest_, typename First_, typename Second_>
198constexpr Dest_ sum_unprotected(First_ first, Second_ second) {
199 static_assert(std::is_integral<Dest_>::value);
200 return static_cast<Dest_>(get_value(first)) + static_cast<Dest_>(get_value(second));
201}
202
203template<typename Dest_, typename First_, typename Second_, typename ... Args_>
204constexpr Dest_ sum_unprotected(First_ first, Second_ second, Args_... more) {
205 const auto subsum = sum_unprotected<Dest_>(first, second);
206 return sum_unprotected<Dest_>(subsum, more...);
207}
228template<typename Dest_, typename First_, typename ... Args_>
229constexpr Dest_ sum_unsafe(First_ first, Args_... more) {
230 return sum_unprotected<Dest_>(first, more...);
231}
232
233}
234
235#endif
Specify compile-time bounds on integers.
Specify a constant integer.
Signedness-safe integer comparisons.
Generics for operating on integer representations.
Sanitize sizes to avoid integer overflow.
Definition Bounded.hpp:15
constexpr bool is_greater_than(Left_ left, Right_ right)
Definition comparisons.hpp:91
constexpr auto get_value(const Value_ x)
Definition generics.hpp:23
constexpr Dest_ sum_unsafe(First_ first, Args_... more)
Definition sum.hpp:229
constexpr auto get_max()
Definition generics.hpp:39
constexpr std::optional< Dest_ > sum_maybe(First_ first, Args_... more)
Definition sum.hpp:125
constexpr bool is_less_than_or_equal(Left_ left, Right_ right)
Definition comparisons.hpp:75
constexpr Dest_ sum(First_ first, Args_... more)
Definition sum.hpp:153
Dest_ plus_one(Value_ x)
Definition sum.hpp:173
Integer_ & increment(Integer_ &x)
Definition sum.hpp:189
Integer constant.
Definition Constant.hpp:24
static constexpr bool value
Definition Constant.hpp:50