1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef util_CheckedArithmetic_h
8 #define util_CheckedArithmetic_h
9 
10 #include "mozilla/Compiler.h"
11 #include "mozilla/MathAlgorithms.h"
12 
13 #include <stdint.h>
14 
15 // This macro is should be `one' if current compiler supports builtin functions
16 // like __builtin_sadd_overflow.
17 #if MOZ_IS_GCC
18 // GCC supports these functions.
19 #  define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1
20 #else
21 // For CLANG, we use its own function to check for this.
22 #  ifdef __has_builtin
23 #    define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) __has_builtin(x)
24 #  endif
25 #endif
26 #ifndef BUILTIN_CHECKED_ARITHMETIC_SUPPORTED
27 #  define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 0
28 #endif
29 
30 namespace js {
31 
SafeAdd(int32_t one,int32_t two,int32_t * res)32 [[nodiscard]] inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) {
33 #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_sadd_overflow)
34   // Using compiler's builtin function.
35   return !__builtin_sadd_overflow(one, two, res);
36 #else
37   // Use unsigned for the 32-bit operation since signed overflow gets
38   // undefined behavior.
39   *res = uint32_t(one) + uint32_t(two);
40   int64_t ores = (int64_t)one + (int64_t)two;
41   return ores == (int64_t)*res;
42 #endif
43 }
44 
SafeSub(int32_t one,int32_t two,int32_t * res)45 [[nodiscard]] inline bool SafeSub(int32_t one, int32_t two, int32_t* res) {
46 #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_ssub_overflow)
47   return !__builtin_ssub_overflow(one, two, res);
48 #else
49   *res = uint32_t(one) - uint32_t(two);
50   int64_t ores = (int64_t)one - (int64_t)two;
51   return ores == (int64_t)*res;
52 #endif
53 }
54 
SafeMul(int32_t one,int32_t two,int32_t * res)55 [[nodiscard]] inline bool SafeMul(int32_t one, int32_t two, int32_t* res) {
56 #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_smul_overflow)
57   return !__builtin_smul_overflow(one, two, res);
58 #else
59   *res = uint32_t(one) * uint32_t(two);
60   int64_t ores = (int64_t)one * (int64_t)two;
61   return ores == (int64_t)*res;
62 #endif
63 }
64 
SafeMul(uint64_t one,uint64_t two,uint64_t * res)65 [[nodiscard]] inline bool SafeMul(uint64_t one, uint64_t two, uint64_t* res) {
66 #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_mul_overflow)
67   return !__builtin_mul_overflow(one, two, res);
68 #else
69   // Hacker's Delight, 2nd edition, 2-13 Overflow detection, Fig. 2-2.
70   int zeroes =
71       mozilla::CountLeadingZeroes64(one) + mozilla::CountLeadingZeroes64(two);
72   if (zeroes <= 62) {
73     return false;
74   }
75   uint64_t half = one * (two >> 1);
76   if (int64_t(half) < 0) {
77     return false;
78   }
79   *res = half * 2;
80   if (two & 1) {
81     *res += one;
82     if (*res < one) {
83       return false;
84     }
85   }
86   return true;
87 #endif
88 }
89 
90 } /* namespace js */
91 
92 #endif /* util_CheckedArithmetic_h */
93