From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- mfbt/Casting.h | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 mfbt/Casting.h (limited to 'mfbt/Casting.h') diff --git a/mfbt/Casting.h b/mfbt/Casting.h new file mode 100644 index 0000000000..ebb0e8bc51 --- /dev/null +++ b/mfbt/Casting.h @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Cast operations to supplement the built-in casting operations. */ + +#ifndef mozilla_Casting_h +#define mozilla_Casting_h + +#include "mozilla/Assertions.h" + +#include +#include +#include +#include + +namespace mozilla { + +/** + * Sets the outparam value of type |To| with the same underlying bit pattern of + * |aFrom|. + * + * |To| and |From| must be types of the same size; be careful of cross-platform + * size differences, or this might fail to compile on some but not all + * platforms. + * + * There is also a variant that returns the value directly. In most cases, the + * two variants should be identical. However, in the specific case of x86 + * chips, the behavior differs: returning floating-point values directly is done + * through the x87 stack, and x87 loads and stores turn signaling NaNs into + * quiet NaNs... silently. Returning floating-point values via outparam, + * however, is done entirely within the SSE registers when SSE2 floating-point + * is enabled in the compiler, which has semantics-preserving behavior you would + * expect. + * + * If preserving the distinction between signaling NaNs and quiet NaNs is + * important to you, you should use the outparam version. In all other cases, + * you should use the direct return version. + */ +template +inline void BitwiseCast(const From aFrom, To* aResult) { + static_assert(sizeof(From) == sizeof(To), + "To and From must have the same size"); + + // We could maybe downgrade these to std::is_trivially_copyable, but the + // various STLs we use don't all provide it. + static_assert(std::is_trivial::value, + "shouldn't bitwise-copy a type having non-trivial " + "initialization"); + static_assert(std::is_trivial::value, + "shouldn't bitwise-copy a type having non-trivial " + "initialization"); + + std::memcpy(static_cast(aResult), static_cast(&aFrom), + sizeof(From)); +} + +template +inline To BitwiseCast(const From aFrom) { + To temp; + BitwiseCast(aFrom, &temp); + return temp; +} + +namespace detail { + +template +constexpr int64_t safe_integer() { + static_assert(std::is_floating_point_v); + return std::pow(2, std::numeric_limits::digits); +} + +template +constexpr uint64_t safe_integer_unsigned() { + static_assert(std::is_floating_point_v); + return std::pow(2, std::numeric_limits::digits); +} + +// This is working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81676, +// fixed in gcc-10 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +template +bool IsInBounds(In aIn) { + constexpr bool inSigned = std::is_signed_v; + constexpr bool outSigned = std::is_signed_v; + constexpr bool bothSigned = inSigned && outSigned; + constexpr bool bothUnsigned = !inSigned && !outSigned; + constexpr bool inFloat = std::is_floating_point_v; + constexpr bool outFloat = std::is_floating_point_v; + constexpr bool bothFloat = inFloat && outFloat; + constexpr bool noneFloat = !inFloat && !outFloat; + constexpr Out outMax = std::numeric_limits::max(); + constexpr Out outMin = std::numeric_limits::lowest(); + + // This selects the widest of two types, and is used to cast throughout. + using select_widest = std::conditional_t<(sizeof(In) > sizeof(Out)), In, Out>; + + if constexpr (bothFloat) { + if (aIn > select_widest(outMax) || aIn < select_widest(outMin)) { + return false; + } + } + // Normal casting applies, the floating point number is floored. + if constexpr (inFloat && !outFloat) { + static_assert(sizeof(aIn) <= sizeof(int64_t)); + // Check if the input floating point is larger than the output bounds. This + // catches situations where the input is a float larger than the max of the + // output type. + if (aIn < static_cast(outMin) || + aIn > static_cast(outMax)) { + return false; + } + // At this point we know that the input can be converted to an integer. + // Check if it's larger than the bounds of the target integer. + if (outSigned) { + int64_t asInteger = static_cast(aIn); + if (asInteger < outMin || asInteger > outMax) { + return false; + } + } else { + uint64_t asInteger = static_cast(aIn); + if (asInteger > outMax) { + return false; + } + } + } + + // Checks if the integer is representable exactly as a floating point value of + // a specific width. + if constexpr (!inFloat && outFloat) { + if constexpr (inSigned) { + if (aIn < -safe_integer() || aIn > safe_integer()) { + return false; + } + } else { + if (aIn >= safe_integer_unsigned()) { + return false; + } + } + } + + if constexpr (noneFloat) { + if constexpr (bothUnsigned) { + if (aIn > select_widest(outMax)) { + return false; + } + } + if constexpr (bothSigned) { + if (aIn > select_widest(outMax) || aIn < select_widest(outMin)) { + return false; + } + } + if constexpr (inSigned && !outSigned) { + if (aIn < 0 || std::make_unsigned_t(aIn) > outMax) { + return false; + } + } + if constexpr (!inSigned && outSigned) { + if (aIn > select_widest(outMax)) { + return false; + } + } + } + return true; +} +#pragma GCC diagnostic pop + +} // namespace detail + +/** + * Cast a value of type |From| to a value of type |To|, asserting that the cast + * will be a safe cast per C++ (that is, that |to| is in the range of values + * permitted for the type |From|). + * In particular, this will fail if a integer cannot be represented exactly as a + * floating point value, because it's too large. + */ +template +inline To AssertedCast(const From aFrom) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v); + MOZ_ASSERT((detail::IsInBounds(aFrom))); + return static_cast(aFrom); +} + +/** + * Cast a value of numeric type |From| to a value of numeric type |To|, release + * asserting that the cast will be a safe cast per C++ (that is, that |to| is in + * the range of values permitted for the type |From|). + * In particular, this will fail if a integer cannot be represented exactly as a + * floating point value, because it's too large. + */ +template +inline To ReleaseAssertedCast(const From aFrom) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v); + MOZ_RELEASE_ASSERT((detail::IsInBounds(aFrom))); + return static_cast(aFrom); +} + +namespace detail { + +template +class LazyAssertedCastT final { + const From mVal; + + public: + explicit LazyAssertedCastT(const From val) : mVal(val) {} + + template + operator To() const { + return AssertedCast(mVal); + } +}; + +} // namespace detail + +/** + * Like AssertedCast, but infers |To| for AssertedCast lazily based on usage. + * > uint8_t foo = LazyAssertedCast(1000); // boom + */ +template +inline auto LazyAssertedCast(const From val) { + return detail::LazyAssertedCastT(val); +} + +} // namespace mozilla + +#endif /* mozilla_Casting_h */ -- cgit v1.2.3