blob: 0350d5df81785084f2fad4b06f3543d6bc4f8867 [file] [log] [blame]
kwiberg8a44e1d2016-11-01 12:04:26 -07001/*
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
kwiberg0d4e0682017-04-10 22:44:07 -070011// This file defines six constexpr functions:
kwiberg8a44e1d2016-11-01 12:04:26 -070012//
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
kwibergb0f7e392017-04-10 06:56:58 -070040#include "webrtc/base/type_traits.h"
41
kwiberg8a44e1d2016-11-01 12:04:26 -070042namespace rtc {
43namespace safe_cmp {
44
45namespace safe_cmp_impl {
46
47template <size_t N>
48struct LargerIntImpl : std::false_type {};
49template <>
50struct LargerIntImpl<sizeof(int8_t)> : std::true_type {
51 using type = int16_t;
52};
53template <>
54struct LargerIntImpl<sizeof(int16_t)> : std::true_type {
55 using type = int32_t;
56};
57template <>
58struct LargerIntImpl<sizeof(int32_t)> : std::true_type {
59 using type = int64_t;
60};
61
62// LargerInt<T1, T2>::value is true iff there's a signed type that's larger
63// than T1 (and no larger than the larger of T2 and int*, for performance
64// reasons); and if there is such a type, LargerInt<T1, T2>::type is an alias
65// for it.
66template <typename T1, typename T2>
67struct LargerInt
68 : LargerIntImpl<sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int*)
69 ? sizeof(T1)
70 : 0> {};
71
72template <typename T>
kwiberg0d4e0682017-04-10 22:44:07 -070073constexpr typename std::make_unsigned<T>::type MakeUnsigned(T a) {
kwiberg8a44e1d2016-11-01 12:04:26 -070074 return static_cast<typename std::make_unsigned<T>::type>(a);
75}
76
77// Overload for when both T1 and T2 have the same signedness.
78template <typename Op,
79 typename T1,
80 typename T2,
81 typename std::enable_if<std::is_signed<T1>::value ==
82 std::is_signed<T2>::value>::type* = nullptr>
kwiberg0d4e0682017-04-10 22:44:07 -070083constexpr bool Cmp(T1 a, T2 b) {
kwiberg8a44e1d2016-11-01 12:04:26 -070084 return Op::Op(a, b);
85}
86
87// Overload for signed - unsigned comparison that can be promoted to a bigger
88// signed type.
89template <typename Op,
90 typename T1,
91 typename T2,
92 typename std::enable_if<std::is_signed<T1>::value &&
93 std::is_unsigned<T2>::value &&
94 LargerInt<T2, T1>::value>::type* = nullptr>
kwiberg0d4e0682017-04-10 22:44:07 -070095constexpr bool Cmp(T1 a, T2 b) {
kwiberg8a44e1d2016-11-01 12:04:26 -070096 return Op::Op(a, static_cast<typename LargerInt<T2, T1>::type>(b));
97}
98
99// Overload for unsigned - signed comparison that can be promoted to a bigger
100// signed type.
101template <typename Op,
102 typename T1,
103 typename T2,
104 typename std::enable_if<std::is_unsigned<T1>::value &&
105 std::is_signed<T2>::value &&
106 LargerInt<T1, T2>::value>::type* = nullptr>
kwiberg0d4e0682017-04-10 22:44:07 -0700107constexpr bool Cmp(T1 a, T2 b) {
kwiberg8a44e1d2016-11-01 12:04:26 -0700108 return Op::Op(static_cast<typename LargerInt<T1, T2>::type>(a), b);
109}
110
111// Overload for signed - unsigned comparison that can't be promoted to a bigger
112// signed type.
113template <typename Op,
114 typename T1,
115 typename T2,
116 typename std::enable_if<std::is_signed<T1>::value &&
117 std::is_unsigned<T2>::value &&
118 !LargerInt<T2, T1>::value>::type* = nullptr>
kwiberg0d4e0682017-04-10 22:44:07 -0700119constexpr bool Cmp(T1 a, T2 b) {
kwiberg8a44e1d2016-11-01 12:04:26 -0700120 return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b);
121}
122
123// Overload for unsigned - signed comparison that can't be promoted to a bigger
124// signed type.
125template <typename Op,
126 typename T1,
127 typename T2,
128 typename std::enable_if<std::is_unsigned<T1>::value &&
129 std::is_signed<T2>::value &&
130 !LargerInt<T1, T2>::value>::type* = nullptr>
kwiberg0d4e0682017-04-10 22:44:07 -0700131constexpr bool Cmp(T1 a, T2 b) {
kwiberg8a44e1d2016-11-01 12:04:26 -0700132 return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b));
133}
134
135#define RTC_SAFECMP_MAKE_OP(name, op) \
136 struct name { \
137 template <typename T1, typename T2> \
138 static constexpr bool Op(T1 a, T2 b) { \
139 return a op b; \
140 } \
141 };
142RTC_SAFECMP_MAKE_OP(EqOp, ==)
143RTC_SAFECMP_MAKE_OP(NeOp, !=)
144RTC_SAFECMP_MAKE_OP(LtOp, <)
145RTC_SAFECMP_MAKE_OP(LeOp, <=)
146RTC_SAFECMP_MAKE_OP(GtOp, >)
147RTC_SAFECMP_MAKE_OP(GeOp, >=)
148#undef RTC_SAFECMP_MAKE_OP
149
150} // namespace safe_cmp_impl
151
kwiberg0d4e0682017-04-10 22:44:07 -0700152#define RTC_SAFECMP_MAKE_FUN(name) \
153 template <typename T1, typename T2> \
154 constexpr \
155 typename std::enable_if<IsIntlike<T1>::value && IsIntlike<T2>::value, \
156 bool>::type \
157 name(T1 a, T2 b) { \
158 /* Unary plus here turns enums into real integral types. */ \
159 return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(+a, +b); \
160 } \
161 template <typename T1, typename T2> \
162 constexpr \
163 typename std::enable_if<!IsIntlike<T1>::value || !IsIntlike<T2>::value, \
164 bool>::type \
165 name(const T1& a, const T2& b) { \
166 return safe_cmp_impl::name##Op::Op(a, b); \
kwiberg8a44e1d2016-11-01 12:04:26 -0700167 }
168RTC_SAFECMP_MAKE_FUN(Eq)
169RTC_SAFECMP_MAKE_FUN(Ne)
170RTC_SAFECMP_MAKE_FUN(Lt)
171RTC_SAFECMP_MAKE_FUN(Le)
172RTC_SAFECMP_MAKE_FUN(Gt)
173RTC_SAFECMP_MAKE_FUN(Ge)
174#undef RTC_SAFECMP_MAKE_FUN
175
176} // namespace safe_cmp
177} // namespace rtc
178
179#endif // WEBRTC_BASE_SAFE_COMPARE_H_