1 | //======================================================================== |
2 | // |
3 | // GooCheckedOps.h |
4 | // |
5 | // This file is licensed under the GPLv2 or later |
6 | // |
7 | // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
8 | // Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com> |
9 | // Copyright (C) 2019-2021 Albert Astals Cid <aacid@kde.org> |
10 | // |
11 | //======================================================================== |
12 | |
13 | #ifndef GOO_CHECKED_OPS_H |
14 | #define GOO_CHECKED_OPS_H |
15 | |
16 | #include <limits> |
17 | #include <type_traits> |
18 | |
19 | template<typename T> |
20 | inline bool checkedAssign(long long lz, T *z) |
21 | { |
22 | static_assert((std::numeric_limits<long long>::max)() > (std::numeric_limits<T>::max)(), "The max of long long type must be larger to perform overflow checks." ); |
23 | static_assert((std::numeric_limits<long long>::min)() < (std::numeric_limits<T>::min)(), "The min of long long type must be smaller to perform overflow checks." ); |
24 | |
25 | if (lz > (std::numeric_limits<T>::max)() || lz < (std::numeric_limits<T>::min)()) { |
26 | return true; |
27 | } |
28 | |
29 | *z = static_cast<T>(lz); |
30 | return false; |
31 | } |
32 | |
33 | #ifndef __has_builtin |
34 | # define __has_builtin(x) 0 |
35 | #endif |
36 | |
37 | template<typename T> |
38 | inline bool checkedAdd(T x, T y, T *z) |
39 | { |
40 | // The __GNUC__ checks can not be removed until we depend on GCC >= 10.1 |
41 | // which is the first version that returns true for __has_builtin(__builtin_add_overflow) |
42 | #if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) |
43 | return __builtin_add_overflow(x, y, z); |
44 | #else |
45 | const auto lz = static_cast<long long>(x) + static_cast<long long>(y); |
46 | return checkedAssign(lz, z); |
47 | #endif |
48 | } |
49 | |
50 | template<> |
51 | inline bool checkedAdd<long long>(long long x, long long y, long long *z) |
52 | { |
53 | #if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) |
54 | return __builtin_add_overflow(x, y, z); |
55 | #else |
56 | if (x > 0 && y > 0) { |
57 | if (x > (std::numeric_limits<long long>::max)() - y) { |
58 | return true; |
59 | } |
60 | } else if (x < 0 && y < 0) { |
61 | if (x < (std::numeric_limits<long long>::min)() - y) { |
62 | return true; |
63 | } |
64 | } |
65 | *z = x + y; |
66 | return false; |
67 | #endif |
68 | } |
69 | |
70 | template<typename T> |
71 | inline bool checkedSubtraction(T x, T y, T *z) |
72 | { |
73 | #if __GNUC__ >= 5 || __has_builtin(__builtin_sub_overflow) |
74 | return __builtin_sub_overflow(x, y, z); |
75 | #else |
76 | const auto lz = static_cast<long long>(x) - static_cast<long long>(y); |
77 | return checkedAssign(lz, z); |
78 | #endif |
79 | } |
80 | |
81 | template<typename T> |
82 | inline bool checkedMultiply(T x, T y, T *z) |
83 | { |
84 | #if __GNUC__ >= 5 || __has_builtin(__builtin_mul_overflow) |
85 | return __builtin_mul_overflow(x, y, z); |
86 | #else |
87 | const auto lz = static_cast<long long>(x) * static_cast<long long>(y); |
88 | return checkedAssign(lz, z); |
89 | #endif |
90 | } |
91 | |
92 | template<> |
93 | inline bool checkedMultiply<long long>(long long x, long long y, long long *z) |
94 | { |
95 | #if __GNUC__ >= 5 || __has_builtin(__builtin_mul_overflow) |
96 | return __builtin_mul_overflow(x, y, z); |
97 | #else |
98 | if (x != 0 && (std::numeric_limits<long long>::max)() / x < y) { |
99 | return true; |
100 | } |
101 | |
102 | *z = x * y; |
103 | return false; |
104 | #endif |
105 | } |
106 | |
107 | template<typename T> |
108 | inline T safeAverage(T a, T b) |
109 | { |
110 | static_assert((std::numeric_limits<long long>::max)() > (std::numeric_limits<T>::max)(), "The max of long long type must be larger to perform overflow checks." ); |
111 | static_assert((std::numeric_limits<long long>::min)() < (std::numeric_limits<T>::min)(), "The min of long long type must be smaller to perform overflow checks." ); |
112 | |
113 | return static_cast<T>((static_cast<long long>(a) + static_cast<long long>(b)) / 2); |
114 | } |
115 | |
116 | #endif // GOO_CHECKED_OPS_H |
117 | |