1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378
  379
  380
  381
  382
  383
  384

base / numerics / safe_conversions.h [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_

#include <stddef.h>

#include <cmath>
#include <concepts>
#include <limits>
#include <type_traits>

#include "base/numerics/safe_conversions_impl.h"  // IWYU pragma: export

#if defined(__ARMEL__) && !defined(__native_client__)
#include "base/numerics/safe_conversions_arm_impl.h"  // IWYU pragma: export
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
#else
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
#endif

namespace base {
namespace internal {

#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
template <typename Dst, typename Src>
struct SaturateFastAsmOp {
  static constexpr bool is_supported = false;
  static constexpr Dst Do(Src) {
    // Force a compile failure if instantiated.
    return CheckOnFailure::template HandleFailure<Dst>();
  }
};
#endif  // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS

// The following special case a few specific integer conversions where we can
// eke out better performance than range checking.
template <typename Dst, typename Src>
struct IsValueInRangeFastOp {
  static constexpr bool is_supported = false;
  static constexpr bool Do(Src) {
    // Force a compile failure if instantiated.
    return CheckOnFailure::template HandleFailure<bool>();
  }
};

// Signed to signed range comparison.
template <typename Dst, typename Src>
  requires(std::signed_integral<Dst> && std::signed_integral<Src> &&
           !kIsTypeInRangeForNumericType<Dst, Src>)
struct IsValueInRangeFastOp<Dst, Src> {
  static constexpr bool is_supported = true;

  static constexpr bool Do(Src value) {
    // Just downcast to the smaller type, sign extend it back to the original
    // type, and then see if it matches the original value.
    return value == static_cast<Dst>(value);
  }
};

// Signed to unsigned range comparison.
template <typename Dst, typename Src>
  requires(std::unsigned_integral<Dst> && std::signed_integral<Src> &&
           !kIsTypeInRangeForNumericType<Dst, Src>)
struct IsValueInRangeFastOp<Dst, Src> {
  static constexpr bool is_supported = true;

  static constexpr bool Do(Src value) {
    // We cast a signed as unsigned to overflow negative values to the top,
    // then compare against whichever maximum is smaller, as our upper bound.
    return as_unsigned(value) <= as_unsigned(kCommonMax<Src, Dst>);
  }
};

// Convenience function that returns true if the supplied value is in range
// for the destination type.
template <typename Dst, typename Src>
  requires(IsNumeric<Src> && std::is_arithmetic_v<Dst> &&
           std::numeric_limits<Dst>::lowest() < std::numeric_limits<Dst>::max())
constexpr bool IsValueInRangeForNumericType(Src value) {
  using SrcType = UnderlyingType<Src>;
  const auto underlying_value = static_cast<SrcType>(value);
  return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
             ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
                   underlying_value)
             : internal::DstRangeRelationToSrcRange<Dst>(underlying_value)
                   .IsValid();
}

// checked_cast<> is analogous to static_cast<> for numeric types,
// except that it CHECKs that the specified numeric conversion will not
// overflow or underflow. NaN source will always trigger a CHECK.
template <typename Dst,
          class CheckHandler = internal::CheckOnFailure,
          typename Src>
  requires(IsNumeric<Src> && std::is_arithmetic_v<Dst> &&
           std::numeric_limits<Dst>::lowest() < std::numeric_limits<Dst>::max())
constexpr Dst checked_cast(Src value) {
  // This throws a compile-time error on evaluating the constexpr if it can be
  // determined at compile-time as failing, otherwise it will CHECK at runtime.
  if (IsValueInRangeForNumericType<Dst>(value)) [[likely]] {
    return static_cast<Dst>(static_cast<UnderlyingType<Src>>(value));
  }
  return CheckHandler::template HandleFailure<Dst>();
}

// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
// You may provide your own limits (e.g. to saturated_cast) so long as you
// implement all of the static constexpr member functions in the class below.
template <typename T>
struct SaturationDefaultLimits : public std::numeric_limits<T> {
  static constexpr T NaN() {
    if constexpr (std::numeric_limits<T>::has_quiet_NaN) {
      return std::numeric_limits<T>::quiet_NaN();
    } else {
      return T();
    }
  }
  using std::numeric_limits<T>::max;
  static constexpr T Overflow() {
    if constexpr (std::numeric_limits<T>::has_infinity) {
      return std::numeric_limits<T>::infinity();
    } else {
      return std::numeric_limits<T>::max();
    }
  }
  using std::numeric_limits<T>::lowest;
  static constexpr T Underflow() {
    if constexpr (std::numeric_limits<T>::has_infinity) {
      return std::numeric_limits<T>::infinity() * -1;
    } else {
      return std::numeric_limits<T>::lowest();
    }
  }
};

template <typename Dst, template <typename> class S, typename Src>
constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
  // For some reason clang generates much better code when the branch is
  // structured exactly this way, rather than a sequence of checks.
  return !constraint.IsOverflowFlagSet()
             ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
                                                 : S<Dst>::Underflow())
             // Skip this check for integral Src, which cannot be NaN.
             : (std::is_integral_v<Src> || !constraint.IsUnderflowFlagSet()
                    ? S<Dst>::Overflow()
                    : S<Dst>::NaN());
}

// We can reduce the number of conditions and get slightly better performance
// for normal signed and unsigned integer ranges. And in the specific case of
// Arm, we can use the optimized saturation instructions.
template <typename Dst, typename Src>
struct SaturateFastOp {
  static constexpr bool is_supported = false;
  static constexpr Dst Do(Src) {
    // Force a compile failure if instantiated.
    return CheckOnFailure::template HandleFailure<Dst>();
  }
};

template <typename Dst, typename Src>
  requires(std::integral<Src> && std::integral<Dst> &&
           SaturateFastAsmOp<Dst, Src>::is_supported)
struct SaturateFastOp<Dst, Src> {
  static constexpr bool is_supported = true;
  static constexpr Dst Do(Src value) {
    return SaturateFastAsmOp<Dst, Src>::Do(value);
  }
};

template <typename Dst, typename Src>
  requires(std::integral<Src> && std::integral<Dst> &&
           !SaturateFastAsmOp<Dst, Src>::is_supported)
struct SaturateFastOp<Dst, Src> {
  static constexpr bool is_supported = true;
  static constexpr Dst Do(Src value) {
    // The exact order of the following is structured to hit the correct
    // optimization heuristics across compilers. Do not change without
    // checking the emitted code.
    const Dst saturated = CommonMaxOrMin<Dst, Src>(
        kIsMaxInRangeForNumericType<Dst, Src> ||
        (!kIsMinInRangeForNumericType<Dst, Src> && IsValueNegative(value)));
    if (IsValueInRangeForNumericType<Dst>(value)) [[likely]] {
      return static_cast<Dst>(value);
    }
    return saturated;
  }
};

// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate by default rather than
// overflow or underflow, and NaN assignment to an integral will return 0.
// All boundary condition behaviors can be overridden with a custom handler.
template <typename Dst,
          template <typename> class SaturationHandler = SaturationDefaultLimits,
          typename Src>
constexpr Dst saturated_cast(Src value) {
  using SrcType = UnderlyingType<Src>;
  const auto underlying_value = static_cast<SrcType>(value);
  return !std::is_constant_evaluated() &&
                 SaturateFastOp<Dst, SrcType>::is_supported &&
                 std::is_same_v<SaturationHandler<Dst>,
                                SaturationDefaultLimits<Dst>>
             ? SaturateFastOp<Dst, SrcType>::Do(underlying_value)
             : saturated_cast_impl<Dst, SaturationHandler, SrcType>(
                   underlying_value,
                   DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
                       underlying_value));
}

// strict_cast<> is analogous to static_cast<> for numeric types, except that
// it will cause a compile failure if the destination type is not large enough
// to contain any value in the source type. It performs no runtime checking.
template <typename Dst, typename Src, typename SrcType = UnderlyingType<Src>>
  requires(
      IsNumeric<Src> && std::is_arithmetic_v<Dst> &&
      // If you got here from a compiler error, it's because you tried to assign
      // from a source type to a destination type that has insufficient range.
      // The solution may be to change the destination type you're assigning to,
      // and use one large enough to represent the source.
      // Alternatively, you may be better served with the checked_cast<> or
      // saturated_cast<> template functions for your particular use case.
      kStaticDstRangeRelationToSrcRange<Dst, SrcType> ==
          NumericRangeRepresentation::kContained)
constexpr Dst strict_cast(Src value) {
  return static_cast<Dst>(static_cast<SrcType>(value));
}

// Some wrappers to statically check that a type is in range.
template <typename Dst, typename Src>
inline constexpr bool kIsNumericRangeContained = false;

template <typename Dst, typename Src>
  requires(std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Dst>> &&
           std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Src>>)
inline constexpr bool kIsNumericRangeContained<Dst, Src> =
    kStaticDstRangeRelationToSrcRange<Dst, Src> ==
    NumericRangeRepresentation::kContained;

// StrictNumeric implements compile time range checking between numeric types by
// wrapping assignment operations in a strict_cast. This class is intended to be
// used for function arguments and return types, to ensure the destination type
// can always contain the source type. This is essentially the same as enforcing
// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
// incrementally at API boundaries, making it easier to convert code so that it
// compiles cleanly with truncation warnings enabled.
// This template should introduce no runtime overhead, but it also provides no
// runtime checking of any of the associated mathematical operations. Use
// CheckedNumeric for runtime range checks of the actual value being assigned.
template <typename T>
  requires std::is_arithmetic_v<T>
class StrictNumeric {
 public:
  using type = T;

  constexpr StrictNumeric() : value_(0) {}

  // Copy constructor.
  template <typename Src>
  constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
      : value_(strict_cast<T>(rhs.value_)) {}

  // This is not an explicit constructor because we implicitly upgrade regular
  // numerics to StrictNumerics to make them easier to use.
  template <typename Src>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr StrictNumeric(Src value) : value_(strict_cast<T>(value)) {}

  // If you got here from a compiler error, it's because you tried to assign
  // from a source type to a destination type that has insufficient range.
  // The solution may be to change the destination type you're assigning to,
  // and use one large enough to represent the source.
  // If you're assigning from a CheckedNumeric<> class, you may be able to use
  // the AssignIfValid() member function, specify a narrower destination type to
  // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
  // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
  // If you've encountered an _ambiguous overload_ you can use a static_cast<>
  // to explicitly cast the result to the destination type.
  // If none of that works, you may be better served with the checked_cast<> or
  // saturated_cast<> template functions for your particular use case.
  template <typename Dst>
    requires(kIsNumericRangeContained<Dst, T>)
  constexpr operator Dst() const {  // NOLINT(google-explicit-constructor)
    return static_cast<ArithmeticOrUnderlyingEnum<Dst>>(value_);
  }

 private:
  template <typename U>
    requires std::is_arithmetic_v<U>
  friend class StrictNumeric;

  T value_;
};

template <typename T>
StrictNumeric(T) -> StrictNumeric<T>;

// Convenience wrapper returns a StrictNumeric from the provided arithmetic
// type.
template <typename T>
constexpr StrictNumeric<UnderlyingType<T>> MakeStrictNum(const T value) {
  return value;
}

#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)                    \
  template <typename L, typename R>                                           \
    requires(internal::Is##CLASS##Op<L, R>)                                   \
  constexpr bool operator OP(L lhs, R rhs) {                                  \
    return SafeCompare<NAME, UnderlyingType<L>, UnderlyingType<R>>(lhs, rhs); \
  }

BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)

}  // namespace internal

using internal::as_signed;
using internal::as_unsigned;
using internal::checked_cast;
using internal::IsValueInRangeForNumericType;
using internal::IsValueNegative;
using internal::kIsTypeInRangeForNumericType;
using internal::MakeStrictNum;
using internal::SafeUnsignedAbs;
using internal::saturated_cast;
using internal::strict_cast;
using internal::StrictNumeric;

// Explicitly make a shorter size_t alias for convenience.
using SizeT = StrictNumeric<size_t>;

// floating -> integral conversions that saturate and thus can actually return
// an integral type.
//
// Generally, what you want is saturated_cast<Dst>(std::nearbyint(x)), which
// rounds correctly according to IEEE-754 (round to nearest, ties go to nearest
// even number; this avoids bias). If your code is performance-critical
// and you are sure that you will never overflow, you can use std::lrint()
// or std::llrint(), which return a long or long long directly.
//
// Below are convenience functions around similar patterns, except that
// they round in nonstandard directions and will generally be slower.

// Rounds towards negative infinity (i.e., down).
template <typename Dst = int, typename Src>
  requires(std::integral<Dst> && std::floating_point<Src>)
Dst ClampFloor(Src value) {
  return saturated_cast<Dst>(std::floor(value));
}

// Rounds towards positive infinity (i.e., up).
template <typename Dst = int, typename Src>
  requires(std::integral<Dst> && std::floating_point<Src>)
Dst ClampCeil(Src value) {
  return saturated_cast<Dst>(std::ceil(value));
}

// Rounds towards nearest integer, with ties away from zero.
// This means that 0.5 will be rounded to 1 and 1.5 will be rounded to 2.
// Similarly, -0.5 will be rounded to -1 and -1.5 will be rounded to -2.
//
// This is normally not what you want accuracy-wise (it introduces a small bias
// away from zero), and it is not the fastest option, but it is frequently what
// existing code expects. Compare with saturated_cast<Dst>(std::nearbyint(x))
// or std::lrint(x), which would round 0.5 and -0.5 to 0 but 1.5 to 2 and
// -1.5 to -2.
template <typename Dst = int, typename Src>
  requires(std::integral<Dst> && std::floating_point<Src>)
Dst ClampRound(Src value) {
  const Src rounded = std::round(value);
  return saturated_cast<Dst>(rounded);
}

}  // namespace base

#endif  // BASE_NUMERICS_SAFE_CONVERSIONS_H_