|
25 | 25 | #include "arrow/util/macros.h" |
26 | 26 | #include "arrow/util/visibility.h" |
27 | 27 |
|
28 | | -// "safe-math.h" includes <intsafe.h> from the Windows headers. |
29 | | -#include "arrow/vendored/portable-snippets/safe-math.h" |
30 | | -// clang-format off (avoid include reordering) |
31 | | -// clang-format on |
| 28 | +#include "arrow/vendored/safeint/safe_math.h" |
32 | 29 |
|
33 | 30 | namespace arrow { |
34 | 31 | namespace internal { |
35 | 32 |
|
| 33 | +// static inline bool check_add_int32_int32(int32_t a, int32_t b, int32_t* ret) |
| 34 | + |
36 | 35 | // Define functions AddWithOverflow, SubtractWithOverflow, MultiplyWithOverflow |
37 | 36 | // with the signature `bool(T u, T v, T* out)` where T is an integer type. |
38 | 37 | // On overflow, these functions return true. Otherwise, false is returned |
39 | 38 | // and `out` is updated with the result of the operation. |
40 | 39 |
|
41 | | -#define OP_WITH_OVERFLOW(_func_name, _psnip_op, _type, _psnip_type) \ |
42 | | - [[nodiscard]] static inline bool _func_name(_type u, _type v, _type* out) { \ |
43 | | - return !psnip_safe_##_psnip_type##_##_psnip_op(out, u, v); \ |
| 40 | +#define SAFE_INT_OP_WITH_OVERFLOW(_func_name, _op_name, _c_type, _type) \ |
| 41 | + [[nodiscard]] static inline bool _func_name(_c_type u, _c_type v, _c_type* out) { \ |
| 42 | + return !check_##_op_name##_##_type##_##_type(u, v, out); \ |
44 | 43 | } |
45 | 44 |
|
46 | | -#define OPS_WITH_OVERFLOW(_func_name, _psnip_op) \ |
47 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, int8_t, int8) \ |
48 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, int16_t, int16) \ |
49 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, int32_t, int32) \ |
50 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, int64_t, int64) \ |
51 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, uint8_t, uint8) \ |
52 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, uint16_t, uint16) \ |
53 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, uint32_t, uint32) \ |
54 | | - OP_WITH_OVERFLOW(_func_name, _psnip_op, uint64_t, uint64) |
| 45 | +#define SAFE_INT_OPS_WITH_OVERFLOW(_func_name, _psnip_op) \ |
| 46 | + SAFE_INT_OP_WITH_OVERFLOW(_func_name, _psnip_op, int32_t, int32) \ |
| 47 | + SAFE_INT_OP_WITH_OVERFLOW(_func_name, _psnip_op, int64_t, int64) \ |
| 48 | + SAFE_INT_OP_WITH_OVERFLOW(_func_name, _psnip_op, uint32_t, uint32) \ |
| 49 | + SAFE_INT_OP_WITH_OVERFLOW(_func_name, _psnip_op, uint64_t, uint64) |
| 50 | + |
| 51 | +SAFE_INT_OPS_WITH_OVERFLOW(SafeIntAddWithOverflow, add) |
| 52 | +SAFE_INT_OPS_WITH_OVERFLOW(SafeIntSubtractWithOverflow, sub) |
| 53 | +SAFE_INT_OPS_WITH_OVERFLOW(SafeIntMultiplyWithOverflow, mul) |
| 54 | +SAFE_INT_OPS_WITH_OVERFLOW(SafeIntDivideWithOverflow, div) |
| 55 | + |
| 56 | +#undef SAFE_INT_OP_WITH_OVERFLOW |
| 57 | +#undef SAFE_INT_OPS_WITH_OVERFLOW |
| 58 | + |
| 59 | +template <typename Int, typename SignedRet, typename UnsignedRet> |
| 60 | +using transformed_int_t = |
| 61 | + std::conditional_t<std::is_signed_v<Int>, SignedRet, UnsignedRet>; |
| 62 | + |
| 63 | +template <typename Int> |
| 64 | +using upscaled_int32_t = transformed_int_t<Int, int32_t, uint32_t>; |
| 65 | + |
| 66 | +// TODO use builtins on clang/gcc |
| 67 | + |
| 68 | +template <typename Int> |
| 69 | +[[nodiscard]] bool AddWithOverflowGeneric(Int u, Int v, Int* out) { |
| 70 | + if constexpr (sizeof(Int) < 4) { |
| 71 | + auto r = |
| 72 | + static_cast<upscaled_int32_t<Int>>(u) + static_cast<upscaled_int32_t<Int>>(v); |
| 73 | + *out = static_cast<Int>(r); |
| 74 | + return r != *out; |
| 75 | + } else { |
| 76 | + return SafeIntAddWithOverflow(u, v, out); |
| 77 | + } |
| 78 | +} |
55 | 79 |
|
56 | | -OPS_WITH_OVERFLOW(AddWithOverflow, add) |
57 | | -OPS_WITH_OVERFLOW(SubtractWithOverflow, sub) |
58 | | -OPS_WITH_OVERFLOW(MultiplyWithOverflow, mul) |
59 | | -OPS_WITH_OVERFLOW(DivideWithOverflow, div) |
| 80 | +template <typename Int> |
| 81 | +[[nodiscard]] bool SubtractWithOverflowGeneric(Int u, Int v, Int* out) { |
| 82 | + if constexpr (sizeof(Int) < 4) { |
| 83 | + auto r = |
| 84 | + static_cast<upscaled_int32_t<Int>>(u) - static_cast<upscaled_int32_t<Int>>(v); |
| 85 | + *out = static_cast<Int>(r); |
| 86 | + return r != *out; |
| 87 | + } else { |
| 88 | + return SafeIntSubtractWithOverflow(u, v, out); |
| 89 | + } |
| 90 | +} |
60 | 91 |
|
61 | | -#undef OP_WITH_OVERFLOW |
62 | | -#undef OPS_WITH_OVERFLOW |
| 92 | +template <typename Int> |
| 93 | +[[nodiscard]] bool MultiplyWithOverflowGeneric(Int u, Int v, Int* out) { |
| 94 | + if constexpr (sizeof(Int) < 4) { |
| 95 | + auto r = |
| 96 | + static_cast<upscaled_int32_t<Int>>(u) * static_cast<upscaled_int32_t<Int>>(v); |
| 97 | + *out = static_cast<Int>(r); |
| 98 | + return r != *out; |
| 99 | + } else { |
| 100 | + return SafeIntMultiplyWithOverflow(u, v, out); |
| 101 | + } |
| 102 | +} |
63 | 103 |
|
64 | | -// Define function NegateWithOverflow with the signature `bool(T u, T* out)` |
65 | | -// where T is a signed integer type. On overflow, these functions return true. |
66 | | -// Otherwise, false is returned and `out` is updated with the result of the |
67 | | -// operation. |
| 104 | +template <typename Int> |
| 105 | +[[nodiscard]] bool DivideWithOverflowGeneric(Int u, Int v, Int* out) { |
| 106 | + if constexpr (sizeof(Int) < 4) { |
| 107 | + using UpscaledInt = upscaled_int32_t<Int>; |
| 108 | + UpscaledInt r; |
| 109 | + bool error = SafeIntDivideWithOverflow(UpscaledInt{u}, UpscaledInt{v}, &r); |
| 110 | + *out = static_cast<Int>(r); |
| 111 | + return error || r != *out; |
| 112 | + } else { |
| 113 | + return SafeIntDivideWithOverflow(u, v, out); |
| 114 | + } |
| 115 | +} |
68 | 116 |
|
69 | | -#define UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, _type, _psnip_type) \ |
70 | | - [[nodiscard]] static inline bool _func_name(_type u, _type* out) { \ |
71 | | - return !psnip_safe_##_psnip_type##_##_psnip_op(out, u); \ |
| 117 | +// Define non-generic versions of the above so as to benefit from automatic |
| 118 | +// integer conversion, to allow for mixed-type calls such as |
| 119 | +// AddWithOverflow(int32_t, int64_t, int64_t*). |
| 120 | + |
| 121 | +#define NON_GENERIC_OP_WITH_OVERFLOW(_func_name, _c_type) \ |
| 122 | + [[nodiscard]] inline bool _func_name(_c_type u, _c_type v, _c_type* out) { \ |
| 123 | + return _func_name##Generic(u, v, out); \ |
72 | 124 | } |
73 | 125 |
|
74 | | -#define SIGNED_UNARY_OPS_WITH_OVERFLOW(_func_name, _psnip_op) \ |
75 | | - UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int8_t, int8) \ |
76 | | - UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int16_t, int16) \ |
77 | | - UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int32_t, int32) \ |
78 | | - UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int64_t, int64) |
| 126 | +#define NON_GENERIC_OPS_WITH_OVERFLOW(_func_name) \ |
| 127 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, int8_t) \ |
| 128 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, uint8_t) \ |
| 129 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, int16_t) \ |
| 130 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, uint16_t) \ |
| 131 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, int32_t) \ |
| 132 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, uint32_t) \ |
| 133 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, int64_t) \ |
| 134 | + NON_GENERIC_OP_WITH_OVERFLOW(_func_name, uint64_t) |
| 135 | + |
| 136 | +NON_GENERIC_OPS_WITH_OVERFLOW(AddWithOverflow) |
| 137 | +NON_GENERIC_OPS_WITH_OVERFLOW(SubtractWithOverflow) |
| 138 | +NON_GENERIC_OPS_WITH_OVERFLOW(MultiplyWithOverflow) |
| 139 | +NON_GENERIC_OPS_WITH_OVERFLOW(DivideWithOverflow) |
79 | 140 |
|
80 | | -SIGNED_UNARY_OPS_WITH_OVERFLOW(NegateWithOverflow, neg) |
| 141 | +#undef NON_GENERIC_OPS_WITH_OVERFLOW |
| 142 | +#undef NON_GENERIC_OP_WITH_OVERFLOW |
81 | 143 |
|
82 | | -#undef UNARY_OP_WITH_OVERFLOW |
83 | | -#undef SIGNED_UNARY_OPS_WITH_OVERFLOW |
| 144 | +// Define function NegateWithOverflow with the signature `bool(T u, T* out)` |
| 145 | +// where T is a signed integer type. On overflow, these functions return true. |
| 146 | +// Otherwise, false is returned and `out` is updated with the result of the |
| 147 | +// operation. |
| 148 | +template <typename Int> |
| 149 | +[[nodiscard]] bool NegateWithOverflow(Int v, Int* out) { |
| 150 | + return SubtractWithOverflow(Int{}, v, out); |
| 151 | +} |
84 | 152 |
|
85 | 153 | /// Signed addition with well-defined behaviour on overflow (as unsigned) |
86 | 154 | template <typename SignedInt> |
|
0 commit comments