1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10 #pragma once
11
12 #include <sal/config.h>
13
14 #include <algorithm>
15 #include <cassert>
16 #include <limits>
17 #include <type_traits>
18
19 #if defined(_MSC_VER)
20 #include <safeint.h>
21 #else
22 #ifndef __has_builtin
23 # define __has_builtin(x) 0
24 #endif
25 #endif
26
27 namespace o3tl
28 {
29
30 template<typename T> inline
saturating_add(T a,T b)31 typename std::enable_if<std::is_signed<T>::value, T>::type saturating_add(
32 T a, T b)
33 {
34 if (b >= 0) {
35 if (a <= std::numeric_limits<T>::max() - b) {
36 return a + b;
37 } else {
38 return std::numeric_limits<T>::max();
39 }
40 } else {
41 if (a >= std::numeric_limits<T>::min() - b) {
42 return a + b;
43 } else {
44 return std::numeric_limits<T>::min();
45 }
46 }
47 }
48
49 template<typename T> inline
saturating_add(T a,T b)50 typename std::enable_if<std::is_unsigned<T>::value, T>::type saturating_add(
51 T a, T b)
52 {
53 if (a <= std::numeric_limits<T>::max() - b) {
54 return a + b;
55 } else {
56 return std::numeric_limits<T>::max();
57 }
58 }
59
60 template<typename T> inline
saturating_sub(T a,T b)61 typename std::enable_if<std::is_signed<T>::value, T>::type saturating_sub(
62 T a, T b)
63 {
64 if (b >= 0) {
65 if (a >= std::numeric_limits<T>::min() + b) {
66 return a - b;
67 } else {
68 return std::numeric_limits<T>::min();
69 }
70 } else {
71 if (a <= std::numeric_limits<T>::max() + b) {
72 return a - b;
73 } else {
74 return std::numeric_limits<T>::max();
75 }
76 }
77 }
78
79 template<typename T> inline
saturating_sub(T a,T b)80 typename std::enable_if<std::is_unsigned<T>::value, T>::type saturating_sub(
81 T a, T b)
82 {
83 if (a >= std::numeric_limits<T>::min() + b) {
84 return a - b;
85 } else {
86 return std::numeric_limits<T>::min();
87 }
88 }
89
90 template<typename T> inline
saturating_toggle_sign(T a)91 typename std::enable_if<std::is_signed<T>::value, T>::type saturating_toggle_sign(
92 T a)
93 {
94 if (a == std::numeric_limits<T>::min())
95 return std::numeric_limits<T>::max();
96 return a * -1;
97 }
98
99 #if defined(_MSC_VER)
100
checked_multiply(T a,T b,T & result)101 template<typename T> inline bool checked_multiply(T a, T b, T& result)
102 {
103 return !msl::utilities::SafeMultiply(a, b, result);
104 }
105
checked_add(T a,T b,T & result)106 template<typename T> inline bool checked_add(T a, T b, T& result)
107 {
108 return !msl::utilities::SafeAdd(a, b, result);
109 }
110
checked_sub(T a,T b,T & result)111 template<typename T> inline bool checked_sub(T a, T b, T& result)
112 {
113 return !msl::utilities::SafeSubtract(a, b, result);
114 }
115
116 #elif (defined __GNUC__ && !defined __clang__) || (__has_builtin(__builtin_mul_overflow) && !(defined ANDROID && defined __clang__) && !(defined(__clang__) && (defined(__arm__) || defined(__i386__))))
117 // 32-bit clang fails with undefined reference to `__mulodi4'
118
checked_multiply(T a,T b,T & result)119 template<typename T> inline bool checked_multiply(T a, T b, T& result)
120 {
121 return __builtin_mul_overflow(a, b, &result);
122 }
123
checked_add(T a,T b,T & result)124 template<typename T> inline bool checked_add(T a, T b, T& result)
125 {
126 return __builtin_add_overflow(a, b, &result);
127 }
128
checked_sub(T a,T b,T & result)129 template<typename T> inline bool checked_sub(T a, T b, T& result)
130 {
131 return __builtin_sub_overflow(a, b, &result);
132 }
133
134 #else
135
136 //https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
checked_multiply(T a,T b,T & result)137 template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_multiply(T a, T b, T& result)
138 {
139 if (a > 0) { /* a is positive */
140 if (b > 0) { /* a and b are positive */
141 if (a > (std::numeric_limits<T>::max() / b)) {
142 return true; /* Handle error */
143 }
144 } else { /* a positive, b nonpositive */
145 if (b < (std::numeric_limits<T>::min() / a)) {
146 return true; /* Handle error */
147 }
148 } /* a positive, b nonpositive */
149 } else { /* a is nonpositive */
150 if (b > 0) { /* a is nonpositive, b is positive */
151 if (a < (std::numeric_limits<T>::min() / b)) {
152 return true; /* Handle error */
153 }
154 } else { /* a and b are nonpositive */
155 if ( (a != 0) && (b < (std::numeric_limits<T>::max() / a))) {
156 return true; /* Handle error */
157 }
158 } /* End if a and b are nonpositive */
159 } /* End if a is nonpositive */
160
161 result = a * b;
162
163 return false;
164 }
165
166 //https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
checked_multiply(T a,T b,T & result)167 template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_multiply(T a, T b, T& result)
168 {
169 if (b && a > std::numeric_limits<T>::max() / b) {
170 return true;/* Handle error */
171 }
172
173 result = a * b;
174
175 return false;
176 }
177
178 //https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
checked_add(T a,T b,T & result)179 template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_add(T a, T b, T& result)
180 {
181 if (((b > 0) && (a > (std::numeric_limits<T>::max() - b))) ||
182 ((b < 0) && (a < (std::numeric_limits<T>::min() - b)))) {
183 return true;
184 }
185
186 result = a + b;
187
188 return false;
189 }
190
191 //https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
checked_add(T a,T b,T & result)192 template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_add(T a, T b, T& result)
193 {
194 if (std::numeric_limits<T>::max() - a < b) {
195 return true;/* Handle error */
196 }
197
198 result = a + b;
199
200 return false;
201 }
202
203 //https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
checked_sub(T a,T b,T & result)204 template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_sub(T a, T b, T& result)
205 {
206 if ((b > 0 && a < std::numeric_limits<T>::min() + b) ||
207 (b < 0 && a > std::numeric_limits<T>::max() + b)) {
208 return true;
209 }
210
211 result = a - b;
212
213 return false;
214 }
215
216 //https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
checked_sub(T a,T b,T & result)217 template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_sub(T a, T b, T& result)
218 {
219 if (a < b) {
220 return true;
221 }
222
223 result = a - b;
224
225 return false;
226 }
227
228 #endif
229
230 template<typename T> constexpr std::enable_if_t<std::is_signed_v<T>, std::make_unsigned_t<T>>
make_unsigned(T value)231 make_unsigned(T value)
232 {
233 assert(value >= 0);
234 return value;
235 }
236
237 // An implicit conversion from T2 to T1, useful in places where an explicit conversion from T2 to
238 // T1 is needed (e.g., in list initialization, if the implicit conversion would be narrowing) but
239 // tools like -fsanitize=implicit-conversion should still be able to detect truncation:
narrowing(T2 value)240 template<typename T1, typename T2> constexpr T1 narrowing(T2 value) { return value; }
241
242 // std::min wrapped to inform coverity that the result is now deemed sanitized
243 // coverity[ -taint_source ]
sanitizing_min(T a,T b)244 template<typename T> [[nodiscard]] inline T sanitizing_min(T a, T b)
245 {
246 return std::min(a, b);
247 }
248
249 }
250
251 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
252