]>
Commit | Line | Data |
---|---|---|
190b76a6 ET |
1 | /* |
2 | * Copyright (C) the libgit2 contributors. All rights reserved. | |
3 | * | |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | #ifndef INCLUDE_integer_h__ | |
8 | #define INCLUDE_integer_h__ | |
9 | ||
10 | /** @return true if p fits into the range of a size_t */ | |
22a2d3d5 | 11 | GIT_INLINE(int) git__is_sizet(int64_t p) |
190b76a6 ET |
12 | { |
13 | size_t r = (size_t)p; | |
22a2d3d5 | 14 | return p == (int64_t)r; |
190b76a6 ET |
15 | } |
16 | ||
17 | /** @return true if p fits into the range of an ssize_t */ | |
18 | GIT_INLINE(int) git__is_ssizet(size_t p) | |
19 | { | |
20 | ssize_t r = (ssize_t)p; | |
21 | return p == (size_t)r; | |
22 | } | |
23 | ||
22a2d3d5 UG |
24 | /** @return true if p fits into the range of a uint16_t */ |
25 | GIT_INLINE(int) git__is_uint16(size_t p) | |
26 | { | |
27 | uint16_t r = (uint16_t)p; | |
28 | return p == (size_t)r; | |
29 | } | |
30 | ||
190b76a6 ET |
31 | /** @return true if p fits into the range of a uint32_t */ |
32 | GIT_INLINE(int) git__is_uint32(size_t p) | |
33 | { | |
34 | uint32_t r = (uint32_t)p; | |
35 | return p == (size_t)r; | |
36 | } | |
37 | ||
38 | /** @return true if p fits into the range of an unsigned long */ | |
22a2d3d5 | 39 | GIT_INLINE(int) git__is_ulong(int64_t p) |
190b76a6 ET |
40 | { |
41 | unsigned long r = (unsigned long)p; | |
22a2d3d5 | 42 | return p == (int64_t)r; |
190b76a6 ET |
43 | } |
44 | ||
f1453c59 | 45 | /** @return true if p fits into the range of an int */ |
c25aa7cd | 46 | GIT_INLINE(int) git__is_int(int64_t p) |
f1453c59 ET |
47 | { |
48 | int r = (int)p; | |
c25aa7cd | 49 | return p == (int64_t)r; |
f1453c59 ET |
50 | } |
51 | ||
16942c6f | 52 | /* Use clang/gcc compiler intrinsics whenever possible */ |
ac3d33df JK |
53 | #if (__has_builtin(__builtin_add_overflow) || \ |
54 | (defined(__GNUC__) && (__GNUC__ >= 5))) | |
55 | ||
56 | # if (SIZE_MAX == UINT_MAX) | |
57 | # define git__add_sizet_overflow(out, one, two) \ | |
58 | __builtin_uadd_overflow(one, two, out) | |
59 | # define git__multiply_sizet_overflow(out, one, two) \ | |
60 | __builtin_umul_overflow(one, two, out) | |
61 | # elif (SIZE_MAX == ULONG_MAX) | |
62 | # define git__add_sizet_overflow(out, one, two) \ | |
63 | __builtin_uaddl_overflow(one, two, out) | |
64 | # define git__multiply_sizet_overflow(out, one, two) \ | |
65 | __builtin_umull_overflow(one, two, out) | |
66 | # elif (SIZE_MAX == ULLONG_MAX) | |
67 | # define git__add_sizet_overflow(out, one, two) \ | |
68 | __builtin_uaddll_overflow(one, two, out) | |
69 | # define git__multiply_sizet_overflow(out, one, two) \ | |
70 | __builtin_umulll_overflow(one, two, out) | |
71 | # else | |
72 | # error compiler has add with overflow intrinsics but SIZE_MAX is unknown | |
73 | # endif | |
74 | ||
22a2d3d5 UG |
75 | # define git__add_int_overflow(out, one, two) \ |
76 | __builtin_sadd_overflow(one, two, out) | |
77 | # define git__sub_int_overflow(out, one, two) \ | |
78 | __builtin_ssub_overflow(one, two, out) | |
79 | ||
c25aa7cd PP |
80 | # define git__add_int64_overflow(out, one, two) \ |
81 | __builtin_add_overflow(one, two, out) | |
82 | ||
83 | /* clang on 32-bit systems produces an undefined reference to `__mulodi4`. */ | |
84 | # if !defined(__clang__) || !defined(GIT_ARCH_32) | |
85 | # define git__multiply_int64_overflow(out, one, two) \ | |
86 | __builtin_mul_overflow(one, two, out) | |
87 | # endif | |
88 | ||
ac3d33df JK |
89 | /* Use Microsoft's safe integer handling functions where available */ |
90 | #elif defined(_MSC_VER) | |
91 | ||
22a2d3d5 | 92 | # define ENABLE_INTSAFE_SIGNED_FUNCTIONS |
ac3d33df JK |
93 | # include <intsafe.h> |
94 | ||
a0d38479 | 95 | # define git__add_sizet_overflow(out, one, two) \ |
ac3d33df | 96 | (SizeTAdd(one, two, out) != S_OK) |
a0d38479 | 97 | # define git__multiply_sizet_overflow(out, one, two) \ |
ac3d33df | 98 | (SizeTMult(one, two, out) != S_OK) |
c25aa7cd | 99 | |
22a2d3d5 UG |
100 | #define git__add_int_overflow(out, one, two) \ |
101 | (IntAdd(one, two, out) != S_OK) | |
102 | #define git__sub_int_overflow(out, one, two) \ | |
103 | (IntSub(one, two, out) != S_OK) | |
ac3d33df | 104 | |
c25aa7cd PP |
105 | #define git__add_int64_overflow(out, one, two) \ |
106 | (LongLongAdd(one, two, out) != S_OK) | |
107 | #define git__multiply_int64_overflow(out, one, two) \ | |
108 | (LongLongMult(one, two, out) != S_OK) | |
109 | ||
16942c6f ET |
110 | #else |
111 | ||
190b76a6 ET |
112 | /** |
113 | * Sets `one + two` into `out`, unless the arithmetic would overflow. | |
ac3d33df | 114 | * @return false if the result fits in a `size_t`, true on overflow. |
190b76a6 ET |
115 | */ |
116 | GIT_INLINE(bool) git__add_sizet_overflow(size_t *out, size_t one, size_t two) | |
117 | { | |
118 | if (SIZE_MAX - one < two) | |
f1453c59 ET |
119 | return true; |
120 | *out = one + two; | |
121 | return false; | |
190b76a6 ET |
122 | } |
123 | ||
124 | /** | |
125 | * Sets `one * two` into `out`, unless the arithmetic would overflow. | |
ac3d33df | 126 | * @return false if the result fits in a `size_t`, true on overflow. |
190b76a6 ET |
127 | */ |
128 | GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t two) | |
129 | { | |
130 | if (one && SIZE_MAX / one < two) | |
f1453c59 ET |
131 | return true; |
132 | *out = one * two; | |
133 | return false; | |
190b76a6 ET |
134 | } |
135 | ||
22a2d3d5 UG |
136 | GIT_INLINE(bool) git__add_int_overflow(int *out, int one, int two) |
137 | { | |
138 | if ((two > 0 && one > (INT_MAX - two)) || | |
139 | (two < 0 && one < (INT_MIN - two))) | |
140 | return true; | |
141 | *out = one + two; | |
142 | return false; | |
143 | } | |
144 | ||
145 | GIT_INLINE(bool) git__sub_int_overflow(int *out, int one, int two) | |
146 | { | |
147 | if ((two > 0 && one < (INT_MIN + two)) || | |
148 | (two < 0 && one > (INT_MAX + two))) | |
149 | return true; | |
150 | *out = one - two; | |
151 | return false; | |
152 | } | |
153 | ||
c25aa7cd PP |
154 | GIT_INLINE(bool) git__add_int64_overflow(int64_t *out, int64_t one, int64_t two) |
155 | { | |
156 | if ((two > 0 && one > (INT64_MAX - two)) || | |
157 | (two < 0 && one < (INT64_MIN - two))) | |
158 | return true; | |
159 | *out = one + two; | |
160 | return false; | |
161 | } | |
162 | ||
163 | #endif | |
164 | ||
165 | /* If we could not provide an intrinsic implementation for this, provide a (slow) fallback. */ | |
166 | #if !defined(git__multiply_int64_overflow) | |
167 | GIT_INLINE(bool) git__multiply_int64_overflow(int64_t *out, int64_t one, int64_t two) | |
168 | { | |
169 | /* | |
170 | * Detects whether `INT64_MAX < (one * two) || INT64_MIN > (one * two)`, | |
171 | * without incurring in undefined behavior. That is done by performing the | |
172 | * comparison with a division instead of a multiplication, which translates | |
173 | * to `INT64_MAX / one < two || INT64_MIN / one > two`. Some caveats: | |
174 | * | |
175 | * - The comparison sign is inverted when both sides of the inequality are | |
176 | * multiplied/divided by a negative number, so if `one < 0` the comparison | |
177 | * needs to be flipped. | |
178 | * - `INT64_MAX / -1` itself overflows (or traps), so that case should be | |
179 | * avoided. | |
180 | * - Since the overflow flag is defined as the discrepance between the result | |
181 | * of performing the multiplication in a signed integer at twice the width | |
182 | * of the operands, and the truncated+sign-extended version of that same | |
183 | * result, there are four cases where the result is the opposite of what | |
184 | * would be expected: | |
185 | * * `INT64_MIN * -1` / `-1 * INT64_MIN` | |
186 | * * `INT64_MIN * 1 / `1 * INT64_MIN` | |
187 | */ | |
188 | if (one && two) { | |
189 | if (one > 0 && two > 0) { | |
190 | if (INT64_MAX / one < two) | |
191 | return true; | |
192 | } else if (one < 0 && two < 0) { | |
193 | if ((one == -1 && two == INT64_MIN) || | |
194 | (two == -1 && one == INT64_MIN)) { | |
195 | *out = INT64_MIN; | |
196 | return false; | |
197 | } | |
198 | if (INT64_MAX / one > two) | |
199 | return true; | |
200 | } else if (one > 0 && two < 0) { | |
201 | if ((one == 1 && two == INT64_MIN) || | |
202 | (INT64_MIN / one > two)) | |
203 | return true; | |
204 | } else if (one == -1) { | |
205 | if (INT64_MIN / two > one) | |
206 | return true; | |
207 | } else { | |
208 | if ((one == INT64_MIN && two == 1) || | |
209 | (INT64_MIN / one < two)) | |
210 | return true; | |
211 | } | |
212 | } | |
213 | *out = one * two; | |
214 | return false; | |
215 | } | |
16942c6f ET |
216 | #endif |
217 | ||
eae0bfdc | 218 | #endif |