Flutter Engine
The Flutter Engine
safe_conversions.h
Go to the documentation of this file.
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
6#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
7
8#include <cmath>
9#include <cstddef>
10#include <limits>
11#include <type_traits>
12
14
15#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
17#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
18#else
19#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
20#endif
21
22#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
23#include <ostream>
24#endif
25
26namespace base {
27namespace internal {
28
29#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
30template <typename Dst, typename Src>
32 static constexpr bool is_supported = false;
33 static constexpr Dst Do(Src) {
34 // Force a compile failure if instantiated.
35 return CheckOnFailure::template HandleFailure<Dst>();
36 }
37};
38#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
39#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
40
41// The following special case a few specific integer conversions where we can
42// eke out better performance than range checking.
43template <typename Dst, typename Src, typename Enable = void>
45 static constexpr bool is_supported = false;
46 static constexpr bool Do(Src value) {
47 // Force a compile failure if instantiated.
48 return CheckOnFailure::template HandleFailure<bool>();
49 }
50};
51
52// Signed to signed range comparison.
53template <typename Dst, typename Src>
55 Dst,
56 Src,
57 typename std::enable_if<
58 std::is_integral<Dst>::value && std::is_integral<Src>::value &&
59 std::is_signed<Dst>::value && std::is_signed<Src>::value &&
60 !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
61 static constexpr bool is_supported = true;
62
63 static constexpr bool Do(Src value) {
64 // Just downcast to the smaller type, sign extend it back to the original
65 // type, and then see if it matches the original value.
66 return value == static_cast<Dst>(value);
67 }
68};
69
70// Signed to unsigned range comparison.
71template <typename Dst, typename Src>
73 Dst,
74 Src,
75 typename std::enable_if<
76 std::is_integral<Dst>::value && std::is_integral<Src>::value &&
77 !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
78 !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
79 static constexpr bool is_supported = true;
80
81 static constexpr bool Do(Src value) {
82 // We cast a signed as unsigned to overflow negative values to the top,
83 // then compare against whichever maximum is smaller, as our upper bound.
84 return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
85 }
86};
87
88// Convenience function that returns true if the supplied value is in range
89// for the destination type.
90template <typename Dst, typename Src>
95 static_cast<SrcType>(value))
96 : internal::DstRangeRelationToSrcRange<Dst>(
97 static_cast<SrcType>(value))
98 .IsValid();
99}
100
101// checked_cast<> is analogous to static_cast<> for numeric types,
102// except that it CHECKs that the specified numeric conversion will not
103// overflow or underflow. NaN source will always trigger a CHECK.
104template <typename Dst,
105 class CheckHandler = internal::CheckOnFailure,
106 typename Src>
107constexpr Dst checked_cast(Src value) {
108 // This throws a compile-time error on evaluating the constexpr if it can be
109 // determined at compile-time as failing, otherwise it will CHECK at runtime.
111 return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
112 ? static_cast<Dst>(static_cast<SrcType>(value))
113 : CheckHandler::template HandleFailure<Dst>();
114}
115
116// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
117// You may provide your own limits (e.g. to saturated_cast) so long as you
118// implement all of the static constexpr member functions in the class below.
119template <typename T>
120struct SaturationDefaultLimits : public std::numeric_limits<T> {
121 static constexpr T NaN() {
122 return std::numeric_limits<T>::has_quiet_NaN
123 ? std::numeric_limits<T>::quiet_NaN()
124 : T();
125 }
126 using std::numeric_limits<T>::max;
127 static constexpr T Overflow() {
128 return std::numeric_limits<T>::has_infinity
129 ? std::numeric_limits<T>::infinity()
131 }
132 using std::numeric_limits<T>::lowest;
133 static constexpr T Underflow() {
134 return std::numeric_limits<T>::has_infinity
135 ? std::numeric_limits<T>::infinity() * -1
136 : std::numeric_limits<T>::lowest();
137 }
138};
139
140template <typename Dst, template <typename> class S, typename Src>
141constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
142 // For some reason clang generates much better code when the branch is
143 // structured exactly this way, rather than a sequence of checks.
144 return !constraint.IsOverflowFlagSet()
145 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
146 : S<Dst>::Underflow())
147 // Skip this check for integral Src, which cannot be NaN.
149 ? S<Dst>::Overflow()
150 : S<Dst>::NaN());
151}
152
153// We can reduce the number of conditions and get slightly better performance
154// for normal signed and unsigned integer ranges. And in the specific case of
155// Arm, we can use the optimized saturation instructions.
156template <typename Dst, typename Src, typename Enable = void>
158 static constexpr bool is_supported = false;
159 static constexpr Dst Do(Src value) {
160 // Force a compile failure if instantiated.
161 return CheckOnFailure::template HandleFailure<Dst>();
162 }
163};
164
165template <typename Dst, typename Src>
167 Dst,
168 Src,
169 typename std::enable_if<std::is_integral<Src>::value &&
170 std::is_integral<Dst>::value &&
171 SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
172 static constexpr bool is_supported = true;
173 static constexpr Dst Do(Src value) {
175 }
176};
177
178template <typename Dst, typename Src>
180 Dst,
181 Src,
182 typename std::enable_if<std::is_integral<Src>::value &&
183 std::is_integral<Dst>::value &&
184 !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
185 static constexpr bool is_supported = true;
186 static constexpr Dst Do(Src value) {
187 // The exact order of the following is structured to hit the correct
188 // optimization heuristics across compilers. Do not change without
189 // checking the emitted code.
190 const Dst saturated = CommonMaxOrMin<Dst, Src>(
191 IsMaxInRangeForNumericType<Dst, Src>() ||
192 (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
193 return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
194 ? static_cast<Dst>(value)
195 : saturated;
196 }
197};
198
199// saturated_cast<> is analogous to static_cast<> for numeric types, except
200// that the specified numeric conversion will saturate by default rather than
201// overflow or underflow, and NaN assignment to an integral will return 0.
202// All boundary condition behaviors can be overridden with a custom handler.
203template <typename Dst,
204 template <typename> class SaturationHandler = SaturationDefaultLimits,
205 typename Src>
206constexpr Dst saturated_cast(Src value) {
207 using SrcType = typename UnderlyingType<Src>::type;
208 return !IsCompileTimeConstant(value) &&
210 std::is_same<SaturationHandler<Dst>,
213 : saturated_cast_impl<Dst, SaturationHandler, SrcType>(
214 static_cast<SrcType>(value),
215 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
216 static_cast<SrcType>(value)));
217}
218
219// strict_cast<> is analogous to static_cast<> for numeric types, except that
220// it will cause a compile failure if the destination type is not large enough
221// to contain any value in the source type. It performs no runtime checking.
222template <typename Dst, typename Src>
223constexpr Dst strict_cast(Src value) {
224 using SrcType = typename UnderlyingType<Src>::type;
225 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
226 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
227
228 // If you got here from a compiler error, it's because you tried to assign
229 // from a source type to a destination type that has insufficient range.
230 // The solution may be to change the destination type you're assigning to,
231 // and use one large enough to represent the source.
232 // Alternatively, you may be better served with the checked_cast<> or
233 // saturated_cast<> template functions for your particular use case.
236 "The source type is out of range for the destination type. "
237 "Please see strict_cast<> comments for more information.");
238
239 return static_cast<Dst>(static_cast<SrcType>(value));
240}
241
242// Some wrappers to statically check that a type is in range.
243template <typename Dst, typename Src, class Enable = void>
245 static constexpr bool value = false;
246};
247
248template <typename Dst, typename Src>
250 Dst,
251 Src,
252 typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
253 ArithmeticOrUnderlyingEnum<Src>::value>::type> {
254 static constexpr bool value =
257};
258
259// StrictNumeric implements compile time range checking between numeric types by
260// wrapping assignment operations in a strict_cast. This class is intended to be
261// used for function arguments and return types, to ensure the destination type
262// can always contain the source type. This is essentially the same as enforcing
263// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
264// incrementally at API boundaries, making it easier to convert code so that it
265// compiles cleanly with truncation warnings enabled.
266// This template should introduce no runtime overhead, but it also provides no
267// runtime checking of any of the associated mathematical operations. Use
268// CheckedNumeric for runtime range checks of the actual value being assigned.
269template <typename T>
271 public:
272 using type = T;
273
274 constexpr StrictNumeric() : value_(0) {}
275
276 // Copy constructor.
277 template <typename Src>
278 constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
279 : value_(strict_cast<T>(rhs.value_)) {}
280
281 // This is not an explicit constructor because we implicitly upgrade regular
282 // numerics to StrictNumerics to make them easier to use.
283 template <typename Src>
284 constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
285 : value_(strict_cast<T>(value)) {}
286
287 // If you got here from a compiler error, it's because you tried to assign
288 // from a source type to a destination type that has insufficient range.
289 // The solution may be to change the destination type you're assigning to,
290 // and use one large enough to represent the source.
291 // If you're assigning from a CheckedNumeric<> class, you may be able to use
292 // the AssignIfValid() member function, specify a narrower destination type to
293 // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
294 // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
295 // If you've encountered an _ambiguous overload_ you can use a static_cast<>
296 // to explicitly cast the result to the destination type.
297 // If none of that works, you may be better served with the checked_cast<> or
298 // saturated_cast<> template functions for your particular use case.
299 template <typename Dst,
300 typename std::enable_if<
302 constexpr operator Dst() const {
303 return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
304 }
305
306 private:
307 const T value_;
308};
309
310// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
311template <typename T>
313 const T value) {
314 return value;
315}
316
317#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
318// Overload the ostream output operator to make logging work nicely.
319template <typename T>
320std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
321 os << static_cast<T>(value);
322 return os;
323}
324#endif
325
326#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
327 template <typename L, typename R, \
328 typename std::enable_if< \
329 internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
330 constexpr bool operator OP(const L lhs, const R rhs) { \
331 return SafeCompare<NAME, typename UnderlyingType<L>::type, \
332 typename UnderlyingType<R>::type>(lhs, rhs); \
333 }
334
335BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
336BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
337BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
338BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
339BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
340BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
341
342} // namespace internal
343
344using internal::as_signed;
345using internal::as_unsigned;
346using internal::checked_cast;
347using internal::IsTypeInRangeForNumericType;
348using internal::IsValueInRangeForNumericType;
349using internal::IsValueNegative;
350using internal::MakeStrictNum;
351using internal::SafeUnsignedAbs;
352using internal::saturated_cast;
353using internal::strict_cast;
354using internal::StrictNumeric;
355
356// Explicitly make a shorter size_t alias for convenience.
357using SizeT = StrictNumeric<size_t>;
358
359// floating -> integral conversions that saturate and thus can actually return
360// an integral type. In most cases, these should be preferred over the std::
361// versions.
362template <typename Dst = int,
363 typename Src,
364 typename = std::enable_if_t<std::is_integral<Dst>::value &&
365 std::is_floating_point<Src>::value>>
367 return saturated_cast<Dst>(std::floor(value));
368}
369template <typename Dst = int,
370 typename Src,
373Dst ClampCeil(Src value) {
374 return saturated_cast<Dst>(std::ceil(value));
375}
376template <typename Dst = int,
377 typename Src,
381 const Src rounded =
382 (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f);
383 return saturated_cast<Dst>(rounded);
384}
385
386} // namespace base
387
388#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
static bool is_integral(const SkRect &r)
GLenum type
constexpr bool IsOverflowFlagSet() const
constexpr bool IsUnderflowFlagSet() const
constexpr StrictNumeric(const StrictNumeric< Src > &rhs)
constexpr StrictNumeric(Src value)
uint8_t value
static float max(float r, float g, float b)
Definition: hsl.cpp:49
constexpr Dst strict_cast(Src value)
constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint)
constexpr bool IsValueNegative(T value)
constexpr std::make_unsigned< T >::type SafeUnsignedAbs(T value)
constexpr std::make_signed< typenamebase::internal::UnderlyingType< Src >::type >::type as_signed(const Src value)
constexpr StrictNumeric< typename UnderlyingType< T >::type > MakeStrictNum(const T value)
constexpr std::make_unsigned< typenamebase::internal::UnderlyingType< Src >::type >::type as_unsigned(const Src value)
constexpr bool IsCompileTimeConstant(const T)
constexpr bool IsValueInRangeForNumericType(Src value)
std::ostream & operator<<(std::ostream &os, const ClampedNumeric< T > &value)
Definition: clamped_math.h:197
constexpr Dst checked_cast(Src value)
constexpr Dst saturated_cast(Src value)
Dst ClampFloor(Src value)
Dst ClampCeil(Src value)
Dst ClampRound(Src value)
SIN Vec< N, float > floor(const Vec< N, float > &x)
Definition: SkVx.h:703
SIN Vec< N, float > ceil(const Vec< N, float > &x)
Definition: SkVx.h:702
Definition: ref_ptr.h:256
#define T
Definition: precompiler.cc:65
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)
#define BASE_NUMERICS_LIKELY(x)
static constexpr bool Do(Src value)
static constexpr Dst Do(Src)
static constexpr bool is_supported
static constexpr bool is_supported
static constexpr Dst Do(Src value)
typename ArithmeticOrUnderlyingEnum< T >::type type
SrcType
Definition: xfermodes.cpp:29