Line data Source code
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 0 : inline typename std::make_unsigned<T>::type MakeUnsigned(T a) {
72 0 : 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 0 : inline bool Cmp(T1 a, T2 b) {
82 0 : 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 0 : inline bool Cmp(T1 a, T2 b) {
94 0 : 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 0 : inline bool Cmp(T1 a, T2 b) {
106 0 : 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 0 : inline bool Cmp(T1 a, T2 b) {
118 0 : 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 0 : inline bool Cmp(T1 a, T2 b) {
130 0 : 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 0 : RTC_SAFECMP_MAKE_OP(EqOp, ==)
141 0 : RTC_SAFECMP_MAKE_OP(NeOp, !=)
142 0 : RTC_SAFECMP_MAKE_OP(LtOp, <)
143 0 : RTC_SAFECMP_MAKE_OP(LeOp, <=)
144 0 : RTC_SAFECMP_MAKE_OP(GtOp, >)
145 0 : RTC_SAFECMP_MAKE_OP(GeOp, >=)
146 : #undef RTC_SAFECMP_MAKE_OP
147 :
148 : // Determines if the given type is an enum that converts implicitly to
149 : // an integral type.
150 : template <typename T>
151 : struct IsIntEnum {
152 : private:
153 : // This overload is used if the type is an enum, and unary plus
154 : // compiles and turns it into an integral type.
155 : template <typename X,
156 : typename std::enable_if<
157 : std::is_enum<X>::value &&
158 : std::is_integral<decltype(+std::declval<X>())>::value>::type* =
159 : nullptr>
160 : static int Test(int);
161 :
162 : // Otherwise, this overload is used.
163 : template <typename>
164 : static char Test(...);
165 :
166 : public:
167 : static constexpr bool value =
168 : std::is_same<decltype(Test<typename std::remove_reference<T>::type>(0)),
169 : int>::value;
170 : };
171 :
172 : // Determines if the given type is integral, or an enum that
173 : // converts implicitly to an integral type.
174 : template <typename T>
175 : struct IsIntlike {
176 : private:
177 : using X = typename std::remove_reference<T>::type;
178 :
179 : public:
180 : static constexpr bool value =
181 : std::is_integral<X>::value || IsIntEnum<X>::value;
182 : };
183 :
184 : namespace test_enum_intlike {
185 :
186 : enum E1 { e1 };
187 : enum { e2 };
188 : enum class E3 { e3 };
189 : struct S {};
190 :
191 : static_assert(IsIntEnum<E1>::value, "");
192 : static_assert(IsIntEnum<decltype(e2)>::value, "");
193 : static_assert(!IsIntEnum<E3>::value, "");
194 : static_assert(!IsIntEnum<int>::value, "");
195 : static_assert(!IsIntEnum<float>::value, "");
196 : static_assert(!IsIntEnum<S>::value, "");
197 :
198 : static_assert(IsIntlike<E1>::value, "");
199 : static_assert(IsIntlike<decltype(e2)>::value, "");
200 : static_assert(!IsIntlike<E3>::value, "");
201 : static_assert(IsIntlike<int>::value, "");
202 : static_assert(!IsIntlike<float>::value, "");
203 : static_assert(!IsIntlike<S>::value, "");
204 :
205 : } // test_enum_intlike
206 : } // namespace safe_cmp_impl
207 :
208 : #define RTC_SAFECMP_MAKE_FUN(name) \
209 : template <typename T1, typename T2, \
210 : typename std::enable_if< \
211 : safe_cmp_impl::IsIntlike<T1>::value && \
212 : safe_cmp_impl::IsIntlike<T2>::value>::type* = nullptr> \
213 : inline bool name(T1 a, T2 b) { \
214 : /* Unary plus here turns enums into real integral types. */ \
215 : return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(+a, +b); \
216 : } \
217 : template <typename T1, typename T2, \
218 : typename std::enable_if< \
219 : !safe_cmp_impl::IsIntlike<T1>::value || \
220 : !safe_cmp_impl::IsIntlike<T2>::value>::type* = nullptr> \
221 : inline bool name(T1&& a, T2&& b) { \
222 : return safe_cmp_impl::name##Op::Op(a, b); \
223 : }
224 0 : RTC_SAFECMP_MAKE_FUN(Eq)
225 0 : RTC_SAFECMP_MAKE_FUN(Ne)
226 0 : RTC_SAFECMP_MAKE_FUN(Lt)
227 0 : RTC_SAFECMP_MAKE_FUN(Le)
228 0 : RTC_SAFECMP_MAKE_FUN(Gt)
229 0 : RTC_SAFECMP_MAKE_FUN(Ge)
230 : #undef RTC_SAFECMP_MAKE_FUN
231 :
232 : } // namespace safe_cmp
233 : } // namespace rtc
234 :
235 : #endif // WEBRTC_BASE_SAFE_COMPARE_H_
|