chromium/third_party/pdfium/core/fxcrt/numerics/checked_math_impl.h

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

#ifndef CORE_FXCRT_NUMERICS_CHECKED_MATH_IMPL_H_
#define CORE_FXCRT_NUMERICS_CHECKED_MATH_IMPL_H_

#include <stddef.h>
#include <stdint.h>

#include <climits>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>

#include "core/fxcrt/numerics/safe_conversions.h"
#include "core/fxcrt/numerics/safe_math_shared_impl.h"

namespace pdfium {
namespace internal {

template <typename T>
constexpr bool CheckedAddImpl(T x, T y, T* result) {}

template <typename T, typename U, class Enable = void>
struct CheckedAddOp {};

CheckedAddOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T>
constexpr bool CheckedSubImpl(T x, T y, T* result) {}

template <typename T, typename U, class Enable = void>
struct CheckedSubOp {};

CheckedSubOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T>
constexpr bool CheckedMulImpl(T x, T y, T* result) {}

template <typename T, typename U, class Enable = void>
struct CheckedMulOp {};

CheckedMulOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

// Division just requires a check for a zero denominator or an invalid negation
// on signed min/-1.
template <typename T, typename U, class Enable = void>
struct CheckedDivOp {};

CheckedDivOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T, typename U, class Enable = void>
struct CheckedModOp {};

CheckedModOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T, typename U, class Enable = void>
struct CheckedLshOp {};

// Left shift. Shifts less than 0 or greater than or equal to the number
// of bits in the promoted type are undefined. Shifts of negative values
// are undefined. Otherwise it is defined when the result fits.
CheckedLshOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T, typename U, class Enable = void>
struct CheckedRshOp {};

// Right shift. Shifts less than 0 or greater than or equal to the number
// of bits in the promoted type are undefined. Otherwise, it is always defined,
// but a right shift of a negative value is implementation-dependent.
CheckedRshOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T, typename U, class Enable = void>
struct CheckedAndOp {};

// For simplicity we support only unsigned integer results.
CheckedAndOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T, typename U, class Enable = void>
struct CheckedOrOp {};

// For simplicity we support only unsigned integers.
CheckedOrOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

template <typename T, typename U, class Enable = void>
struct CheckedXorOp {};

// For simplicity we support only unsigned integers.
CheckedXorOp<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type>;

// Max doesn't really need to be implemented this way because it can't fail,
// but it makes the code much cleaner to use the MathOp wrappers.
template <typename T, typename U, class Enable = void>
struct CheckedMaxOp {};

CheckedMaxOp<T, U, typename std::enable_if<std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>::type>;

// Min doesn't really need to be implemented this way because it can't fail,
// but it makes the code much cleaner to use the MathOp wrappers.
template <typename T, typename U, class Enable = void>
struct CheckedMinOp {};

CheckedMinOp<T, U, typename std::enable_if<std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>::type>;

// This is just boilerplate that wraps the standard floating point arithmetic.
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
#define BASE_FLOAT_ARITHMETIC_OPS

BASE_FLOAT_ARITHMETIC_OPS
BASE_FLOAT_ARITHMETIC_OPS
BASE_FLOAT_ARITHMETIC_OPS
BASE_FLOAT_ARITHMETIC_OPS

#undef BASE_FLOAT_ARITHMETIC_OPS

// Floats carry around their validity state with them, but integers do not. So,
// we wrap the underlying value in a specialization in order to hide that detail
// and expose an interface via accessors.
enum NumericRepresentation {};

template <typename NumericType>
struct GetNumericRepresentation {};

template <typename T,
          NumericRepresentation type = GetNumericRepresentation<T>::value>
class CheckedNumericState {};

// Integrals require quite a bit of additional housekeeping to manage state.
CheckedNumericState<T, NUMERIC_INTEGER>;

// Floating points maintain their own validity, but need translation wrappers.
CheckedNumericState<T, NUMERIC_FLOATING>;

}  // namespace internal
}  // namespace pdfium

#endif  // CORE_FXCRT_NUMERICS_CHECKED_MATH_IMPL_H_