kwiberg | 8a44e1d | 2016-11-01 19:04:26 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
kwiberg | 0d4e068 | 2017-04-11 05:44:07 | [diff] [blame] | 11 | // This file defines six constexpr functions: |
kwiberg | 8a44e1d | 2016-11-01 19:04:26 | [diff] [blame] | 12 | // |
kwiberg | 93ecc5d | 2017-06-26 08:31:31 | [diff] [blame] | 13 | // rtc::SafeEq // == |
| 14 | // rtc::SafeNe // != |
| 15 | // rtc::SafeLt // < |
| 16 | // rtc::SafeLe // <= |
| 17 | // rtc::SafeGt // > |
| 18 | // rtc::SafeGe // >= |
kwiberg | 8a44e1d | 2016-11-01 19:04:26 | [diff] [blame] | 19 | // |
| 20 | // They each accept two arguments of arbitrary types, and in almost all cases, |
| 21 | // they simply call the appropriate comparison operator. However, if both |
| 22 | // arguments are integers, they don't compare them using C++'s quirky rules, |
| 23 | // but instead adhere to the true mathematical definitions. It is as if the |
| 24 | // arguments were first converted to infinite-range signed integers, and then |
| 25 | // compared, although of course nothing expensive like that actually takes |
| 26 | // place. In practice, for signed/signed and unsigned/unsigned comparisons and |
| 27 | // some mixed-signed comparisons with a compile-time constant, the overhead is |
| 28 | // zero; in the remaining cases, it is just a few machine instructions (no |
| 29 | // branches). |
| 30 | |
Karl Wiberg | e40468b | 2017-11-22 09:42:26 | [diff] [blame] | 31 | #ifndef RTC_BASE_NUMERICS_SAFE_COMPARE_H_ |
| 32 | #define RTC_BASE_NUMERICS_SAFE_COMPARE_H_ |
kwiberg | 8a44e1d | 2016-11-01 19:04:26 | [diff] [blame] | 33 | |
Henrik Kjellander | ec78f1c | 2017-06-29 05:52:50 | [diff] [blame] | 34 | #include <stddef.h> |
| 35 | #include <stdint.h> |
kwiberg | 8a44e1d | 2016-11-01 19:04:26 | [diff] [blame] | 36 | |
Henrik Kjellander | ec78f1c | 2017-06-29 05:52:50 | [diff] [blame] | 37 | #include <type_traits> |
| 38 | #include <utility> |
| 39 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 40 | #include "rtc_base/type_traits.h" |
Henrik Kjellander | ec78f1c | 2017-06-29 05:52:50 | [diff] [blame] | 41 | |
| 42 | namespace rtc { |
| 43 | |
| 44 | namespace safe_cmp_impl { |
| 45 | |
| 46 | template <size_t N> |
| 47 | struct LargerIntImpl : std::false_type {}; |
| 48 | template <> |
| 49 | struct LargerIntImpl<sizeof(int8_t)> : std::true_type { |
| 50 | using type = int16_t; |
| 51 | }; |
| 52 | template <> |
| 53 | struct LargerIntImpl<sizeof(int16_t)> : std::true_type { |
| 54 | using type = int32_t; |
| 55 | }; |
| 56 | template <> |
| 57 | struct LargerIntImpl<sizeof(int32_t)> : std::true_type { |
| 58 | using type = int64_t; |
| 59 | }; |
| 60 | |
| 61 | // LargerInt<T1, T2>::value is true iff there's a signed type that's larger |
| 62 | // than T1 (and no larger than the larger of T2 and int*, for performance |
| 63 | // reasons); and if there is such a type, LargerInt<T1, T2>::type is an alias |
| 64 | // for it. |
| 65 | template <typename T1, typename T2> |
| 66 | struct LargerInt |
| 67 | : LargerIntImpl<sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int*) |
| 68 | ? sizeof(T1) |
| 69 | : 0> {}; |
| 70 | |
| 71 | template <typename T> |
| 72 | constexpr typename std::make_unsigned<T>::type MakeUnsigned(T a) { |
| 73 | return static_cast<typename std::make_unsigned<T>::type>(a); |
| 74 | } |
| 75 | |
| 76 | // Overload for when both T1 and T2 have the same signedness. |
| 77 | template <typename Op, |
| 78 | typename T1, |
| 79 | typename T2, |
| 80 | typename std::enable_if<std::is_signed<T1>::value == |
| 81 | std::is_signed<T2>::value>::type* = nullptr> |
| 82 | constexpr bool Cmp(T1 a, T2 b) { |
| 83 | return Op::Op(a, b); |
| 84 | } |
| 85 | |
| 86 | // Overload for signed - unsigned comparison that can be promoted to a bigger |
| 87 | // signed type. |
| 88 | template <typename Op, |
| 89 | typename T1, |
| 90 | typename T2, |
| 91 | typename std::enable_if<std::is_signed<T1>::value && |
| 92 | std::is_unsigned<T2>::value && |
| 93 | LargerInt<T2, T1>::value>::type* = nullptr> |
| 94 | constexpr bool Cmp(T1 a, T2 b) { |
| 95 | return Op::Op(a, static_cast<typename LargerInt<T2, T1>::type>(b)); |
| 96 | } |
| 97 | |
| 98 | // Overload for unsigned - signed comparison that can be promoted to a bigger |
| 99 | // signed type. |
| 100 | template <typename Op, |
| 101 | typename T1, |
| 102 | typename T2, |
| 103 | typename std::enable_if<std::is_unsigned<T1>::value && |
| 104 | std::is_signed<T2>::value && |
| 105 | LargerInt<T1, T2>::value>::type* = nullptr> |
| 106 | constexpr bool Cmp(T1 a, T2 b) { |
| 107 | return Op::Op(static_cast<typename LargerInt<T1, T2>::type>(a), b); |
| 108 | } |
| 109 | |
| 110 | // Overload for signed - unsigned comparison that can't be promoted to a bigger |
| 111 | // signed type. |
| 112 | template <typename Op, |
| 113 | typename T1, |
| 114 | typename T2, |
| 115 | typename std::enable_if<std::is_signed<T1>::value && |
| 116 | std::is_unsigned<T2>::value && |
| 117 | !LargerInt<T2, T1>::value>::type* = nullptr> |
| 118 | constexpr bool Cmp(T1 a, T2 b) { |
| 119 | return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b); |
| 120 | } |
| 121 | |
| 122 | // Overload for unsigned - signed comparison that can't be promoted to a bigger |
| 123 | // signed type. |
| 124 | template <typename Op, |
| 125 | typename T1, |
| 126 | typename T2, |
| 127 | typename std::enable_if<std::is_unsigned<T1>::value && |
| 128 | std::is_signed<T2>::value && |
| 129 | !LargerInt<T1, T2>::value>::type* = nullptr> |
| 130 | constexpr bool Cmp(T1 a, T2 b) { |
| 131 | return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b)); |
| 132 | } |
| 133 | |
| 134 | #define RTC_SAFECMP_MAKE_OP(name, op) \ |
| 135 | struct name { \ |
| 136 | template <typename T1, typename T2> \ |
| 137 | static constexpr bool Op(T1 a, T2 b) { \ |
| 138 | return a op b; \ |
| 139 | } \ |
| 140 | }; |
| 141 | RTC_SAFECMP_MAKE_OP(EqOp, ==) |
| 142 | RTC_SAFECMP_MAKE_OP(NeOp, !=) |
| 143 | RTC_SAFECMP_MAKE_OP(LtOp, <) |
| 144 | RTC_SAFECMP_MAKE_OP(LeOp, <=) |
| 145 | RTC_SAFECMP_MAKE_OP(GtOp, >) |
| 146 | RTC_SAFECMP_MAKE_OP(GeOp, >=) |
| 147 | #undef RTC_SAFECMP_MAKE_OP |
| 148 | |
| 149 | } // namespace safe_cmp_impl |
| 150 | |
| 151 | #define RTC_SAFECMP_MAKE_FUN(name) \ |
| 152 | template <typename T1, typename T2> \ |
| 153 | constexpr \ |
| 154 | typename std::enable_if<IsIntlike<T1>::value && IsIntlike<T2>::value, \ |
| 155 | bool>::type Safe##name(T1 a, T2 b) { \ |
| 156 | /* Unary plus here turns enums into real integral types. */ \ |
| 157 | return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(+a, +b); \ |
| 158 | } \ |
| 159 | template <typename T1, typename T2> \ |
| 160 | constexpr \ |
| 161 | typename std::enable_if<!IsIntlike<T1>::value || !IsIntlike<T2>::value, \ |
| 162 | bool>::type Safe##name(const T1& a, \ |
| 163 | const T2& b) { \ |
| 164 | return safe_cmp_impl::name##Op::Op(a, b); \ |
| 165 | } |
| 166 | RTC_SAFECMP_MAKE_FUN(Eq) |
| 167 | RTC_SAFECMP_MAKE_FUN(Ne) |
| 168 | RTC_SAFECMP_MAKE_FUN(Lt) |
| 169 | RTC_SAFECMP_MAKE_FUN(Le) |
| 170 | RTC_SAFECMP_MAKE_FUN(Gt) |
| 171 | RTC_SAFECMP_MAKE_FUN(Ge) |
| 172 | #undef RTC_SAFECMP_MAKE_FUN |
| 173 | |
| 174 | } // namespace rtc |
kwiberg | 8a44e1d | 2016-11-01 19:04:26 | [diff] [blame] | 175 | |
Karl Wiberg | e40468b | 2017-11-22 09:42:26 | [diff] [blame] | 176 | #endif // RTC_BASE_NUMERICS_SAFE_COMPARE_H_ |