sanisizer
Sanitize sizes to avoid integer overflow
Loading...
Searching...
No Matches
product.hpp
Go to the documentation of this file.
1#ifndef SANISIZER_PRODUCT_HPP
2#define SANISIZER_PRODUCT_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_product_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 if constexpr(first_maxed == 0) {
31 // We are obliged to check that all arguments fit into Dest_, but this is only necessary if first_maxed == 0.
32 return is_greater_than(get_max<Second_>(), dest_maxed);
33 } else {
34 constexpr auto ratio = dest_maxed / static_cast<Dest_>(first_maxed);
35 return is_greater_than(get_max<Second_>(), ratio);
36 }
37}
38
39template<typename Dest_, typename First_, typename Second_>
40constexpr bool does_product_overflow_ordered(First_ first, Second_ second) {
41 constexpr Dest_ dest_maxed = get_max<Dest_>();
42 const auto first_val = get_value(first);
43 if (is_greater_than(first_val, dest_maxed)) { // This check can be compiled out if First_ is not greater than Dest_.
44 return true;
45 } else if (first_val == 0) {
46 // We are obliged to check that all arguments fit into Dest_, but this is only necessary if first_val == 0.
47 return is_greater_than(get_value(second), dest_maxed);
48 } else {
49 const auto ratio = dest_maxed / static_cast<Dest_>(first_val); // If First_ is Constant, then the compiler could compute 'ratio' at compile time.
50 return is_greater_than(get_value(second), ratio);
51 }
52}
53
54template<typename Dest_, typename First_, typename Second_>
55constexpr bool does_product_overflow(First_ first, Second_ second) {
56 if constexpr(is_Constant<First_>::value) {
57 // Shift threshold calculation to compile-time.
58 return does_product_overflow_ordered<Dest_>(first, second);
59
60 } else if constexpr(is_Constant<Second_>::value) {
61 // Shift threshold calculation to compile-time.
62 return does_product_overflow_ordered<Dest_>(second, first);
63
64 } else if constexpr(is_less_than_or_equal(get_max<First_>(), get_max<Dest_>())) {
65 // Skip overflow check when First_ is not greater than Dest_.
66 return does_product_overflow_ordered<Dest_>(first, second);
67
68 } else {
69 // Skip overflow check if Second_ is not greater than Dest_.
70 // 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.
71 return does_product_overflow_ordered<Dest_>(second, first);
72 }
73}
74
75template<typename Dest_, typename First_, typename Second_>
76constexpr auto product_protected(First_ first, Second_ second) {
77 static_assert(std::is_integral<Dest_>::value);
78
79 if constexpr(needs_product_check<Dest_, First_, Second_>()) {
80 if (does_product_overflow<Dest_>(first, second)) {
81 return std::optional<Dest_>();
82 } else {
83 // Cast before product to avoid overflow in the input types or whatever integer promotion decides to use.
84 return std::make_optional<Dest_>(static_cast<Dest_>(get_value(first)) * static_cast<Dest_>(get_value(second)));
85 }
86
87 } else {
88 constexpr Dest_ maxprod = static_cast<Dest_>(get_max<First_>()) * static_cast<Dest_>(get_max<Second_>());
90 // Return a Constant in case we can do more compile-time stuff in the next chained operation.
91 // No need to this when 'needs_product_check' is true as it will always fail with two Constants.
92 return std::make_optional(Constant<Dest_, maxprod>());
93 } else {
94 return std::make_optional<Bounded<Dest_, maxprod> >(static_cast<Dest_>(get_value(first)) * static_cast<Dest_>(get_value(second)));
95 }
96 }
97}
98
99template<typename Dest_, typename First_, typename Second_, typename ... Args_>
100constexpr auto product_protected(First_ first, Second_ second, Args_... more) {
101 const auto subproduct = product_protected<Dest_>(first, second);
102 if (subproduct.has_value()) {
103 return product_protected<Dest_>(*subproduct, more...);
104 } else {
105 return I<decltype(product_protected<Dest_>(*subproduct, more...))>();
106 }
107}
133template<typename Dest_, typename First_, typename ... Args_>
134constexpr std::optional<Dest_> product_maybe(First_ first, Args_... more) {
135 const auto res = product_protected<Dest_>(first, more...);
136 if (res.has_value()) {
137 return std::make_optional<Dest_>(get_value(*res));
138 } else {
139 return std::optional<Dest_>();
140 }
141}
142
165template<typename Dest_, typename First_, typename ... Args_>
166constexpr Dest_ product(First_ first, Args_... more) {
167 const auto res = product_maybe<Dest_>(first, more...);
168 if (!res.has_value()) {
169 throw std::overflow_error("detected " + failure<Dest_>() + " during multiplication");
170 }
171 return *res;
172}
173
177template<typename Dest_, typename First_, typename Second_>
178constexpr Dest_ product_unprotected(First_ left, Second_ right) {
179 static_assert(std::is_integral<Dest_>::value);
180 return static_cast<Dest_>(get_value(left)) * static_cast<Dest_>(get_value(right));
181}
182
183template<typename Dest_, typename First_, typename Second_, typename ... Args_>
184constexpr Dest_ product_unprotected(First_ left, Second_ right, Args_... more) {
185 const auto subproduct = product_unprotected<Dest_>(left, right);
186 return product_unprotected<Dest_>(subproduct, more...);
187}
208template<typename Dest_, typename First_, typename ... Args_>
209constexpr Dest_ product_unsafe(First_ first, Args_... more) {
210 return product_unprotected<Dest_>(first, more...);
211}
212
213}
214
215#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 auto get_max()
Definition generics.hpp:39
constexpr std::optional< Dest_ > product_maybe(First_ first, Args_... more)
Definition product.hpp:134
constexpr bool is_less_than_or_equal(Left_ left, Right_ right)
Definition comparisons.hpp:75
constexpr Dest_ product_unsafe(First_ first, Args_... more)
Definition product.hpp:209
constexpr Dest_ product(First_ first, Args_... more)
Definition product.hpp:166
static constexpr bool value
Definition Constant.hpp:50