OLD | NEW |
(Empty) | |
| 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 |
| 11 // This file defines six functions: |
| 12 // |
| 13 // rtc::safe_cmp::Eq // == |
| 14 // rtc::safe_cmp::Ne // != |
| 15 // rtc::safe_cmp::Lt // < |
| 16 // rtc::safe_cmp::Le // <= |
| 17 // rtc::safe_cmp::Gt // > |
| 18 // rtc::safe_cmp::Ge // >= |
| 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 |
| 31 #ifndef WEBRTC_BASE_SAFE_COMPARE_H_ |
| 32 #define WEBRTC_BASE_SAFE_COMPARE_H_ |
| 33 |
| 34 #include <stddef.h> |
| 35 #include <stdint.h> |
| 36 |
| 37 #include <type_traits> |
| 38 #include <utility> |
| 39 |
| 40 namespace rtc { |
| 41 namespace safe_cmp { |
| 42 |
| 43 namespace safe_cmp_impl { |
| 44 |
| 45 template <size_t N> |
| 46 struct LargerIntImpl : std::false_type {}; |
| 47 template <> |
| 48 struct LargerIntImpl<sizeof(int8_t)> : std::true_type { |
| 49 using type = int16_t; |
| 50 }; |
| 51 template <> |
| 52 struct LargerIntImpl<sizeof(int16_t)> : std::true_type { |
| 53 using type = int32_t; |
| 54 }; |
| 55 template <> |
| 56 struct LargerIntImpl<sizeof(int32_t)> : std::true_type { |
| 57 using type = int64_t; |
| 58 }; |
| 59 |
| 60 // LargerInt<T1, T2>::value is true iff there's a signed type that's larger |
| 61 // than T1 (and no larger than the larger of T2 and int*, for performance |
| 62 // reasons); and if there is such a type, LargerInt<T1, T2>::type is an alias |
| 63 // for it. |
| 64 template <typename T1, typename T2> |
| 65 struct LargerInt |
| 66 : LargerIntImpl<sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int*) |
| 67 ? sizeof(T1) |
| 68 : 0> {}; |
| 69 |
| 70 template <typename T> |
| 71 inline typename std::make_unsigned<T>::type MakeUnsigned(T a) { |
| 72 return static_cast<typename std::make_unsigned<T>::type>(a); |
| 73 } |
| 74 |
| 75 // Overload for when both T1 and T2 have the same signedness. |
| 76 template <typename Op, |
| 77 typename T1, |
| 78 typename T2, |
| 79 typename std::enable_if<std::is_signed<T1>::value == |
| 80 std::is_signed<T2>::value>::type* = nullptr> |
| 81 inline bool Cmp(T1 a, T2 b) { |
| 82 return Op::Op(a, b); |
| 83 } |
| 84 |
| 85 // Overload for signed - unsigned comparison that can be promoted to a bigger |
| 86 // signed type. |
| 87 template <typename Op, |
| 88 typename T1, |
| 89 typename T2, |
| 90 typename std::enable_if<std::is_signed<T1>::value && |
| 91 std::is_unsigned<T2>::value && |
| 92 LargerInt<T2, T1>::value>::type* = nullptr> |
| 93 inline bool Cmp(T1 a, T2 b) { |
| 94 return Op::Op(a, static_cast<typename LargerInt<T2, T1>::type>(b)); |
| 95 } |
| 96 |
| 97 // Overload for unsigned - signed comparison that can be promoted to a bigger |
| 98 // signed type. |
| 99 template <typename Op, |
| 100 typename T1, |
| 101 typename T2, |
| 102 typename std::enable_if<std::is_unsigned<T1>::value && |
| 103 std::is_signed<T2>::value && |
| 104 LargerInt<T1, T2>::value>::type* = nullptr> |
| 105 inline bool Cmp(T1 a, T2 b) { |
| 106 return Op::Op(static_cast<typename LargerInt<T1, T2>::type>(a), b); |
| 107 } |
| 108 |
| 109 // Overload for signed - unsigned comparison that can't be promoted to a bigger |
| 110 // signed type. |
| 111 template <typename Op, |
| 112 typename T1, |
| 113 typename T2, |
| 114 typename std::enable_if<std::is_signed<T1>::value && |
| 115 std::is_unsigned<T2>::value && |
| 116 !LargerInt<T2, T1>::value>::type* = nullptr> |
| 117 inline bool Cmp(T1 a, T2 b) { |
| 118 return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b); |
| 119 } |
| 120 |
| 121 // Overload for unsigned - signed comparison that can't be promoted to a bigger |
| 122 // signed type. |
| 123 template <typename Op, |
| 124 typename T1, |
| 125 typename T2, |
| 126 typename std::enable_if<std::is_unsigned<T1>::value && |
| 127 std::is_signed<T2>::value && |
| 128 !LargerInt<T1, T2>::value>::type* = nullptr> |
| 129 inline bool Cmp(T1 a, T2 b) { |
| 130 return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b)); |
| 131 } |
| 132 |
| 133 #define RTC_SAFECMP_MAKE_OP(name, op) \ |
| 134 struct name { \ |
| 135 template <typename T1, typename T2> \ |
| 136 static constexpr bool Op(T1 a, T2 b) { \ |
| 137 return a op b; \ |
| 138 } \ |
| 139 }; |
| 140 RTC_SAFECMP_MAKE_OP(EqOp, ==) |
| 141 RTC_SAFECMP_MAKE_OP(NeOp, !=) |
| 142 RTC_SAFECMP_MAKE_OP(LtOp, <) |
| 143 RTC_SAFECMP_MAKE_OP(LeOp, <=) |
| 144 RTC_SAFECMP_MAKE_OP(GtOp, >) |
| 145 RTC_SAFECMP_MAKE_OP(GeOp, >=) |
| 146 #undef RTC_SAFECMP_MAKE_OP |
| 147 |
| 148 } // namespace safe_cmp_impl |
| 149 |
| 150 #define RTC_SAFECMP_MAKE_FUN(name) \ |
| 151 template < \ |
| 152 typename T1, typename T2, \ |
| 153 typename std::enable_if< \ |
| 154 std::is_integral<typename std::remove_reference<T1>::type>::value && \ |
| 155 std::is_integral<typename std::remove_reference<T2>::type>::value>:: \ |
| 156 type* = nullptr> \ |
| 157 inline bool name(T1 a, T2 b) { \ |
| 158 return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(a, b); \ |
| 159 } \ |
| 160 template <typename T1, typename T2, \ |
| 161 typename std::enable_if< \ |
| 162 !std::is_integral< \ |
| 163 typename std::remove_reference<T1>::type>::value || \ |
| 164 !std::is_integral<typename std::remove_reference<T2>::type>:: \ |
| 165 value>::type* = nullptr> \ |
| 166 inline bool name(T1&& a, T2&& b) { \ |
| 167 return safe_cmp_impl::name##Op::Op(a, b); \ |
| 168 } |
| 169 RTC_SAFECMP_MAKE_FUN(Eq) |
| 170 RTC_SAFECMP_MAKE_FUN(Ne) |
| 171 RTC_SAFECMP_MAKE_FUN(Lt) |
| 172 RTC_SAFECMP_MAKE_FUN(Le) |
| 173 RTC_SAFECMP_MAKE_FUN(Gt) |
| 174 RTC_SAFECMP_MAKE_FUN(Ge) |
| 175 #undef RTC_SAFECMP_MAKE_FUN |
| 176 |
| 177 } // namespace safe_cmp |
| 178 } // namespace rtc |
| 179 |
| 180 #endif // WEBRTC_BASE_SAFE_COMPARE_H_ |
OLD | NEW |