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 | |
ossu
2016/10/31 16:44:57
I bet the ?: operator could turn into a branch on
kwiberg-webrtc
2016/10/31 21:32:26
Probably. I didn't even try to look at unoptimized
ossu
2016/11/01 15:36:57
Not sure if it matters, but perhaps turning the ?:
kwiberg-webrtc
2016/11/01 16:14:06
No, this is the sort of transformation the compile
ossu
2016/11/01 16:48:07
You think? Yeah, maybe. Granted, if the compiler i
| |
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)); | |
ossu
2016/11/01 15:36:57
Isn't Op::Op(0, -1) a bad choice here if Op is sup
kwiberg-webrtc
2016/11/01 16:14:06
Op::Op<T1, T2> will accept any two types, and the
ossu
2016/11/01 16:48:07
Ah, right, yes. I somehow expected them to be type
| |
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) { \ | |
kwiberg-webrtc
2016/10/31 09:16:01
Should I perhaps take a and b by value instead?
ossu
2016/10/31 16:44:57
I'd say const&. That's what comparison operators u
kwiberg-webrtc
2016/10/31 21:32:26
Yes, but this is specifically for built-in integer
ossu
2016/11/01 15:36:57
Ah, right. Yes, pass by value instead!
kwiberg-webrtc
2016/11/01 16:14:06
Done.
| |
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) \ | |
ossu
2016/10/31 16:44:57
I like fun! Make more fun! :D
kwiberg-webrtc
2016/10/31 21:32:26
Acknowledged.
| |
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 |