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

#ifndef BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_

#include <cassert>
#include <limits>
#include <type_traits>

#include "anglebase/numerics/safe_conversions.h"

#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
#    include "anglebase/numerics/safe_math_arm_impl.h"
#    define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
#else
#    define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
#endif

namespace angle
{
namespace base
{
namespace internal
{

// These are the non-functioning boilerplate implementations of the optimized
// safe math routines.
#if !BASE_HAS_ASSEMBLER_SAFE_MATH
template <typename T, typename U>
struct CheckedMulFastAsmOp
{
    static const bool is_supported = false;
    template <typename V>
    static constexpr bool Do(T, U, V *)
    {
        // Force a compile failure if instantiated.
        return CheckOnFailure::template HandleFailure<bool>();
    }
};

template <typename T, typename U>
struct ClampedAddFastAsmOp
{
    static const bool is_supported = false;
    template <typename V>
    static constexpr V Do(T, U)
    {
        // Force a compile failure if instantiated.
        return CheckOnFailure::template HandleFailure<V>();
    }
};

template <typename T, typename U>
struct ClampedSubFastAsmOp
{
    static const bool is_supported = false;
    template <typename V>
    static constexpr V Do(T, U)
    {
        // Force a compile failure if instantiated.
        return CheckOnFailure::template HandleFailure<V>();
    }
};

template <typename T, typename U>
struct ClampedMulFastAsmOp
{
    static const bool is_supported = false;
    template <typename V>
    static constexpr V Do(T, U)
    {
        // Force a compile failure if instantiated.
        return CheckOnFailure::template HandleFailure<V>();
    }
};
#endif  // BASE_HAS_ASSEMBLER_SAFE_MATH
#undef BASE_HAS_ASSEMBLER_SAFE_MATH

template <typename T, typename U>
struct CheckedAddFastOp
{
    static const bool is_supported = true;
    template <typename V>
    __attribute__((always_inline)) static constexpr bool Do(T x, U y, V *result)
    {
        return !__builtin_add_overflow(x, y, result);
    }
};

template <typename T, typename U>
struct CheckedSubFastOp
{
    static const bool is_supported = true;
    template <typename V>
    __attribute__((always_inline)) static constexpr bool Do(T x, U y, V *result)
    {
        return !__builtin_sub_overflow(x, y, result);
    }
};

template <typename T, typename U>
struct CheckedMulFastOp
{
#if defined(__clang__)
    // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
    // support full-width, mixed-sign multiply builtins.
    // https://crbug.com/613003
    // We can support intptr_t, uintptr_t, or a smaller common type.
    static const bool is_supported = (IsTypeInRangeForNumericType<intptr_t, T>::value &&
                                      IsTypeInRangeForNumericType<intptr_t, U>::value) ||
                                     (IsTypeInRangeForNumericType<uintptr_t, T>::value &&
                                      IsTypeInRangeForNumericType<uintptr_t, U>::value);
#else
    static const bool is_supported = true;
#endif
    template <typename V>
    __attribute__((always_inline)) static constexpr bool Do(T x, U y, V *result)
    {
        return CheckedMulFastAsmOp<T, U>::is_supported ? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
                                                       : !__builtin_mul_overflow(x, y, result);
    }
};

template <typename T, typename U>
struct ClampedAddFastOp
{
    static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
    template <typename V>
    __attribute__((always_inline)) static V Do(T x, U y)
    {
        return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
    }
};

template <typename T, typename U>
struct ClampedSubFastOp
{
    static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
    template <typename V>
    __attribute__((always_inline)) static V Do(T x, U y)
    {
        return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
    }
};

template <typename T, typename U>
struct ClampedMulFastOp
{
    static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
    template <typename V>
    __attribute__((always_inline)) static V Do(T x, U y)
    {
        return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
    }
};

template <typename T>
struct ClampedNegFastOp
{
    static const bool is_supported = std::is_signed<T>::value;
    __attribute__((always_inline)) static T Do(T value)
    {
        // Use this when there is no assembler path available.
        if (!ClampedSubFastAsmOp<T, T>::is_supported)
        {
            T result;
            return !__builtin_sub_overflow(T(0), value, &result) ? result
                                                                 : std::numeric_limits<T>::max();
        }

        // Fallback to the normal subtraction path.
        return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
    }
};

}  // namespace internal
}  // namespace base
}  // namespace angle

#endif  // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
