Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1998)

Side by Side Diff: src/base/safe_math_impl.h

Issue 336183003: Add safe numerics classes, imported from Chromium (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: changes required for V8 Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Slightly adapted for inclusion in V8.
6 // Copyright 2014 the V8 project authors. All rights reserved.
7
8 #ifndef BASE_SAFE_MATH_IMPL_H_
9 #define BASE_SAFE_MATH_IMPL_H_
10
11 #include <stdint.h>
12
13 #include <cmath>
14 #include <cstdlib>
15 #include <limits>
16
17 #include "src/base/macros.h"
18 #include "src/base/safe_conversions.h"
19
20 namespace v8 {
21 namespace internal {
22
23
24 // From Chromium's base/template_util.h:
25
26 template<class T, T v>
27 struct integral_constant {
28 static const T value = v;
29 typedef T value_type;
30 typedef integral_constant<T, v> type;
31 };
32
33 template <class T, T v> const T integral_constant<T, v>::value;
34
35 typedef integral_constant<bool, true> true_type;
36 typedef integral_constant<bool, false> false_type;
37
38 template <class T, class U> struct is_same : public false_type {};
39 template <class T> struct is_same<T, T> : true_type {};
40
41 template<bool B, class T = void>
42 struct enable_if {};
43
44 template<class T>
45 struct enable_if<true, T> { typedef T type; };
46
47 // </template_util.h>
48
49
50 // Everything from here up to the floating point operations is portable C++,
51 // but it may not be fast. This code could be split based on
52 // platform/architecture and replaced with potentially faster implementations.
53
54 // Integer promotion templates used by the portable checked integer arithmetic.
55 template <size_t Size, bool IsSigned>
56 struct IntegerForSizeAndSign;
57 template <>
58 struct IntegerForSizeAndSign<1, true> {
59 typedef int8_t type;
60 };
61 template <>
62 struct IntegerForSizeAndSign<1, false> {
63 typedef uint8_t type;
64 };
65 template <>
66 struct IntegerForSizeAndSign<2, true> {
67 typedef int16_t type;
68 };
69 template <>
70 struct IntegerForSizeAndSign<2, false> {
71 typedef uint16_t type;
72 };
73 template <>
74 struct IntegerForSizeAndSign<4, true> {
75 typedef int32_t type;
76 };
77 template <>
78 struct IntegerForSizeAndSign<4, false> {
79 typedef uint32_t type;
80 };
81 template <>
82 struct IntegerForSizeAndSign<8, true> {
83 typedef int64_t type;
84 };
85 template <>
86 struct IntegerForSizeAndSign<8, false> {
87 typedef uint64_t type;
88 };
89
90 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
91 // support 128-bit math, then the ArithmeticPromotion template below will need
92 // to be updated (or more likely replaced with a decltype expression).
93
94 template <typename Integer>
95 struct UnsignedIntegerForSize {
96 typedef typename enable_if<
97 std::numeric_limits<Integer>::is_integer,
98 typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
99 };
100
101 template <typename Integer>
102 struct SignedIntegerForSize {
103 typedef typename enable_if<
104 std::numeric_limits<Integer>::is_integer,
105 typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
106 };
107
108 template <typename Integer>
109 struct TwiceWiderInteger {
110 typedef typename enable_if<
111 std::numeric_limits<Integer>::is_integer,
112 typename IntegerForSizeAndSign<
113 sizeof(Integer) * 2,
114 std::numeric_limits<Integer>::is_signed>::type>::type type;
115 };
116
117 template <typename Integer>
118 struct PositionOfSignBit {
119 static const typename enable_if<std::numeric_limits<Integer>::is_integer,
120 size_t>::type value = 8 * sizeof(Integer) - 1;
121 };
122
123 // Helper templates for integer manipulations.
124
125 template <typename T>
126 bool HasSignBit(T x) {
127 // Cast to unsigned since right shift on signed is undefined.
128 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
129 PositionOfSignBit<T>::value);
130 }
131
132 // This wrapper undoes the standard integer promotions.
133 template <typename T>
134 T BinaryComplement(T x) {
135 return ~x;
136 }
137
138 // Here are the actual portable checked integer math implementations.
139 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
140 // way to coalesce things into the CheckedNumericState specializations below.
141
142 template <typename T>
143 typename enable_if<std::numeric_limits<T>::is_integer, T>::type
144 CheckedAdd(T x, T y, RangeConstraint* validity) {
145 // Since the value of x+y is undefined if we have a signed type, we compute
146 // it using the unsigned type of the same size.
147 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
148 UnsignedDst ux = static_cast<UnsignedDst>(x);
149 UnsignedDst uy = static_cast<UnsignedDst>(y);
150 UnsignedDst uresult = ux + uy;
151 // Addition is valid if the sign of (x + y) is equal to either that of x or
152 // that of y.
153 if (std::numeric_limits<T>::is_signed) {
154 if (HasSignBit(BinaryComplement((uresult ^ ux) & (uresult ^ uy))))
155 *validity = RANGE_VALID;
156 else // Direction of wrap is inverse of result sign.
157 *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
158
159 } else { // Unsigned is either valid or overflow.
160 *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
161 }
162 return static_cast<T>(uresult);
163 }
164
165 template <typename T>
166 typename enable_if<std::numeric_limits<T>::is_integer, T>::type
167 CheckedSub(T x, T y, RangeConstraint* validity) {
168 // Since the value of x+y is undefined if we have a signed type, we compute
169 // it using the unsigned type of the same size.
170 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
171 UnsignedDst ux = static_cast<UnsignedDst>(x);
172 UnsignedDst uy = static_cast<UnsignedDst>(y);
173 UnsignedDst uresult = ux - uy;
174 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
175 // the same sign.
176 if (std::numeric_limits<T>::is_signed) {
177 if (HasSignBit(BinaryComplement((uresult ^ ux) & (ux ^ uy))))
178 *validity = RANGE_VALID;
179 else // Direction of wrap is inverse of result sign.
180 *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
181
182 } else { // Unsigned is either valid or underflow.
183 *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
184 }
185 return static_cast<T>(uresult);
186 }
187
188 // Integer multiplication is a bit complicated. In the fast case we just
189 // we just promote to a twice wider type, and range check the result. In the
190 // slow case we need to manually check that the result won't be truncated by
191 // checking with division against the appropriate bound.
192 template <typename T>
193 typename enable_if<
194 std::numeric_limits<T>::is_integer && sizeof(T) * 2 <= sizeof(uintmax_t),
195 T>::type
196 CheckedMul(T x, T y, RangeConstraint* validity) {
197 typedef typename TwiceWiderInteger<T>::type IntermediateType;
198 IntermediateType tmp =
199 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
200 *validity = DstRangeRelationToSrcRange<T>(tmp);
201 return static_cast<T>(tmp);
202 }
203
204 template <typename T>
205 typename enable_if<std::numeric_limits<T>::is_integer &&
206 std::numeric_limits<T>::is_signed &&
207 (sizeof(T) * 2 > sizeof(uintmax_t)),
208 T>::type
209 CheckedMul(T x, T y, RangeConstraint* validity) {
210 // if either side is zero then the result will be zero.
211 if (!(x || y)) {
212 return RANGE_VALID;
213
214 } else if (x > 0) {
215 if (y > 0)
216 *validity =
217 x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
218 else
219 *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID
220 : RANGE_UNDERFLOW;
221
222 } else {
223 if (y > 0)
224 *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID
225 : RANGE_UNDERFLOW;
226 else
227 *validity =
228 y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
229 }
230
231 return x * y;
232 }
233
234 template <typename T>
235 typename enable_if<std::numeric_limits<T>::is_integer &&
236 !std::numeric_limits<T>::is_signed &&
237 (sizeof(T) * 2 > sizeof(uintmax_t)),
238 T>::type
239 CheckedMul(T x, T y, RangeConstraint* validity) {
240 *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y)
241 ? RANGE_VALID
242 : RANGE_OVERFLOW;
243 return x * y;
244 }
245
246 // Division just requires a check for an invalid negation on signed min/-1.
247 template <typename T>
248 T CheckedDiv(
249 T x,
250 T y,
251 RangeConstraint* validity,
252 typename enable_if<std::numeric_limits<T>::is_integer, int>::type = 0) {
253 if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
254 y == static_cast<T>(-1)) {
255 *validity = RANGE_OVERFLOW;
256 return std::numeric_limits<T>::min();
257 }
258
259 *validity = RANGE_VALID;
260 return x / y;
261 }
262
263 template <typename T>
264 typename enable_if<
265 std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
266 T>::type
267 CheckedMod(T x, T y, RangeConstraint* validity) {
268 *validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
269 return x % y;
270 }
271
272 template <typename T>
273 typename enable_if<
274 std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
275 T>::type
276 CheckedMod(T x, T y, RangeConstraint* validity) {
277 *validity = RANGE_VALID;
278 return x % y;
279 }
280
281 template <typename T>
282 typename enable_if<
283 std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
284 T>::type
285 CheckedNeg(T value, RangeConstraint* validity) {
286 *validity =
287 value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
288 // The negation of signed min is min, so catch that one.
289 return -value;
290 }
291
292 template <typename T>
293 typename enable_if<
294 std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
295 T>::type
296 CheckedNeg(T value, RangeConstraint* validity) {
297 // The only legal unsigned negation is zero.
298 *validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
299 return static_cast<T>(
300 -static_cast<typename SignedIntegerForSize<T>::type>(value));
301 }
302
303 template <typename T>
304 typename enable_if<
305 std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
306 T>::type
307 CheckedAbs(T value, RangeConstraint* validity) {
308 *validity =
309 value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
310 return std::abs(value);
311 }
312
313 template <typename T>
314 typename enable_if<
315 std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
316 T>::type
317 CheckedAbs(T value, RangeConstraint* validity) {
318 // Absolute value of a positive is just its identiy.
319 *validity = RANGE_VALID;
320 return value;
321 }
322
323 // These are the floating point stubs that the compiler needs to see. Only the
324 // negation operation is ever called.
325 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
326 template <typename T> \
327 typename enable_if<std::numeric_limits<T>::is_iec559, T>::type \
328 Checked##NAME(T, T, RangeConstraint*) { \
329 UNREACHABLE(); \
330 return 0; \
331 }
332
333 BASE_FLOAT_ARITHMETIC_STUBS(Add)
334 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
335 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
336 BASE_FLOAT_ARITHMETIC_STUBS(Div)
337 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
338
339 #undef BASE_FLOAT_ARITHMETIC_STUBS
340
341 template <typename T>
342 typename enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
343 T value,
344 RangeConstraint*) {
345 return -value;
346 }
347
348 template <typename T>
349 typename enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
350 T value,
351 RangeConstraint*) {
352 return std::abs(value);
353 }
354
355 // Floats carry around their validity state with them, but integers do not. So,
356 // we wrap the underlying value in a specialization in order to hide that detail
357 // and expose an interface via accessors.
358 enum NumericRepresentation {
359 NUMERIC_INTEGER,
360 NUMERIC_FLOATING,
361 NUMERIC_UNKNOWN
362 };
363
364 template <typename NumericType>
365 struct GetNumericRepresentation {
366 static const NumericRepresentation value =
367 std::numeric_limits<NumericType>::is_integer
368 ? NUMERIC_INTEGER
369 : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING
370 : NUMERIC_UNKNOWN);
371 };
372
373 template <typename T, NumericRepresentation type =
374 GetNumericRepresentation<T>::value>
375 class CheckedNumericState {};
376
377 // Integrals require quite a bit of additional housekeeping to manage state.
378 template <typename T>
379 class CheckedNumericState<T, NUMERIC_INTEGER> {
380 private:
381 T value_;
382 RangeConstraint validity_;
383
384 public:
385 template <typename Src, NumericRepresentation type>
386 friend class CheckedNumericState;
387
388 CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
389
390 template <typename Src>
391 CheckedNumericState(Src value, RangeConstraint validity)
392 : value_(value),
393 validity_(GetRangeConstraint(validity |
394 DstRangeRelationToSrcRange<T>(value))) {
395 // Argument must be numeric.
396 STATIC_ASSERT(std::numeric_limits<Src>::is_specialized);
397 }
398
399 // Copy constructor.
400 template <typename Src>
401 CheckedNumericState(const CheckedNumericState<Src>& rhs)
402 : value_(static_cast<T>(rhs.value())),
403 validity_(GetRangeConstraint(
404 rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {}
405
406 template <typename Src>
407 explicit CheckedNumericState(
408 Src value,
409 typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type =
410 0)
411 : value_(static_cast<T>(value)),
412 validity_(DstRangeRelationToSrcRange<T>(value)) {}
413
414 RangeConstraint validity() const { return validity_; }
415 T value() const { return value_; }
416 };
417
418 // Floating points maintain their own validity, but need translation wrappers.
419 template <typename T>
420 class CheckedNumericState<T, NUMERIC_FLOATING> {
421 private:
422 T value_;
423
424 public:
425 template <typename Src, NumericRepresentation type>
426 friend class CheckedNumericState;
427
428 CheckedNumericState() : value_(0.0) {}
429
430 template <typename Src>
431 CheckedNumericState(
432 Src value,
433 RangeConstraint validity,
434 typename enable_if<std::numeric_limits<Src>::is_integer, int>::type = 0) {
435 switch (DstRangeRelationToSrcRange<T>(value)) {
436 case RANGE_VALID:
437 value_ = static_cast<T>(value);
438 break;
439
440 case RANGE_UNDERFLOW:
441 value_ = -std::numeric_limits<T>::infinity();
442 break;
443
444 case RANGE_OVERFLOW:
445 value_ = std::numeric_limits<T>::infinity();
446 break;
447
448 case RANGE_INVALID:
449 value_ = std::numeric_limits<T>::quiet_NaN();
450 break;
451 }
452 }
453
454 template <typename Src>
455 explicit CheckedNumericState(
456 Src value,
457 typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type =
458 0)
459 : value_(static_cast<T>(value)) {}
460
461 // Copy constructor.
462 template <typename Src>
463 CheckedNumericState(const CheckedNumericState<Src>& rhs)
464 : value_(static_cast<T>(rhs.value())) {}
465
466 RangeConstraint validity() const {
467 return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
468 value_ >= -std::numeric_limits<T>::max());
469 }
470 T value() const { return value_; }
471 };
472
473 // For integers less than 128-bit and floats 32-bit or larger, we can distil
474 // C/C++ arithmetic promotions down to two simple rules:
475 // 1. The type with the larger maximum exponent always takes precedence.
476 // 2. The resulting type must be promoted to at least an int.
477 // The following template specializations implement that promotion logic.
478 enum ArithmeticPromotionCategory {
479 LEFT_PROMOTION,
480 RIGHT_PROMOTION,
481 DEFAULT_PROMOTION
482 };
483
484 template <typename Lhs,
485 typename Rhs = Lhs,
486 ArithmeticPromotionCategory Promotion =
487 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
488 ? (MaxExponent<Lhs>::value > MaxExponent<int>::value
489 ? LEFT_PROMOTION
490 : DEFAULT_PROMOTION)
491 : (MaxExponent<Rhs>::value > MaxExponent<int>::value
492 ? RIGHT_PROMOTION
493 : DEFAULT_PROMOTION) >
494 struct ArithmeticPromotion;
495
496 template <typename Lhs, typename Rhs>
497 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
498 typedef Lhs type;
499 };
500
501 template <typename Lhs, typename Rhs>
502 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
503 typedef Rhs type;
504 };
505
506 template <typename Lhs, typename Rhs>
507 struct ArithmeticPromotion<Lhs, Rhs, DEFAULT_PROMOTION> {
508 typedef int type;
509 };
510
511 // We can statically check if operations on the provided types can wrap, so we
512 // can skip the checked operations if they're not needed. So, for an integer we
513 // care if the destination type preserves the sign and is twice the width of
514 // the source.
515 template <typename T, typename Lhs, typename Rhs>
516 struct IsIntegerArithmeticSafe {
517 static const bool value = !std::numeric_limits<T>::is_iec559 &&
518 StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
519 NUMERIC_RANGE_CONTAINED &&
520 sizeof(T) >= (2 * sizeof(Lhs)) &&
521 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
522 NUMERIC_RANGE_CONTAINED &&
523 sizeof(T) >= (2 * sizeof(Rhs));
524 };
525
526 } // namespace internal
527 } // namespace v8
528
529 #endif // BASE_SAFE_MATH_IMPL_H_
OLDNEW
« src/base/safe_conversions_impl.h ('K') | « src/base/safe_math.h ('k') | tools/gyp/v8.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698