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