include/onlineupdate/mozilla/ArrayUtils.h | 188 + include/onlineupdate/mozilla/Casting.h | 229 + include/onlineupdate/mozilla/DebugOnly.h | 102 include/onlineupdate/mozilla/DynamicallyLinkedFunctionPtr.h | 137 include/onlineupdate/mozilla/EndianUtils.h | 611 ++++ include/onlineupdate/mozilla/HashFunctions.h | 417 +++ include/onlineupdate/mozilla/MathAlgorithms.h | 492 +++ include/onlineupdate/mozilla/MemoryReporting.h | 30 include/onlineupdate/mozilla/ReentrancyGuard.h | 50 include/onlineupdate/mozilla/Result.h | 873 ++++++ include/onlineupdate/mozilla/ResultVariant.h | 61 include/onlineupdate/mozilla/Span.h | 973 +++++++ include/onlineupdate/mozilla/Variant.h | 928 ++++++ include/onlineupdate/mozilla/Vector.h | 1653 ++++++++++++ include/onlineupdate/mozilla/WinHeaderOnlyUtils.h | 826 +++++ include/onlineupdate/mozilla/WrappingOperations.h | 262 + onlineupdate/Executable_update_service.mk | 8 onlineupdate/Module_onlineupdate.mk | 3 onlineupdate/StaticLibrary_libmarverify.mk | 5 onlineupdate/source/service/nsAutoRef.h | 489 +++ onlineupdate/source/service/nsCharTraits.h | 486 +++ onlineupdate/source/service/nsUTF8Utils.h | 247 + onlineupdate/source/service/nsWindowsHelpers.h | 339 ++ onlineupdate/source/service/nsWindowsRestart.cpp | 197 + 24 files changed, 9602 insertions(+), 4 deletions(-)
New commits: commit 67b8ad26b868c6de69b0aa2d1f11ccf66cb1a2dd Author: Thorsten Behrens <thorsten.behr...@allotropia.de> AuthorDate: Thu Dec 7 01:02:40 2023 +0000 Commit: Thorsten Behrens <thorsten.behr...@allotropia.de> CommitDate: Thu Dec 7 02:06:41 2023 +0100 Missing headers Change-Id: I1a68e8f84df2c216c3507704d06131c4bfd20747 diff --git a/include/onlineupdate/mozilla/ArrayUtils.h b/include/onlineupdate/mozilla/ArrayUtils.h new file mode 100644 index 000000000000..0d55bb1f6546 --- /dev/null +++ b/include/onlineupdate/mozilla/ArrayUtils.h @@ -0,0 +1,188 @@ +/* -*- 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/. */ + +/* + * Implements various helper functions related to arrays. + */ + +#ifndef mozilla_ArrayUtils_h +#define mozilla_ArrayUtils_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +# include <algorithm> +# include <type_traits> + +# include "mozilla/Alignment.h" + +namespace mozilla { + +template <typename T, size_t Length> +class Array; +template <typename IndexType, IndexType SizeAsEnumValue, typename ValueType> +class EnumeratedArray; + +/* + * Safely subtract two pointers when it is known that aEnd >= aBegin, yielding a + * size_t result. + * + * Ordinary pointer subtraction yields a ptrdiff_t result, which, being signed, + * has insufficient range to express the distance between pointers at opposite + * ends of the address space. Furthermore, most compilers use ptrdiff_t to + * represent the intermediate byte address distance, before dividing by + * sizeof(T); if that intermediate result overflows, they'll produce results + * with the wrong sign even when the correct scaled distance would fit in a + * ptrdiff_t. + */ +template <class T> +MOZ_ALWAYS_INLINE size_t PointerRangeSize(T* aBegin, T* aEnd) { + MOZ_ASSERT(aEnd >= aBegin); + return (size_t(aEnd) - size_t(aBegin)) / sizeof(T); +} + +/* + * Compute the length of an array with constant length. (Use of this method + * with a non-array pointer will not compile.) + * + * Beware of the implicit trailing '\0' when using this with string constants. + */ +template <typename T, size_t N> +constexpr size_t ArrayLength(T (&aArr)[N]) { + return N; +} + +template <typename T, size_t N> +constexpr size_t ArrayLength(const Array<T, N>& aArr) { + return N; +} + +template <typename E, E N, typename T> +constexpr size_t ArrayLength(const EnumeratedArray<E, N, T>& aArr) { + return size_t(N); +} + +/* + * Compute the address one past the last element of a constant-length array. + * + * Beware of the implicit trailing '\0' when using this with string constants. + */ +template <typename T, size_t N> +constexpr T* ArrayEnd(T (&aArr)[N]) { + return aArr + ArrayLength(aArr); +} + +template <typename T, size_t N> +constexpr T* ArrayEnd(Array<T, N>& aArr) { + return &aArr[0] + ArrayLength(aArr); +} + +template <typename T, size_t N> +constexpr const T* ArrayEnd(const Array<T, N>& aArr) { + return &aArr[0] + ArrayLength(aArr); +} + +/** + * std::equal has subpar ergonomics. + */ + +template <typename T, typename U, size_t N> +bool ArrayEqual(const T (&a)[N], const U (&b)[N]) { + return std::equal(a, a + N, b); +} + +template <typename T, typename U> +bool ArrayEqual(const T* const a, const U* const b, const size_t n) { + return std::equal(a, a + n, b); +} + +namespace detail { + +template <typename AlignType, typename Pointee, typename = void> +struct AlignedChecker { + static void test(const Pointee* aPtr) { + MOZ_ASSERT((uintptr_t(aPtr) % MOZ_ALIGNOF(AlignType)) == 0, + "performing a range-check with a misaligned pointer"); + } +}; + +template <typename AlignType, typename Pointee> +struct AlignedChecker<AlignType, Pointee, + std::enable_if_t<std::is_void_v<AlignType>>> { + static void test(const Pointee* aPtr) {} +}; + +} // namespace detail + +/** + * Determines whether |aPtr| points at an object in the range [aBegin, aEnd). + * + * |aPtr| must have the same alignment as |aBegin| and |aEnd|. This usually + * should be achieved by ensuring |aPtr| points at a |U|, not just that it + * points at a |T|. + * + * It is a usage error for any argument to be misaligned. + * + * It's okay for T* to be void*, and if so U* may also be void*. In the latter + * case no argument is required to be aligned (obviously, as void* implies no + * particular alignment). + */ +template <typename T, typename U> +inline std::enable_if_t<std::is_same_v<T, U> || std::is_base_of<T, U>::value || + std::is_void_v<T>, + bool> +IsInRange(const T* aPtr, const U* aBegin, const U* aEnd) { + MOZ_ASSERT(aBegin <= aEnd); + detail::AlignedChecker<U, T>::test(aPtr); + detail::AlignedChecker<U, U>::test(aBegin); + detail::AlignedChecker<U, U>::test(aEnd); + return aBegin <= reinterpret_cast<const U*>(aPtr) && + reinterpret_cast<const U*>(aPtr) < aEnd; +} + +/** + * Convenience version of the above method when the valid range is specified as + * uintptr_t values. As above, |aPtr| must be aligned, and |aBegin| and |aEnd| + * must be aligned with respect to |T|. + */ +template <typename T> +inline bool IsInRange(const T* aPtr, uintptr_t aBegin, uintptr_t aEnd) { + return IsInRange(aPtr, reinterpret_cast<const T*>(aBegin), + reinterpret_cast<const T*>(aEnd)); +} + +namespace detail { + +/* + * Helper for the MOZ_ARRAY_LENGTH() macro to make the length a typesafe + * compile-time constant even on compilers lacking constexpr support. + */ +template <typename T, size_t N> +char (&ArrayLengthHelper(T (&array)[N]))[N]; + +} /* namespace detail */ + +} /* namespace mozilla */ + +#endif /* __cplusplus */ + +/* + * MOZ_ARRAY_LENGTH() is an alternative to mozilla::ArrayLength() for C files + * that can't use C++ template functions and for static_assert() calls that + * can't call ArrayLength() when it is not a C++11 constexpr function. + */ +#ifdef __cplusplus +# define MOZ_ARRAY_LENGTH(array) \ + sizeof(mozilla::detail::ArrayLengthHelper(array)) +#else +# define MOZ_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) +#endif + +#endif /* mozilla_ArrayUtils_h */ diff --git a/include/onlineupdate/mozilla/Casting.h b/include/onlineupdate/mozilla/Casting.h new file mode 100644 index 000000000000..ebb0e8bc512b --- /dev/null +++ b/include/onlineupdate/mozilla/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 <cstring> +#include <type_traits> +#include <limits> +#include <cmath> + +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 <typename To, typename From> +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<From>::value, + "shouldn't bitwise-copy a type having non-trivial " + "initialization"); + static_assert(std::is_trivial<To>::value, + "shouldn't bitwise-copy a type having non-trivial " + "initialization"); + + std::memcpy(static_cast<void*>(aResult), static_cast<const void*>(&aFrom), + sizeof(From)); +} + +template <typename To, typename From> +inline To BitwiseCast(const From aFrom) { + To temp; + BitwiseCast<To, From>(aFrom, &temp); + return temp; +} + +namespace detail { + +template <typename T> +constexpr int64_t safe_integer() { + static_assert(std::is_floating_point_v<T>); + return std::pow(2, std::numeric_limits<T>::digits); +} + +template <typename T> +constexpr uint64_t safe_integer_unsigned() { + static_assert(std::is_floating_point_v<T>); + return std::pow(2, std::numeric_limits<T>::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 <typename In, typename Out> +bool IsInBounds(In aIn) { + constexpr bool inSigned = std::is_signed_v<In>; + constexpr bool outSigned = std::is_signed_v<Out>; + constexpr bool bothSigned = inSigned && outSigned; + constexpr bool bothUnsigned = !inSigned && !outSigned; + constexpr bool inFloat = std::is_floating_point_v<In>; + constexpr bool outFloat = std::is_floating_point_v<Out>; + constexpr bool bothFloat = inFloat && outFloat; + constexpr bool noneFloat = !inFloat && !outFloat; + constexpr Out outMax = std::numeric_limits<Out>::max(); + constexpr Out outMin = std::numeric_limits<Out>::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<double>(outMin) || + aIn > static_cast<double>(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<int64_t>(aIn); + if (asInteger < outMin || asInteger > outMax) { + return false; + } + } else { + uint64_t asInteger = static_cast<uint64_t>(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<Out>() || aIn > safe_integer<Out>()) { + return false; + } + } else { + if (aIn >= safe_integer_unsigned<Out>()) { + 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<In>(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 <typename To, typename From> +inline To AssertedCast(const From aFrom) { + static_assert(std::is_arithmetic_v<To> && std::is_arithmetic_v<From>); + MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom))); + return static_cast<To>(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 <typename To, typename From> +inline To ReleaseAssertedCast(const From aFrom) { + static_assert(std::is_arithmetic_v<To> && std::is_arithmetic_v<From>); + MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom))); + return static_cast<To>(aFrom); +} + +namespace detail { + +template <typename From> +class LazyAssertedCastT final { + const From mVal; + + public: + explicit LazyAssertedCastT(const From val) : mVal(val) {} + + template <typename To> + operator To() const { + return AssertedCast<To>(mVal); + } +}; + +} // namespace detail + +/** + * Like AssertedCast, but infers |To| for AssertedCast lazily based on usage. + * > uint8_t foo = LazyAssertedCast(1000); // boom + */ +template <typename From> +inline auto LazyAssertedCast(const From val) { + return detail::LazyAssertedCastT<From>(val); +} + +} // namespace mozilla + +#endif /* mozilla_Casting_h */ diff --git a/include/onlineupdate/mozilla/DebugOnly.h b/include/onlineupdate/mozilla/DebugOnly.h new file mode 100644 index 000000000000..0441685735bd --- /dev/null +++ b/include/onlineupdate/mozilla/DebugOnly.h @@ -0,0 +1,102 @@ +/* -*- 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/. */ + +/* + * Provides DebugOnly, a type for variables used only in debug builds (i.e. by + * assertions). + */ + +#ifndef mozilla_DebugOnly_h +#define mozilla_DebugOnly_h + +#include "mozilla/Attributes.h" + +#include <utility> + +namespace mozilla { + +/** + * DebugOnly contains a value of type T, but only in debug builds. In release + * builds, it does not contain a value. This helper is intended to be used with + * MOZ_ASSERT()-style macros, allowing one to write: + * + * DebugOnly<bool> check = func(); + * MOZ_ASSERT(check); + * + * more concisely than declaring |check| conditional on #ifdef DEBUG. + * + * DebugOnly instances can only be coerced to T in debug builds. In release + * builds they don't have a value, so type coercion is not well defined. + * + * NOTE: DebugOnly instances still take up one byte of space, plus padding, even + * in optimized, non-DEBUG builds (see bug 1253094 comment 37 for more info). + * For this reason the class is MOZ_STACK_CLASS to prevent consumers using + * DebugOnly for struct/class members and unwittingly inflating the size of + * their objects in release builds. + */ +template <typename T> +class MOZ_STACK_CLASS DebugOnly { + public: +#ifdef DEBUG + T value; + + DebugOnly() = default; + MOZ_IMPLICIT DebugOnly(T&& aOther) : value(std::move(aOther)) {} + MOZ_IMPLICIT DebugOnly(const T& aOther) : value(aOther) {} + DebugOnly(const DebugOnly& aOther) : value(aOther.value) {} + DebugOnly& operator=(const T& aRhs) { + value = aRhs; + return *this; + } + DebugOnly& operator=(T&& aRhs) { + value = std::move(aRhs); + return *this; + } + + void operator++(int) { value++; } + void operator--(int) { value--; } + + // Do not define operator+=(), etc. here. These will coerce via the + // implicit cast and built-in operators. Defining explicit methods here + // will create ambiguity the compiler can't deal with. + + T* operator&() { return &value; } + + operator T&() { return value; } + operator const T&() const { return value; } + + T& operator->() { return value; } + const T& operator->() const { return value; } + + const T& inspect() const { return value; } + +#else + DebugOnly() = default; + MOZ_IMPLICIT DebugOnly(const T&) {} + DebugOnly(const DebugOnly&) {} + DebugOnly& operator=(const T&) { return *this; } + MOZ_IMPLICIT DebugOnly(T&&) {} + DebugOnly& operator=(T&&) { return *this; } + void operator++(int) {} + void operator--(int) {} + DebugOnly& operator+=(const T&) { return *this; } + DebugOnly& operator-=(const T&) { return *this; } + DebugOnly& operator&=(const T&) { return *this; } + DebugOnly& operator|=(const T&) { return *this; } + DebugOnly& operator^=(const T&) { return *this; } +#endif + + /* + * DebugOnly must always have a user-defined destructor or else it will + * generate "unused variable" warnings, exactly what it's intended + * to avoid! + */ + ~DebugOnly() {} +}; + +} // namespace mozilla + +#endif /* mozilla_DebugOnly_h */ diff --git a/include/onlineupdate/mozilla/DynamicallyLinkedFunctionPtr.h b/include/onlineupdate/mozilla/DynamicallyLinkedFunctionPtr.h new file mode 100644 index 000000000000..4313974ec5d4 --- /dev/null +++ b/include/onlineupdate/mozilla/DynamicallyLinkedFunctionPtr.h @@ -0,0 +1,137 @@ +/* -*- 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/. */ + +#ifndef mozilla_DynamicallyLinkedFunctionPtr_h +#define mozilla_DynamicallyLinkedFunctionPtr_h + +#include <windows.h> + +#include <utility> + +#include "mozilla/Attributes.h" + +namespace mozilla { +namespace detail { + +template <typename T> +struct FunctionPtrCracker; + +template <typename R, typename... Args> +struct FunctionPtrCracker<R (*)(Args...)> { + using ReturnT = R; + using FunctionPtrT = R (*)(Args...); +}; + +#if defined(_M_IX86) +template <typename R, typename... Args> +struct FunctionPtrCracker<R(__stdcall*)(Args...)> { + using ReturnT = R; + using FunctionPtrT = R(__stdcall*)(Args...); +}; + +template <typename R, typename... Args> +struct FunctionPtrCracker<R(__fastcall*)(Args...)> { + using ReturnT = R; + using FunctionPtrT = R(__fastcall*)(Args...); +}; +#endif // defined(_M_IX86) + +template <typename T> +class DynamicallyLinkedFunctionPtrBase { + public: + using ReturnT = typename FunctionPtrCracker<T>::ReturnT; + using FunctionPtrT = typename FunctionPtrCracker<T>::FunctionPtrT; + + DynamicallyLinkedFunctionPtrBase(const wchar_t* aLibName, + const char* aFuncName) + : mModule(::LoadLibraryW(aLibName)), mFunction(nullptr) { + if (!mModule) { + return; + } + + mFunction = + reinterpret_cast<FunctionPtrT>(::GetProcAddress(mModule, aFuncName)); + + if (!mFunction) { + // Since the function doesn't exist, there is no point in holding a + // reference to mModule anymore. + ::FreeLibrary(mModule); + mModule = nullptr; + } + } + + DynamicallyLinkedFunctionPtrBase(const DynamicallyLinkedFunctionPtrBase&) = + delete; + DynamicallyLinkedFunctionPtrBase& operator=( + const DynamicallyLinkedFunctionPtrBase&) = delete; + + DynamicallyLinkedFunctionPtrBase(DynamicallyLinkedFunctionPtrBase&&) = delete; + DynamicallyLinkedFunctionPtrBase& operator=( + DynamicallyLinkedFunctionPtrBase&&) = delete; + + template <typename... Args> + ReturnT operator()(Args&&... args) const { + return mFunction(std::forward<Args>(args)...); + } + + explicit operator bool() const { return !!mFunction; } + + protected: + HMODULE mModule; + FunctionPtrT mFunction; +}; + +} // namespace detail + +/** + * In most cases, this class is the one that you want to use for resolving a + * dynamically-linked function pointer. It should be instantiated as a static + * local variable. + * + * NB: It has a trivial destructor, so the DLL that is loaded is never freed. + * Assuming that this function is called fairly often, this is the most + * sensible option. OTOH, if the function you are calling is a one-off, or the + * static local requirement is too restrictive, use DynamicallyLinkedFunctionPtr + * instead. + */ +template <typename T> +class MOZ_STATIC_LOCAL_CLASS StaticDynamicallyLinkedFunctionPtr final + : public detail::DynamicallyLinkedFunctionPtrBase<T> { + public: + StaticDynamicallyLinkedFunctionPtr(const wchar_t* aLibName, + const char* aFuncName) + : detail::DynamicallyLinkedFunctionPtrBase<T>(aLibName, aFuncName) {} + + /** + * We only offer this operator for the static local case, as it is not + * possible for this object to be destroyed while the returned pointer is + * being held. + */ + operator typename detail::DynamicallyLinkedFunctionPtrBase<T>::FunctionPtrT() + const { + return this->mFunction; + } +}; + +template <typename T> +class MOZ_NON_PARAM MOZ_NON_TEMPORARY_CLASS DynamicallyLinkedFunctionPtr final + : public detail::DynamicallyLinkedFunctionPtrBase<T> { + public: + DynamicallyLinkedFunctionPtr(const wchar_t* aLibName, const char* aFuncName) + : detail::DynamicallyLinkedFunctionPtrBase<T>(aLibName, aFuncName) {} + + ~DynamicallyLinkedFunctionPtr() { + if (!this->mModule) { + return; + } + + ::FreeLibrary(this->mModule); + } +}; + +} // namespace mozilla + +#endif // mozilla_DynamicallyLinkedFunctionPtr_h diff --git a/include/onlineupdate/mozilla/EndianUtils.h b/include/onlineupdate/mozilla/EndianUtils.h new file mode 100644 index 000000000000..b6f3e2c315d0 --- /dev/null +++ b/include/onlineupdate/mozilla/EndianUtils.h @@ -0,0 +1,611 @@ +/* -*- 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/. */ + +/* Functions for reading and writing integers in various endiannesses. */ + +/* + * The classes LittleEndian and BigEndian expose static methods for + * reading and writing 16-, 32-, and 64-bit signed and unsigned integers + * in their respective endianness. The addresses read from or written + * to may be misaligned (although misaligned accesses may incur + * architecture-specific performance costs). The naming scheme is: + * + * {Little,Big}Endian::{read,write}{Uint,Int}<bitsize> + * + * For instance, LittleEndian::readInt32 will read a 32-bit signed + * integer from memory in little endian format. Similarly, + * BigEndian::writeUint16 will write a 16-bit unsigned integer to memory + * in big-endian format. + * + * The class NativeEndian exposes methods for conversion of existing + * data to and from the native endianness. These methods are intended + * for cases where data needs to be transferred, serialized, etc. + * swap{To,From}{Little,Big}Endian byteswap a single value if necessary. + * Bulk conversion functions are also provided which optimize the + * no-conversion-needed case: + * + * - copyAndSwap{To,From}{Little,Big}Endian; + * - swap{To,From}{Little,Big}EndianInPlace. + * + * The *From* variants are intended to be used for reading data and the + * *To* variants for writing data. + * + * Methods on NativeEndian work with integer data of any type. + * Floating-point data is not supported. + * + * For clarity in networking code, "Network" may be used as a synonym + * for "Big" in any of the above methods or class names. + * + * As an example, reading a file format header whose fields are stored + * in big-endian format might look like: + * + * class ExampleHeader + * { + * private: + * uint32_t mMagic; + * uint32_t mLength; + * uint32_t mTotalRecords; + * uint64_t mChecksum; + * + * public: + * ExampleHeader(const void* data) + * { + * const uint8_t* ptr = static_cast<const uint8_t*>(data); + * mMagic = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t); + * mLength = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t); + * mTotalRecords = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t); + * mChecksum = BigEndian::readUint64(ptr); + * } + * ... + * }; + */ + +#ifndef mozilla_EndianUtils_h +#define mozilla_EndianUtils_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Compiler.h" +#include "mozilla/DebugOnly.h" + +#include <stdint.h> +#include <string.h> + +#if defined(_MSC_VER) +# include <stdlib.h> +# pragma intrinsic(_byteswap_ushort) +# pragma intrinsic(_byteswap_ulong) +# pragma intrinsic(_byteswap_uint64) +#endif + +/* + * Our supported compilers provide architecture-independent macros for this. + * Yes, there are more than two values for __BYTE_ORDER__. + */ +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + defined(__ORDER_BIG_ENDIAN__) +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define MOZ_LITTLE_ENDIAN() 1 +# define MOZ_BIG_ENDIAN() 0 +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define MOZ_LITTLE_ENDIAN() 0 +# define MOZ_BIG_ENDIAN() 1 +# else +# error "Can't handle mixed-endian architectures" +# endif +#else +# error "Don't know how to determine endianness" +#endif + +#if defined(__clang__) +# if __has_builtin(__builtin_bswap16) +# define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16 +# endif +#elif defined(__GNUC__) +# define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16 +#elif defined(_MSC_VER) +# define MOZ_HAVE_BUILTIN_BYTESWAP16 _byteswap_ushort +#endif + +namespace mozilla { + +namespace detail { + +/* + * We need wrappers here because free functions with default template + * arguments and/or partial specialization of function templates are not + * supported by all the compilers we use. + */ +template <typename T, size_t Size = sizeof(T)> +struct Swapper; + +template <typename T> +struct Swapper<T, 2> { + static T swap(T aValue) { +#if defined(MOZ_HAVE_BUILTIN_BYTESWAP16) + return MOZ_HAVE_BUILTIN_BYTESWAP16(aValue); +#else + return T(((aValue & 0x00ff) << 8) | ((aValue & 0xff00) >> 8)); +#endif + } +}; + +template <typename T> +struct Swapper<T, 4> { + static T swap(T aValue) { +#if defined(__clang__) || defined(__GNUC__) + return T(__builtin_bswap32(aValue)); +#elif defined(_MSC_VER) + return T(_byteswap_ulong(aValue)); +#else + return T(((aValue & 0x000000ffU) << 24) | ((aValue & 0x0000ff00U) << 8) | + ((aValue & 0x00ff0000U) >> 8) | ((aValue & 0xff000000U) >> 24)); +#endif + } +}; + +template <typename T> +struct Swapper<T, 8> { + static inline T swap(T aValue) { +#if defined(__clang__) || defined(__GNUC__) + return T(__builtin_bswap64(aValue)); +#elif defined(_MSC_VER) + return T(_byteswap_uint64(aValue)); +#else + return T(((aValue & 0x00000000000000ffULL) << 56) | + ((aValue & 0x000000000000ff00ULL) << 40) | + ((aValue & 0x0000000000ff0000ULL) << 24) | + ((aValue & 0x00000000ff000000ULL) << 8) | + ((aValue & 0x000000ff00000000ULL) >> 8) | + ((aValue & 0x0000ff0000000000ULL) >> 24) | + ((aValue & 0x00ff000000000000ULL) >> 40) | + ((aValue & 0xff00000000000000ULL) >> 56)); +#endif + } +}; + +enum Endianness { Little, Big }; + +#if MOZ_BIG_ENDIAN() +# define MOZ_NATIVE_ENDIANNESS detail::Big +#else +# define MOZ_NATIVE_ENDIANNESS detail::Little +#endif + +class EndianUtils { + /** + * Assert that the memory regions [aDest, aDest+aCount) and + * [aSrc, aSrc+aCount] do not overlap. aCount is given in bytes. + */ + static void assertNoOverlap(const void* aDest, const void* aSrc, + size_t aCount) { + DebugOnly<const uint8_t*> byteDestPtr = static_cast<const uint8_t*>(aDest); + DebugOnly<const uint8_t*> byteSrcPtr = static_cast<const uint8_t*>(aSrc); + MOZ_ASSERT( + (byteDestPtr <= byteSrcPtr && byteDestPtr + aCount <= byteSrcPtr) || + (byteSrcPtr <= byteDestPtr && byteSrcPtr + aCount <= byteDestPtr)); + } + + template <typename T> + static void assertAligned(T* aPtr) { + MOZ_ASSERT((uintptr_t(aPtr) % sizeof(T)) == 0, "Unaligned pointer!"); + } + + protected: + /** + * Return |aValue| converted from SourceEndian encoding to DestEndian + * encoding. + */ + template <Endianness SourceEndian, Endianness DestEndian, typename T> + static inline T maybeSwap(T aValue) { + if (SourceEndian == DestEndian) { + return aValue; + } + return Swapper<T>::swap(aValue); + } + + /** + * Convert |aCount| elements at |aPtr| from SourceEndian encoding to + * DestEndian encoding. + */ + template <Endianness SourceEndian, Endianness DestEndian, typename T> + static inline void maybeSwapInPlace(T* aPtr, size_t aCount) { + assertAligned(aPtr); + + if (SourceEndian == DestEndian) { + return; + } + for (size_t i = 0; i < aCount; i++) { + aPtr[i] = Swapper<T>::swap(aPtr[i]); + } + } + + /** + * Write |aCount| elements to the unaligned address |aDest| in DestEndian + * format, using elements found at |aSrc| in SourceEndian format. + */ + template <Endianness SourceEndian, Endianness DestEndian, typename T> + static void copyAndSwapTo(void* aDest, const T* aSrc, size_t aCount) { + assertNoOverlap(aDest, aSrc, aCount * sizeof(T)); + assertAligned(aSrc); + + if (SourceEndian == DestEndian) { + memcpy(aDest, aSrc, aCount * sizeof(T)); + return; + } + + uint8_t* byteDestPtr = static_cast<uint8_t*>(aDest); + for (size_t i = 0; i < aCount; ++i) { + union { + T mVal; + uint8_t mBuffer[sizeof(T)]; + } u; + u.mVal = maybeSwap<SourceEndian, DestEndian>(aSrc[i]); + memcpy(byteDestPtr, u.mBuffer, sizeof(T)); + byteDestPtr += sizeof(T); + } + } + + /** + * Write |aCount| elements to |aDest| in DestEndian format, using elements + * found at the unaligned address |aSrc| in SourceEndian format. + */ + template <Endianness SourceEndian, Endianness DestEndian, typename T> + static void copyAndSwapFrom(T* aDest, const void* aSrc, size_t aCount) { + assertNoOverlap(aDest, aSrc, aCount * sizeof(T)); + assertAligned(aDest); + + if (SourceEndian == DestEndian) { + memcpy(aDest, aSrc, aCount * sizeof(T)); + return; + } + + const uint8_t* byteSrcPtr = static_cast<const uint8_t*>(aSrc); + for (size_t i = 0; i < aCount; ++i) { + union { + T mVal; + uint8_t mBuffer[sizeof(T)]; + } u; + memcpy(u.mBuffer, byteSrcPtr, sizeof(T)); + aDest[i] = maybeSwap<SourceEndian, DestEndian>(u.mVal); + byteSrcPtr += sizeof(T); + } + } +}; + +template <Endianness ThisEndian> +class Endian : private EndianUtils { + protected: + /** Read a uint16_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static uint16_t readUint16(const void* aPtr) { + return read<uint16_t>(aPtr); + } + + /** Read a uint32_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static uint32_t readUint32(const void* aPtr) { + return read<uint32_t>(aPtr); + } + + /** Read a uint64_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static uint64_t readUint64(const void* aPtr) { + return read<uint64_t>(aPtr); + } + + /** Read a uintptr_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static uintptr_t readUintptr(const void* aPtr) { + return read<uintptr_t>(aPtr); + } + + /** Read an int16_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static int16_t readInt16(const void* aPtr) { + return read<int16_t>(aPtr); + } + + /** Read an int32_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static int32_t readInt32(const void* aPtr) { + return read<uint32_t>(aPtr); + } + + /** Read an int64_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static int64_t readInt64(const void* aPtr) { + return read<int64_t>(aPtr); + } + + /** Read an intptr_t in ThisEndian endianness from |aPtr| and return it. */ + [[nodiscard]] static intptr_t readIntptr(const void* aPtr) { + return read<intptr_t>(aPtr); + } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeUint16(void* aPtr, uint16_t aValue) { write(aPtr, aValue); } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeUint32(void* aPtr, uint32_t aValue) { write(aPtr, aValue); } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeUint64(void* aPtr, uint64_t aValue) { write(aPtr, aValue); } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeUintptr(void* aPtr, uintptr_t aValue) { + write(aPtr, aValue); + } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeInt16(void* aPtr, int16_t aValue) { write(aPtr, aValue); } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeInt32(void* aPtr, int32_t aValue) { write(aPtr, aValue); } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeInt64(void* aPtr, int64_t aValue) { write(aPtr, aValue); } + + /** Write |aValue| to |aPtr| using ThisEndian endianness. */ + static void writeIntptr(void* aPtr, intptr_t aValue) { write(aPtr, aValue); } + + /* + * Converts a value of type T to little-endian format. + * + * This function is intended for cases where you have data in your + * native-endian format and you need it to appear in little-endian + * format for transmission. + */ + template <typename T> + [[nodiscard]] static T swapToLittleEndian(T aValue) { + return maybeSwap<ThisEndian, Little>(aValue); + } + + /* + * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting + * them to little-endian format if ThisEndian is Big. |aSrc| as a typed + * pointer must be aligned; |aDest| need not be. + * + * As with memcpy, |aDest| and |aSrc| must not overlap. + */ + template <typename T> + static void copyAndSwapToLittleEndian(void* aDest, const T* aSrc, + size_t aCount) { + copyAndSwapTo<ThisEndian, Little>(aDest, aSrc, aCount); + } + + /* + * Likewise, but converts values in place. + */ + template <typename T> + static void swapToLittleEndianInPlace(T* aPtr, size_t aCount) { + maybeSwapInPlace<ThisEndian, Little>(aPtr, aCount); + } + + /* + * Converts a value of type T to big-endian format. + */ + template <typename T> + [[nodiscard]] static T swapToBigEndian(T aValue) { + return maybeSwap<ThisEndian, Big>(aValue); + } + + /* + * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting + * them to big-endian format if ThisEndian is Little. |aSrc| as a typed + * pointer must be aligned; |aDest| need not be. + * + * As with memcpy, |aDest| and |aSrc| must not overlap. + */ + template <typename T> + static void copyAndSwapToBigEndian(void* aDest, const T* aSrc, + size_t aCount) { + copyAndSwapTo<ThisEndian, Big>(aDest, aSrc, aCount); + } + + /* + * Likewise, but converts values in place. + */ + template <typename T> + static void swapToBigEndianInPlace(T* aPtr, size_t aCount) { + maybeSwapInPlace<ThisEndian, Big>(aPtr, aCount); + } + + /* + * Synonyms for the big-endian functions, for better readability + * in network code. + */ + + template <typename T> + [[nodiscard]] static T swapToNetworkOrder(T aValue) { + return swapToBigEndian(aValue); + } + + template <typename T> + static void copyAndSwapToNetworkOrder(void* aDest, const T* aSrc, + size_t aCount) { + copyAndSwapToBigEndian(aDest, aSrc, aCount); + } + + template <typename T> + static void swapToNetworkOrderInPlace(T* aPtr, size_t aCount) { + swapToBigEndianInPlace(aPtr, aCount); + } + + /* + * Converts a value of type T from little-endian format. + */ + template <typename T> + [[nodiscard]] static T swapFromLittleEndian(T aValue) { + return maybeSwap<Little, ThisEndian>(aValue); + } + + /* + * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting + * them to little-endian format if ThisEndian is Big. |aDest| as a typed + * pointer must be aligned; |aSrc| need not be. + * + * As with memcpy, |aDest| and |aSrc| must not overlap. + */ + template <typename T> + static void copyAndSwapFromLittleEndian(T* aDest, const void* aSrc, + size_t aCount) { + copyAndSwapFrom<Little, ThisEndian>(aDest, aSrc, aCount); + } + + /* + * Likewise, but converts values in place. + */ + template <typename T> + static void swapFromLittleEndianInPlace(T* aPtr, size_t aCount) { + maybeSwapInPlace<Little, ThisEndian>(aPtr, aCount); + } + + /* + * Converts a value of type T from big-endian format. + */ + template <typename T> + [[nodiscard]] static T swapFromBigEndian(T aValue) { + return maybeSwap<Big, ThisEndian>(aValue); + } + + /* + * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting + * them to big-endian format if ThisEndian is Little. |aDest| as a typed + * pointer must be aligned; |aSrc| need not be. + * + * As with memcpy, |aDest| and |aSrc| must not overlap. + */ + template <typename T> + static void copyAndSwapFromBigEndian(T* aDest, const void* aSrc, + size_t aCount) { + copyAndSwapFrom<Big, ThisEndian>(aDest, aSrc, aCount); + } + + /* + * Likewise, but converts values in place. + */ + template <typename T> + static void swapFromBigEndianInPlace(T* aPtr, size_t aCount) { + maybeSwapInPlace<Big, ThisEndian>(aPtr, aCount); + } + + /* + * Synonyms for the big-endian functions, for better readability + * in network code. + */ + template <typename T> + [[nodiscard]] static T swapFromNetworkOrder(T aValue) { + return swapFromBigEndian(aValue); + } + + template <typename T> + static void copyAndSwapFromNetworkOrder(T* aDest, const void* aSrc, + size_t aCount) { + copyAndSwapFromBigEndian(aDest, aSrc, aCount); + } + + template <typename T> + static void swapFromNetworkOrderInPlace(T* aPtr, size_t aCount) { + swapFromBigEndianInPlace(aPtr, aCount); + } + + private: + /** + * Read a value of type T, encoded in endianness ThisEndian from |aPtr|. + * Return that value encoded in native endianness. + */ + template <typename T> + static T read(const void* aPtr) { + union { + T mVal; + uint8_t mBuffer[sizeof(T)]; + } u; + memcpy(u.mBuffer, aPtr, sizeof(T)); + return maybeSwap<ThisEndian, MOZ_NATIVE_ENDIANNESS>(u.mVal); + } + + /** + * Write a value of type T, in native endianness, to |aPtr|, in ThisEndian + * endianness. + */ + template <typename T> + static void write(void* aPtr, T aValue) { + T tmp = maybeSwap<MOZ_NATIVE_ENDIANNESS, ThisEndian>(aValue); + memcpy(aPtr, &tmp, sizeof(T)); + } + + Endian() = delete; + Endian(const Endian& aTther) = delete; + void operator=(const Endian& aOther) = delete; +}; + +template <Endianness ThisEndian> +class EndianReadWrite : public Endian<ThisEndian> { + private: + typedef Endian<ThisEndian> super; + + public: + using super::readInt16; + using super::readInt32; + using super::readInt64; + using super::readIntptr; + using super::readUint16; + using super::readUint32; + using super::readUint64; + using super::readUintptr; + using super::writeInt16; + using super::writeInt32; + using super::writeInt64; + using super::writeIntptr; + using super::writeUint16; + using super::writeUint32; + using super::writeUint64; + using super::writeUintptr; +}; + +} /* namespace detail */ + +class LittleEndian final : public detail::EndianReadWrite<detail::Little> {}; + +class BigEndian final : public detail::EndianReadWrite<detail::Big> {}; + +typedef BigEndian NetworkEndian; + +class NativeEndian final : public detail::Endian<MOZ_NATIVE_ENDIANNESS> { + private: + typedef detail::Endian<MOZ_NATIVE_ENDIANNESS> super; + + public: + /* + * These functions are intended for cases where you have data in your + * native-endian format and you need the data to appear in the appropriate + * endianness for transmission, serialization, etc. + */ + using super::copyAndSwapToBigEndian; + using super::copyAndSwapToLittleEndian; + using super::copyAndSwapToNetworkOrder; + using super::swapToBigEndian; + using super::swapToBigEndianInPlace; + using super::swapToLittleEndian; + using super::swapToLittleEndianInPlace; + using super::swapToNetworkOrder; + using super::swapToNetworkOrderInPlace; + + /* + * These functions are intended for cases where you have data in the + * given endianness (e.g. reading from disk or a file-format) and you + * need the data to appear in native-endian format for processing. + */ + using super::copyAndSwapFromBigEndian; + using super::copyAndSwapFromLittleEndian; + using super::copyAndSwapFromNetworkOrder; + using super::swapFromBigEndian; + using super::swapFromBigEndianInPlace; + using super::swapFromLittleEndian; + using super::swapFromLittleEndianInPlace; + using super::swapFromNetworkOrder; + using super::swapFromNetworkOrderInPlace; +}; + +#undef MOZ_NATIVE_ENDIANNESS + +} /* namespace mozilla */ + +#endif /* mozilla_EndianUtils_h */ diff --git a/include/onlineupdate/mozilla/HashFunctions.h b/include/onlineupdate/mozilla/HashFunctions.h new file mode 100644 index 000000000000..4b740a3db16b --- /dev/null +++ b/include/onlineupdate/mozilla/HashFunctions.h @@ -0,0 +1,417 @@ +/* -*- 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/. */ + +/* Utilities for hashing. */ + +/* + * This file exports functions for hashing data down to a uint32_t (a.k.a. + * mozilla::HashNumber), including: + * + * - HashString Hash a char* or char16_t/wchar_t* of known or unknown + * length. + * + * - HashBytes Hash a byte array of known length. + * + * - HashGeneric Hash one or more values. Currently, we support uint32_t, + * types which can be implicitly cast to uint32_t, data + * pointers, and function pointers. + * + * - AddToHash Add one or more values to the given hash. This supports the + * same list of types as HashGeneric. + * + * + * You can chain these functions together to hash complex objects. For example: + * + * class ComplexObject + * { + * char* mStr; + * uint32_t mUint1, mUint2; + * void (*mCallbackFn)(); + * + * public: + * HashNumber hash() + * { + * HashNumber hash = HashString(mStr); + * hash = AddToHash(hash, mUint1, mUint2); + * return AddToHash(hash, mCallbackFn); + * } + * }; + * + * If you want to hash an nsAString or nsACString, use the HashString functions + * in nsHashKeys.h. + */ + +#ifndef mozilla_HashFunctions_h +#define mozilla_HashFunctions_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Char16.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/Types.h" +#include "mozilla/WrappingOperations.h" + +#include <stdint.h> +#include <type_traits> + +namespace mozilla { + +using HashNumber = uint32_t; +static const uint32_t kHashNumberBits = 32; + +/** + * The golden ratio as a 32-bit fixed-point value. + */ +static const HashNumber kGoldenRatioU32 = 0x9E3779B9U; + +/* + * Given a raw hash code, h, return a number that can be used to select a hash + * bucket. + * + * This function aims to produce as uniform an output distribution as possible, + * especially in the most significant (leftmost) bits, even though the input + * distribution may be highly nonrandom, given the constraints that this must + * be deterministic and quick to compute. + * + * Since the leftmost bits of the result are best, the hash bucket index is + * computed by doing ScrambleHashCode(h) / (2^32/N) or the equivalent + * right-shift, not ScrambleHashCode(h) % N or the equivalent bit-mask. + */ +constexpr HashNumber ScrambleHashCode(HashNumber h) { + /* + * Simply returning h would not cause any hash tables to produce wrong + * answers. But it can produce pathologically bad performance: The caller + * right-shifts the result, keeping only the highest bits. The high bits of + * hash codes are very often completely entropy-free. (So are the lowest + * bits.) + * + * So we use Fibonacci hashing, as described in Knuth, The Art of Computer + * Programming, 6.4. This mixes all the bits of the input hash code h. + * + * The value of goldenRatio is taken from the hex expansion of the golden + * ratio, which starts 1.9E3779B9.... This value is especially good if + * values with consecutive hash codes are stored in a hash table; see Knuth + * for details. + */ + return mozilla::WrappingMultiply(h, kGoldenRatioU32); +} + +namespace detail { + +MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW +constexpr HashNumber RotateLeft5(HashNumber aValue) { + return (aValue << 5) | (aValue >> 27); +} + +constexpr HashNumber AddU32ToHash(HashNumber aHash, uint32_t aValue) { + /* + * This is the meat of all our hash routines. This hash function is not + * particularly sophisticated, but it seems to work well for our mostly + * plain-text inputs. Implementation notes follow. + * + * Our use of the golden ratio here is arbitrary; we could pick almost any + * number which: + * + * * is odd (because otherwise, all our hash values will be even) + * + * * has a reasonably-even mix of 1's and 0's (consider the extreme case + * where we multiply by 0x3 or 0xeffffff -- this will not produce good + * mixing across all bits of the hash). + * + * The rotation length of 5 is also arbitrary, although an odd number is again + * preferable so our hash explores the whole universe of possible rotations. + * + * Finally, we multiply by the golden ratio *after* xor'ing, not before. + * Otherwise, if |aHash| is 0 (as it often is for the beginning of a + * message), the expression + * + * mozilla::WrappingMultiply(kGoldenRatioU32, RotateLeft5(aHash)) + * |xor| + * aValue + * + * evaluates to |aValue|. + * + * (Number-theoretic aside: Because any odd number |m| is relatively prime to + * our modulus (2**32), the list + * + * [x * m (mod 2**32) for 0 <= x < 2**32] + * + * has no duplicate elements. This means that multiplying by |m| does not + * cause us to skip any possible hash values. + * + * It's also nice if |m| has large-ish order mod 2**32 -- that is, if the + * smallest k such that m**k == 1 (mod 2**32) is large -- so we can safely + * multiply our hash value by |m| a few times without negating the + * multiplicative effect. Our golden ratio constant has order 2**29, which is + * more than enough for our purposes.) + */ + return mozilla::WrappingMultiply(kGoldenRatioU32, + RotateLeft5(aHash) ^ aValue); +} + +/** + * AddUintptrToHash takes sizeof(uintptr_t) as a template parameter. + */ +template <size_t PtrSize> +constexpr HashNumber AddUintptrToHash(HashNumber aHash, uintptr_t aValue) { + return AddU32ToHash(aHash, static_cast<uint32_t>(aValue)); +} + +template <> +inline HashNumber AddUintptrToHash<8>(HashNumber aHash, uintptr_t aValue) { + uint32_t v1 = static_cast<uint32_t>(aValue); + uint32_t v2 = static_cast<uint32_t>(static_cast<uint64_t>(aValue) >> 32); + return AddU32ToHash(AddU32ToHash(aHash, v1), v2); +} + +} /* namespace detail */ + +/** + * AddToHash takes a hash and some values and returns a new hash based on the + * inputs. + * + * Currently, we support hashing uint32_t's, values which we can implicitly + * convert to uint32_t, data pointers, and function pointers. + */ +template <typename T, bool TypeIsNotIntegral = !std::is_integral_v<T>, + bool TypeIsNotEnum = !std::is_enum_v<T>, + std::enable_if_t<TypeIsNotIntegral && TypeIsNotEnum, int> = 0> +[[nodiscard]] inline HashNumber AddToHash(HashNumber aHash, T aA) { + /* + * Try to convert |A| to uint32_t implicitly. If this works, great. If not, + * we'll error out. + */ + return detail::AddU32ToHash(aHash, aA); +} + +template <typename A> +[[nodiscard]] inline HashNumber AddToHash(HashNumber aHash, A* aA) { + /* + * You might think this function should just take a void*. But then we'd only + * catch data pointers and couldn't handle function pointers. + */ + + static_assert(sizeof(aA) == sizeof(uintptr_t), "Strange pointer!"); + + return detail::AddUintptrToHash<sizeof(uintptr_t)>(aHash, uintptr_t(aA)); +} + +// We use AddUintptrToHash() for hashing all integral types. 8-byte integral +// types are treated the same as 64-bit pointers, and smaller integral types are +// first implicitly converted to 32 bits and then passed to AddUintptrToHash() +// to be hashed. +template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0> +[[nodiscard]] constexpr HashNumber AddToHash(HashNumber aHash, T aA) { + return detail::AddUintptrToHash<sizeof(T)>(aHash, aA); +} + +template <typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0> +[[nodiscard]] constexpr HashNumber AddToHash(HashNumber aHash, T aA) { + // Hash using AddUintptrToHash with the underlying type of the enum type + using UnderlyingType = typename std::underlying_type<T>::type; + return detail::AddUintptrToHash<sizeof(UnderlyingType)>( + aHash, static_cast<UnderlyingType>(aA)); +} + +template <typename A, typename... Args> +[[nodiscard]] HashNumber AddToHash(HashNumber aHash, A aArg, Args... aArgs) { + return AddToHash(AddToHash(aHash, aArg), aArgs...); +} + +/** + * The HashGeneric class of functions let you hash one or more values. + * + * If you want to hash together two values x and y, calling HashGeneric(x, y) is + * much better than calling AddToHash(x, y), because AddToHash(x, y) assumes + * that x has already been hashed. + */ +template <typename... Args> +[[nodiscard]] inline HashNumber HashGeneric(Args... aArgs) { + return AddToHash(0, aArgs...); +} + +/** + * Hash successive |*aIter| until |!*aIter|, i.e. til null-termination. + * + * This function is *not* named HashString like the non-template overloads + * below. Some users define HashString overloads and pass inexactly-matching + * values to them -- but an inexactly-matching value would match this overload + * instead! We follow the general rule and don't mix and match template and + * regular overloads to avoid this. + * + * If you have the string's length, call HashStringKnownLength: it may be + * marginally faster. + */ +template <typename Iterator> +[[nodiscard]] constexpr HashNumber HashStringUntilZero(Iterator aIter) { + HashNumber hash = 0; + for (; auto c = *aIter; ++aIter) { + hash = AddToHash(hash, c); + } + return hash; +} + +/** + * Hash successive |aIter[i]| up to |i == aLength|. + */ +template <typename Iterator> +[[nodiscard]] constexpr HashNumber HashStringKnownLength(Iterator aIter, + size_t aLength) { + HashNumber hash = 0; + for (size_t i = 0; i < aLength; i++) { + hash = AddToHash(hash, aIter[i]); + } + return hash; +} + +/** + * The HashString overloads below do just what you'd expect. + * + * These functions are non-template functions so that users can 1) overload them + * with their own types 2) in a way that allows implicit conversions to happen. + */ +[[nodiscard]] inline HashNumber HashString(const char* aStr) { + // Use the |const unsigned char*| version of the above so that all ordinary + // character data hashes identically. + return HashStringUntilZero(reinterpret_cast<const unsigned char*>(aStr)); +} + +[[nodiscard]] inline HashNumber HashString(const char* aStr, size_t aLength) { + // Delegate to the |const unsigned char*| version of the above to share + // template instantiations. + return HashStringKnownLength(reinterpret_cast<const unsigned char*>(aStr), + aLength); +} + +[[nodiscard]] inline HashNumber HashString(const unsigned char* aStr, + size_t aLength) { + return HashStringKnownLength(aStr, aLength); +} + +[[nodiscard]] constexpr HashNumber HashString(const char16_t* aStr) { + return HashStringUntilZero(aStr); +} + +[[nodiscard]] inline HashNumber HashString(const char16_t* aStr, + size_t aLength) { + return HashStringKnownLength(aStr, aLength); +} + +/** + * HashString overloads for |wchar_t| on platforms where it isn't |char16_t|. + */ +template <typename WCharT, typename = typename std::enable_if< + std::is_same<WCharT, wchar_t>::value && + !std::is_same<wchar_t, char16_t>::value>::type> +[[nodiscard]] inline HashNumber HashString(const WCharT* aStr) { + return HashStringUntilZero(aStr); +} + +template <typename WCharT, typename = typename std::enable_if< + std::is_same<WCharT, wchar_t>::value && + !std::is_same<wchar_t, char16_t>::value>::type> +[[nodiscard]] inline HashNumber HashString(const WCharT* aStr, size_t aLength) { + return HashStringKnownLength(aStr, aLength); +} + +/** + * Hash some number of bytes. + * + * This hash walks word-by-word, rather than byte-by-byte, so you won't get the + * same result out of HashBytes as you would out of HashString. + */ +[[nodiscard]] extern MFBT_API HashNumber HashBytes(const void* bytes, + size_t aLength); + +/** + * A pseudorandom function mapping 32-bit integers to 32-bit integers. + * + * This is for when you're feeding private data (like pointer values or credit + * card numbers) to a non-crypto hash function (like HashBytes) and then using + * the hash code for something that untrusted parties could observe (like a JS + * Map). Plug in a HashCodeScrambler before that last step to avoid leaking the + * private data. + * + * By itself, this does not prevent hash-flooding DoS attacks, because an + * attacker can still generate many values with exactly equal hash codes by + * attacking the non-crypto hash function alone. Equal hash codes will, of + * course, still be equal however much you scramble them. + * + * The algorithm is SipHash-1-3. See <https://131002.net/siphash/>. + */ +class HashCodeScrambler { + struct SipHasher; + + uint64_t mK0, mK1; + + public: + /** Creates a new scrambler with the given 128-bit key. */ + constexpr HashCodeScrambler(uint64_t aK0, uint64_t aK1) + : mK0(aK0), mK1(aK1) {} + + /** + * Scramble a hash code. Always produces the same result for the same + * combination of key and hash code. + */ + HashNumber scramble(HashNumber aHashCode) const { + SipHasher hasher(mK0, mK1); + return HashNumber(hasher.sipHash(aHashCode)); + } + + static constexpr size_t offsetOfMK0() { + return offsetof(HashCodeScrambler, mK0); + } + + static constexpr size_t offsetOfMK1() { + return offsetof(HashCodeScrambler, mK1); + } + + private: + struct SipHasher { + SipHasher(uint64_t aK0, uint64_t aK1) { + // 1. Initialization. + mV0 = aK0 ^ UINT64_C(0x736f6d6570736575); + mV1 = aK1 ^ UINT64_C(0x646f72616e646f6d); + mV2 = aK0 ^ UINT64_C(0x6c7967656e657261); + mV3 = aK1 ^ UINT64_C(0x7465646279746573); + } + + uint64_t sipHash(uint64_t aM) { + // 2. Compression. + mV3 ^= aM; + sipRound(); + mV0 ^= aM; + + // 3. Finalization. + mV2 ^= 0xff; + for (int i = 0; i < 3; i++) sipRound(); + return mV0 ^ mV1 ^ mV2 ^ mV3; + } + + void sipRound() { + mV0 = WrappingAdd(mV0, mV1); + mV1 = RotateLeft(mV1, 13); + mV1 ^= mV0; + mV0 = RotateLeft(mV0, 32); + mV2 = WrappingAdd(mV2, mV3); + mV3 = RotateLeft(mV3, 16); + mV3 ^= mV2; + mV0 = WrappingAdd(mV0, mV3); + mV3 = RotateLeft(mV3, 21); + mV3 ^= mV0; + mV2 = WrappingAdd(mV2, mV1); + mV1 = RotateLeft(mV1, 17); + mV1 ^= mV2; + mV2 = RotateLeft(mV2, 32); + } + + uint64_t mV0, mV1, mV2, mV3; + }; +}; + +} /* namespace mozilla */ + +#endif /* mozilla_HashFunctions_h */ diff --git a/include/onlineupdate/mozilla/MathAlgorithms.h b/include/onlineupdate/mozilla/MathAlgorithms.h new file mode 100644 index 000000000000..66aa1b9f7124 --- /dev/null +++ b/include/onlineupdate/mozilla/MathAlgorithms.h @@ -0,0 +1,492 @@ +/* -*- 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/. */ + +/* mfbt maths algorithms. */ + +#ifndef mozilla_MathAlgorithms_h +#define mozilla_MathAlgorithms_h + +#include "mozilla/Assertions.h" + +#include <cmath> +#include <algorithm> +#include <limits.h> +#include <stdint.h> +#include <type_traits> + +namespace mozilla { + +namespace detail { + +template <typename T> +struct AllowDeprecatedAbsFixed : std::false_type {}; + +template <> +struct AllowDeprecatedAbsFixed<int32_t> : std::true_type {}; +template <> +struct AllowDeprecatedAbsFixed<int64_t> : std::true_type {}; + +template <typename T> +struct AllowDeprecatedAbs : AllowDeprecatedAbsFixed<T> {}; + +template <> +struct AllowDeprecatedAbs<int> : std::true_type {}; +template <> +struct AllowDeprecatedAbs<long> : std::true_type {}; + +} // namespace detail + +// DO NOT USE DeprecatedAbs. It exists only until its callers can be converted +// to Abs below, and it will be removed when all callers have been changed. +template <typename T> +inline std::enable_if_t<detail::AllowDeprecatedAbs<T>::value, T> DeprecatedAbs( + const T aValue) { + // The absolute value of the smallest possible value of a signed-integer type + // won't fit in that type (on twos-complement systems -- and we're blithely + // assuming we're on such systems, for the non-<stdint.h> types listed above), + // so assert that the input isn't that value. + // + // This is the case if: the value is non-negative; or if adding one (giving a + // value in the range [-maxvalue, 0]), then negating (giving a value in the + // range [0, maxvalue]), doesn't produce maxvalue (because in twos-complement, + // (minvalue + 1) == -maxvalue). + MOZ_ASSERT(aValue >= 0 || + -(aValue + 1) != T((1ULL << (CHAR_BIT * sizeof(T) - 1)) - 1), + "You can't negate the smallest possible negative integer!"); + return aValue >= 0 ? aValue : -aValue; +} + +namespace detail { + +template <typename T, typename = void> +struct AbsReturnType; + +template <typename T> +struct AbsReturnType< + T, std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>>> { + using Type = std::make_unsigned_t<T>; +}; + +template <typename T> +struct AbsReturnType<T, std::enable_if_t<std::is_floating_point_v<T>>> { + using Type = T; +}; + +} // namespace detail + +template <typename T> +inline constexpr typename detail::AbsReturnType<T>::Type Abs(const T aValue) { + using ReturnType = typename detail::AbsReturnType<T>::Type; + return aValue >= 0 ? ReturnType(aValue) : ~ReturnType(aValue) + 1; +} + +template <> +inline float Abs<float>(const float aFloat) { + return std::fabs(aFloat); +} + +template <> +inline double Abs<double>(const double aDouble) { + return std::fabs(aDouble); +} + +template <> +inline long double Abs<long double>(const long double aLongDouble) { + return std::fabs(aLongDouble); +} + +} // namespace mozilla + +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \ + defined(_M_X64) || defined(_M_ARM64)) +# define MOZ_BITSCAN_WINDOWS + +# include <intrin.h> +# pragma intrinsic(_BitScanForward, _BitScanReverse) + +# if defined(_M_AMD64) || defined(_M_X64) || defined(_M_ARM64) +# define MOZ_BITSCAN_WINDOWS64 +# pragma intrinsic(_BitScanForward64, _BitScanReverse64) +# endif + +#endif + +namespace mozilla { + +namespace detail { + +#if defined(MOZ_BITSCAN_WINDOWS) + +inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) { + unsigned long index; + if (!_BitScanReverse(&index, static_cast<unsigned long>(aValue))) return 32; + return uint_fast8_t(31 - index); +} + +inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) { + unsigned long index; + if (!_BitScanForward(&index, static_cast<unsigned long>(aValue))) return 32; + return uint_fast8_t(index); +} + +inline uint_fast8_t CountPopulation32(uint32_t aValue) { + uint32_t x = aValue - ((aValue >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + return (((x + (x >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; +} +inline uint_fast8_t CountPopulation64(uint64_t aValue) { + return uint_fast8_t(CountPopulation32(aValue & 0xffffffff) + + CountPopulation32(aValue >> 32)); +} + +inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) { +# if defined(MOZ_BITSCAN_WINDOWS64) + unsigned long index; + if (!_BitScanReverse64(&index, static_cast<unsigned __int64>(aValue))) + return 64; + return uint_fast8_t(63 - index); +# else + uint32_t hi = uint32_t(aValue >> 32); + if (hi != 0) { + return CountLeadingZeroes32(hi); + } + return 32u + CountLeadingZeroes32(uint32_t(aValue)); +# endif +} + +inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) { +# if defined(MOZ_BITSCAN_WINDOWS64) + unsigned long index; + if (!_BitScanForward64(&index, static_cast<unsigned __int64>(aValue))) + return 64; + return uint_fast8_t(index); +# else + uint32_t lo = uint32_t(aValue); + if (lo != 0) { + return CountTrailingZeroes32(lo); + } + return 32u + CountTrailingZeroes32(uint32_t(aValue >> 32)); +# endif +} + +#elif defined(__clang__) || defined(__GNUC__) + +# if defined(__clang__) +# if !__has_builtin(__builtin_ctz) || !__has_builtin(__builtin_clz) +# error "A clang providing __builtin_c[lt]z is required to build" +# endif +# else +// gcc has had __builtin_clz and friends since 3.4: no need to check. +# endif + +inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) { + return static_cast<uint_fast8_t>(__builtin_clz(aValue)); +} + +inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) { + return static_cast<uint_fast8_t>(__builtin_ctz(aValue)); +} + +inline uint_fast8_t CountPopulation32(uint32_t aValue) { + return static_cast<uint_fast8_t>(__builtin_popcount(aValue)); +} + +inline uint_fast8_t CountPopulation64(uint64_t aValue) { + return static_cast<uint_fast8_t>(__builtin_popcountll(aValue)); +} + +inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) { + return static_cast<uint_fast8_t>(__builtin_clzll(aValue)); +} + +inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) { + return static_cast<uint_fast8_t>(__builtin_ctzll(aValue)); +} + +#else +# error "Implement these!" +inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) = delete; +inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) = delete; +inline uint_fast8_t CountPopulation32(uint32_t aValue) = delete; +inline uint_fast8_t CountPopulation64(uint64_t aValue) = delete; +inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) = delete; +inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) = delete; +#endif + +} // namespace detail + +/** + * Compute the number of high-order zero bits in the NON-ZERO number |aValue|. + * That is, looking at the bitwise representation of the number, with the + * highest- valued bits at the start, return the number of zeroes before the + * first one is observed. + * + * CountLeadingZeroes32(0xF0FF1000) is 0; + * CountLeadingZeroes32(0x7F8F0001) is 1; + * CountLeadingZeroes32(0x3FFF0100) is 2; + * CountLeadingZeroes32(0x1FF50010) is 3; and so on. + */ +inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) { + MOZ_ASSERT(aValue != 0); + return detail::CountLeadingZeroes32(aValue); +} + +/** + * Compute the number of low-order zero bits in the NON-ZERO number |aValue|. + * That is, looking at the bitwise representation of the number, with the + * lowest- valued bits at the start, return the number of zeroes before the + * first one is observed. + * + * CountTrailingZeroes32(0x0100FFFF) is 0; + * CountTrailingZeroes32(0x7000FFFE) is 1; + * CountTrailingZeroes32(0x0080FFFC) is 2; + * CountTrailingZeroes32(0x0080FFF8) is 3; and so on. + */ +inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) { + MOZ_ASSERT(aValue != 0); + return detail::CountTrailingZeroes32(aValue); +} + +/** + * Compute the number of one bits in the number |aValue|, + */ +inline uint_fast8_t CountPopulation32(uint32_t aValue) { + return detail::CountPopulation32(aValue); +} + +/** Analogous to CountPopulation32, but for 64-bit numbers */ +inline uint_fast8_t CountPopulation64(uint64_t aValue) { + return detail::CountPopulation64(aValue); +} + +/** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */ +inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) { + MOZ_ASSERT(aValue != 0); + return detail::CountLeadingZeroes64(aValue); +} + +/** Analogous to CountTrailingZeroes32, but for 64-bit numbers. */ +inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) { + MOZ_ASSERT(aValue != 0); + return detail::CountTrailingZeroes64(aValue); +} + +namespace detail { + +template <typename T, size_t Size = sizeof(T)> +class CeilingLog2; + +template <typename T> +class CeilingLog2<T, 4> { + public: + static uint_fast8_t compute(const T aValue) { + // Check for <= 1 to avoid the == 0 undefined case. + return aValue <= 1 ? 0u : 32u - CountLeadingZeroes32(aValue - 1); + } +}; + +template <typename T> +class CeilingLog2<T, 8> { + public: + static uint_fast8_t compute(const T aValue) { + // Check for <= 1 to avoid the == 0 undefined case. + return aValue <= 1 ? 0u : 64u - CountLeadingZeroes64(aValue - 1); + } +}; + +} // namespace detail + +/** + * Compute the log of the least power of 2 greater than or equal to |aValue|. + * + * CeilingLog2(0..1) is 0; + * CeilingLog2(2) is 1; + * CeilingLog2(3..4) is 2; + * CeilingLog2(5..8) is 3; + * CeilingLog2(9..16) is 4; and so on. + */ +template <typename T> +inline uint_fast8_t CeilingLog2(const T aValue) { + return detail::CeilingLog2<T>::compute(aValue); +} + +/** A CeilingLog2 variant that accepts only size_t. */ +inline uint_fast8_t CeilingLog2Size(size_t aValue) { + return CeilingLog2(aValue); +} + +namespace detail { + +template <typename T, size_t Size = sizeof(T)> +class FloorLog2; + +template <typename T> +class FloorLog2<T, 4> { + public: + static uint_fast8_t compute(const T aValue) { + return 31u - CountLeadingZeroes32(aValue | 1); + } +}; + +template <typename T> +class FloorLog2<T, 8> { + public: + static uint_fast8_t compute(const T aValue) { + return 63u - CountLeadingZeroes64(aValue | 1); + } +}; + +} // namespace detail + +/** + * Compute the log of the greatest power of 2 less than or equal to |aValue|. + * + * FloorLog2(0..1) is 0; + * FloorLog2(2..3) is 1; + * FloorLog2(4..7) is 2; + * FloorLog2(8..15) is 3; and so on. + */ +template <typename T> +inline constexpr uint_fast8_t FloorLog2(const T aValue) { + return detail::FloorLog2<T>::compute(aValue); +} + +/** A FloorLog2 variant that accepts only size_t. */ +inline uint_fast8_t FloorLog2Size(size_t aValue) { return FloorLog2(aValue); } + +/* + * Compute the smallest power of 2 greater than or equal to |x|. |x| must not + * be so great that the computed value would overflow |size_t|. + */ +inline size_t RoundUpPow2(size_t aValue) { + MOZ_ASSERT(aValue <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)), + "can't round up -- will overflow!"); + return size_t(1) << CeilingLog2(aValue); +} + +/** + * Rotates the bits of the given value left by the amount of the shift width. + */ +template <typename T> +MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW inline T RotateLeft(const T aValue, + uint_fast8_t aShift) { + static_assert(std::is_unsigned_v<T>, "Rotates require unsigned values"); + + MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!"); + MOZ_ASSERT(aShift > 0, + "Rotation by value length is undefined behavior, but compilers " + "do not currently fold a test into the rotate instruction. " + "Please remove this restriction when compilers optimize the " + "zero case (http://blog.regehr.org/archives/1063)."); + + return (aValue << aShift) | (aValue >> (sizeof(T) * CHAR_BIT - aShift)); +} + +/** + * Rotates the bits of the given value right by the amount of the shift width. + */ +template <typename T> +MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW inline T RotateRight(const T aValue, + uint_fast8_t aShift) { + static_assert(std::is_unsigned_v<T>, "Rotates require unsigned values"); + + MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!"); + MOZ_ASSERT(aShift > 0, + "Rotation by value length is undefined behavior, but compilers " + "do not currently fold a test into the rotate instruction. " + "Please remove this restriction when compilers optimize the " + "zero case (http://blog.regehr.org/archives/1063)."); + + return (aValue >> aShift) | (aValue << (sizeof(T) * CHAR_BIT - aShift)); +} + +/** + * Returns true if |x| is a power of two. + * Zero is not an integer power of two. (-Inf is not an integer) + */ +template <typename T> +constexpr bool IsPowerOfTwo(T x) { + static_assert(std::is_unsigned_v<T>, "IsPowerOfTwo requires unsigned values"); + return x && (x & (x - 1)) == 0; +} + +template <typename T> +inline T Clamp(const T aValue, const T aMin, const T aMax) { + static_assert(std::is_integral_v<T>, + "Clamp accepts only integral types, so that it doesn't have" + " to distinguish differently-signed zeroes (which users may" + " or may not care to distinguish, likely at a perf cost) or" + " to decide how to clamp NaN or a range with a NaN" + " endpoint."); + MOZ_ASSERT(aMin <= aMax); + + if (aValue <= aMin) return aMin; + if (aValue >= aMax) return aMax; + return aValue; +} + +template <typename T> +inline uint_fast8_t CountTrailingZeroes(T aValue) { + static_assert(sizeof(T) <= 8); + static_assert(std::is_integral_v<T>); + // This casts to 32-bits + if constexpr (sizeof(T) <= 4) { + return CountTrailingZeroes32(aValue); + } + // This doesn't + if constexpr (sizeof(T) == 8) { + return CountTrailingZeroes64(aValue); + } +} + +// Greatest Common Divisor, from +// https://en.wikipedia.org/wiki/Binary_GCD_algorithm#Implementation +template <typename T> +MOZ_ALWAYS_INLINE T GCD(T aA, T aB) { + static_assert(std::is_integral_v<T>); + + MOZ_ASSERT(aA >= 0); + MOZ_ASSERT(aB >= 0); + + if (aA == 0) { + return aB; + } + if (aB == 0) { + return aA; + } + + T az = CountTrailingZeroes(aA); + T bz = CountTrailingZeroes(aB); + T shift = std::min<T>(az, bz); + aA >>= az; + aB >>= bz; + + while (aA != 0) { + if constexpr (!std::is_signed_v<T>) { + if (aA < aB) { + std::swap(aA, aB); + } + } + T diff = aA - aB; + if constexpr (std::is_signed_v<T>) { + aB = std::min<T>(aA, aB); + } + if constexpr (std::is_signed_v<T>) { + aA = std::abs(diff); + } else { + aA = diff; + } + if (aA) { + aA >>= CountTrailingZeroes(aA); + } + } + + return aB << shift; +} + +} /* namespace mozilla */ + +#endif /* mozilla_MathAlgorithms_h */ diff --git a/include/onlineupdate/mozilla/MemoryReporting.h b/include/onlineupdate/mozilla/MemoryReporting.h new file mode 100644 index 000000000000..d2340ecf0905 --- /dev/null +++ b/include/onlineupdate/mozilla/MemoryReporting.h @@ -0,0 +1,30 @@ +/* -*- 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/. */ + +/* Memory reporting infrastructure. */ + +#ifndef mozilla_MemoryReporting_h +#define mozilla_MemoryReporting_h + +#include <stddef.h> + +#ifdef __cplusplus + +namespace mozilla { + +/* + * This is for functions that are like malloc_usable_size. Such functions are + * used for measuring the size of data structures. + */ +typedef size_t (*MallocSizeOf)(const void* p); + +} /* namespace mozilla */ + +#endif /* __cplusplus */ + +typedef size_t (*MozMallocSizeOf)(const void* p); + +#endif /* mozilla_MemoryReporting_h */ diff --git a/include/onlineupdate/mozilla/ReentrancyGuard.h b/include/onlineupdate/mozilla/ReentrancyGuard.h new file mode 100644 index 000000000000..56c963b4188e --- /dev/null +++ b/include/onlineupdate/mozilla/ReentrancyGuard.h @@ -0,0 +1,50 @@ +/* -*- 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/. */ + +/* Small helper class for asserting uses of a class are non-reentrant. */ + +#ifndef mozilla_ReentrancyGuard_h +#define mozilla_ReentrancyGuard_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" + +namespace mozilla { + +/* Useful for implementing containers that assert non-reentrancy */ +class MOZ_RAII ReentrancyGuard { +#ifdef DEBUG + bool& mEntered; +#endif + + public: + template <class T> +#ifdef DEBUG + explicit ReentrancyGuard(T& aObj) + : mEntered(aObj.mEntered) +#else + explicit ReentrancyGuard(T&) +#endif + { +#ifdef DEBUG + MOZ_ASSERT(!mEntered); + mEntered = true; +#endif + } + ~ReentrancyGuard() { +#ifdef DEBUG + mEntered = false; +#endif + } + + private: + ReentrancyGuard(const ReentrancyGuard&) = delete; + void operator=(const ReentrancyGuard&) = delete; +}; + +} // namespace mozilla + +#endif /* mozilla_ReentrancyGuard_h */ diff --git a/include/onlineupdate/mozilla/Result.h b/include/onlineupdate/mozilla/Result.h new file mode 100644 index 000000000000..052920fdbf17 --- /dev/null +++ b/include/onlineupdate/mozilla/Result.h @@ -0,0 +1,873 @@ +/* -*- 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/. */ + +/* A type suitable for returning either a value or an error from a function. */ + +#ifndef mozilla_Result_h +#define mozilla_Result_h + +#include <algorithm> +#include <cstdint> +#include <cstring> +#include <type_traits> +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/CompactPair.h" +#include "mozilla/MaybeStorageBase.h" + +namespace mozilla { + +/** + * Empty struct, indicating success for operations that have no return value. + * For example, if you declare another empty struct `struct OutOfMemory {};`, + * then `Result<Ok, OutOfMemory>` represents either success or OOM. + */ +struct Ok {}; + +/** + * A tag used to differentiate between GenericErrorResult created by the Err + * function (completely new error) and GenericErrorResult created by the + * Result::propagateErr function (propagated error). This can be used to track + * error propagation and eventually produce error stacks for logging/debugging + * purposes. + */ +struct ErrorPropagationTag {}; + +template <typename E> +class GenericErrorResult; +template <typename V, typename E> +class Result; + +namespace detail { + +enum class PackingStrategy { + Variant, + NullIsOk, + LowBitTagIsError, + PackedVariant, + ZeroIsEmptyError, +}; + +template <typename T> +struct UnusedZero; + +template <typename V, typename E, PackingStrategy Strategy> +class ResultImplementation; + +template <typename V> +struct EmptyWrapper : V { + constexpr EmptyWrapper() = default; + explicit constexpr EmptyWrapper(const V&) {} + explicit constexpr EmptyWrapper(std::in_place_t) {} + + constexpr V* addr() { return this; } + constexpr const V* addr() const { return this; } +}; + +// The purpose of AlignedStorageOrEmpty is to make an empty class look like +// std::aligned_storage_t for the purposes of the PackingStrategy::NullIsOk +// specializations of ResultImplementation below. We can't use +// std::aligned_storage_t itself with an empty class, since it would no longer +// be empty. +template <typename V> +using AlignedStorageOrEmpty = + std::conditional_t<std::is_empty_v<V>, EmptyWrapper<V>, + MaybeStorageBase<V>>; + +template <typename V, typename E> +class ResultImplementationNullIsOkBase { + protected: + using ErrorStorageType = typename UnusedZero<E>::StorageType; + + static constexpr auto kNullValue = UnusedZero<E>::nullValue; + + static_assert(std::is_trivially_copyable_v<ErrorStorageType>); + + // XXX This can't be statically asserted in general, if ErrorStorageType is + // not a basic type. With C++20 bit_cast, we could probably re-add such as + // assertion. static_assert(kNullValue == decltype(kNullValue)(0)); + + CompactPair<AlignedStorageOrEmpty<V>, ErrorStorageType> mValue; + + public: + explicit constexpr ResultImplementationNullIsOkBase(const V& aSuccessValue) + : mValue(aSuccessValue, kNullValue) {} + explicit constexpr ResultImplementationNullIsOkBase(V&& aSuccessValue) + : mValue(std::move(aSuccessValue), kNullValue) {} + template <typename... Args> + explicit constexpr ResultImplementationNullIsOkBase(std::in_place_t, + Args&&... aArgs) + : mValue(std::piecewise_construct, + std::tuple(std::in_place, std::forward<Args>(aArgs)...), + std::tuple(kNullValue)) {} + explicit constexpr ResultImplementationNullIsOkBase(E aErrorValue) + : mValue(std::piecewise_construct, std::tuple<>(), + std::tuple(UnusedZero<E>::Store(std::move(aErrorValue)))) { + MOZ_ASSERT(mValue.second() != kNullValue); + } + + constexpr ResultImplementationNullIsOkBase( + ResultImplementationNullIsOkBase&& aOther) + : mValue(std::piecewise_construct, std::tuple<>(), + std::tuple(aOther.mValue.second())) { + if constexpr (!std::is_empty_v<V>) { + if (isOk()) { + new (mValue.first().addr()) V(std::move(*aOther.mValue.first().addr())); + } + } + } + ResultImplementationNullIsOkBase& operator=( + ResultImplementationNullIsOkBase&& aOther) { + if constexpr (!std::is_empty_v<V>) { + if (isOk()) { + mValue.first().addr()->~V(); + } + } + mValue.second() = std::move(aOther.mValue.second()); + if constexpr (!std::is_empty_v<V>) { + if (isOk()) { + new (mValue.first().addr()) V(std::move(*aOther.mValue.first().addr())); + } + } + return *this; + } + + constexpr bool isOk() const { return mValue.second() == kNullValue; } + + constexpr const V& inspect() const { return *mValue.first().addr(); } + constexpr V unwrap() { return std::move(*mValue.first().addr()); } + constexpr void updateAfterTracing(V&& aValue) { + MOZ_ASSERT(isOk()); + if (!std::is_empty_v<V>) { + mValue.first().addr()->~V(); + new (mValue.first().addr()) V(std::move(aValue)); + } + } + + constexpr decltype(auto) inspectErr() const { + return UnusedZero<E>::Inspect(mValue.second()); + } + constexpr E unwrapErr() { return UnusedZero<E>::Unwrap(mValue.second()); } + constexpr void updateErrorAfterTracing(E&& aErrorValue) { + mValue.second() = UnusedZero<E>::Store(std::move(aErrorValue)); + } +}; + +template <typename V, typename E, + bool IsVTriviallyDestructible = std::is_trivially_destructible_v<V>> +class ResultImplementationNullIsOk; + +template <typename V, typename E> +class ResultImplementationNullIsOk<V, E, true> + : public ResultImplementationNullIsOkBase<V, E> { + public: + using ResultImplementationNullIsOkBase<V, + E>::ResultImplementationNullIsOkBase; +}; + +template <typename V, typename E> +class ResultImplementationNullIsOk<V, E, false> + : public ResultImplementationNullIsOkBase<V, E> { + public: + using ResultImplementationNullIsOkBase<V, + E>::ResultImplementationNullIsOkBase; + + ResultImplementationNullIsOk(ResultImplementationNullIsOk&&) = default; + ResultImplementationNullIsOk& operator=(ResultImplementationNullIsOk&&) = + default; + + ~ResultImplementationNullIsOk() { + if (this->isOk()) { + this->mValue.first().addr()->~V(); + } + } +}; + +/** + * Specialization for when the success type is one of integral, pointer, or + * enum, where 0 is unused, and the error type is an empty struct. + */ +template <typename V, typename E> +class ResultImplementation<V, E, PackingStrategy::ZeroIsEmptyError> { + static_assert(std::is_integral_v<V> || std::is_pointer_v<V> || + std::is_enum_v<V>); + static_assert(std::is_empty_v<E>); + + V mValue; + + public: + static constexpr PackingStrategy Strategy = PackingStrategy::ZeroIsEmptyError; + + explicit constexpr ResultImplementation(V aValue) : mValue(aValue) {} + explicit constexpr ResultImplementation(E aErrorValue) : mValue(V(0)) {} + + constexpr bool isOk() const { return mValue != V(0); } + + constexpr V inspect() const { return mValue; } + constexpr V unwrap() { return inspect(); } + + constexpr E inspectErr() const { return E(); } + constexpr E unwrapErr() { return inspectErr(); } + + constexpr void updateAfterTracing(V&& aValue) { + this->~ResultImplementation(); + new (this) ResultImplementation(std::move(aValue)); + } + constexpr void updateErrorAfterTracing(E&& aErrorValue) { + this->~ResultImplementation(); + new (this) ResultImplementation(std::move(aErrorValue)); + } +}; + +/** + * Specialization for when the success type is default-constructible and the + * error type is a value type which can never have the value 0 (as determined by + * UnusedZero<>). + */ +template <typename V, typename E> +class ResultImplementation<V, E, PackingStrategy::NullIsOk> + : public ResultImplementationNullIsOk<V, E> { + public: + static constexpr PackingStrategy Strategy = PackingStrategy::NullIsOk; + using ResultImplementationNullIsOk<V, E>::ResultImplementationNullIsOk; +}; + +template <size_t S> +using UnsignedIntType = std::conditional_t< + S == 1, std::uint8_t, + std::conditional_t< + S == 2, std::uint16_t, + std::conditional_t<S == 3 || S == 4, std::uint32_t, + std::conditional_t<S <= 8, std::uint64_t, void>>>>; + +/** + * Specialization for when alignment permits using the least significant bit + * as a tag bit. + */ +template <typename V, typename E> +class ResultImplementation<V, E, PackingStrategy::LowBitTagIsError> { + static_assert(std::is_trivially_copyable_v<V> && + std::is_trivially_destructible_v<V>); + static_assert(std::is_trivially_copyable_v<E> && + std::is_trivially_destructible_v<E>); + + static constexpr size_t kRequiredSize = std::max(sizeof(V), sizeof(E)); + + using StorageType = UnsignedIntType<kRequiredSize>; + +#if defined(__clang__) + alignas(std::max(alignof(V), alignof(E))) StorageType mBits; +#else + // Some gcc versions choke on using std::max with alignas, see + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94929 (and this seems to have + // regressed in some gcc 9.x version before being fixed again) Keeping the + // code above since we would eventually drop this when we no longer support + // gcc versions with the bug. + alignas(alignof(V) > alignof(E) ? alignof(V) : alignof(E)) StorageType mBits; +#endif + + public: + static constexpr PackingStrategy Strategy = PackingStrategy::LowBitTagIsError; + + explicit constexpr ResultImplementation(V aValue) : mBits(0) { + if constexpr (!std::is_empty_v<V>) { + std::memcpy(&mBits, &aValue, sizeof(V)); + MOZ_ASSERT((mBits & 1) == 0); + } else { + (void)aValue; + } + } + explicit constexpr ResultImplementation(E aErrorValue) : mBits(1) { + if constexpr (!std::is_empty_v<E>) { + std::memcpy(&mBits, &aErrorValue, sizeof(E)); + MOZ_ASSERT((mBits & 1) == 0); + mBits |= 1; + } else { + (void)aErrorValue; + } + } + + constexpr bool isOk() const { return (mBits & 1) == 0; } + + constexpr V inspect() const { + V res; + std::memcpy(&res, &mBits, sizeof(V)); + return res; + } + constexpr V unwrap() { return inspect(); } + + constexpr E inspectErr() const { + const auto bits = mBits ^ 1; + E res; + std::memcpy(&res, &bits, sizeof(E)); + return res; + } + constexpr E unwrapErr() { return inspectErr(); } + + constexpr void updateAfterTracing(V&& aValue) { + this->~ResultImplementation(); + new (this) ResultImplementation(std::move(aValue)); + } + constexpr void updateErrorAfterTracing(E&& aErrorValue) { + this->~ResultImplementation(); + new (this) ResultImplementation(std::move(aErrorValue)); + } +}; + +// Return true if any of the struct can fit in a word. +template <typename V, typename E> +struct IsPackableVariant { + struct VEbool { + explicit constexpr VEbool(V&& aValue) : v(std::move(aValue)), ok(true) {} + explicit constexpr VEbool(E&& aErrorValue) + : e(std::move(aErrorValue)), ok(false) {} + V v; + E e; + bool ok; + }; + struct EVbool { + explicit constexpr EVbool(V&& aValue) : v(std::move(aValue)), ok(true) {} + explicit constexpr EVbool(E&& aErrorValue) + : e(std::move(aErrorValue)), ok(false) {} + E e; + V v; + bool ok; + }; + + using Impl = + std::conditional_t<sizeof(VEbool) <= sizeof(EVbool), VEbool, EVbool>; + + static const bool value = sizeof(Impl) <= sizeof(uintptr_t); +}; + +/** + * Specialization for when both type are not using all the bytes, in order to + * use one byte as a tag. + */ +template <typename V, typename E> +class ResultImplementation<V, E, PackingStrategy::PackedVariant> { + using Impl = typename IsPackableVariant<V, E>::Impl; + Impl data; + + public: + static constexpr PackingStrategy Strategy = PackingStrategy::PackedVariant; + + explicit constexpr ResultImplementation(V aValue) : data(std::move(aValue)) {} + explicit constexpr ResultImplementation(E aErrorValue) + : data(std::move(aErrorValue)) {} + + constexpr bool isOk() const { return data.ok; } + + constexpr const V& inspect() const { return data.v; } + constexpr V unwrap() { return std::move(data.v); } + + constexpr const E& inspectErr() const { return data.e; } + constexpr E unwrapErr() { return std::move(data.e); } + + constexpr void updateAfterTracing(V&& aValue) { + MOZ_ASSERT(data.ok); + this->~ResultImplementation(); + new (this) ResultImplementation(std::move(aValue)); + } + constexpr void updateErrorAfterTracing(E&& aErrorValue) { + MOZ_ASSERT(!data.ok); + this->~ResultImplementation(); + new (this) ResultImplementation(std::move(aErrorValue)); + } +}; + +// To use nullptr as a special value, we need the counter part to exclude zero +// from its range of valid representations. +// +// By default assume that zero can be represented. +template <typename T> +struct UnusedZero { + static const bool value = false; +}; + +// This template can be used as a helper for specializing UnusedZero for scoped +// enum types which never use 0 as an error value, e.g. +// +// namespace mozilla::detail { +// +// template <> +// struct UnusedZero<MyEnumType> : UnusedZeroEnum<MyEnumType> {}; +// +// } // namespace mozilla::detail +// +template <typename T> +struct UnusedZeroEnum { + using StorageType = std::underlying_type_t<T>; + + static constexpr bool value = true; + static constexpr StorageType nullValue = 0; + + static constexpr T Inspect(const StorageType& aValue) { + return static_cast<T>(aValue); + } + static constexpr T Unwrap(StorageType aValue) { + return static_cast<T>(aValue); + } + static constexpr StorageType Store(T aValue) { + return static_cast<StorageType>(aValue); + } +}; + +// A bit of help figuring out which of the above specializations to use. +// +// We begin by safely assuming types don't have a spare bit, unless they are +// empty. +template <typename T> +struct HasFreeLSB { + static const bool value = std::is_empty_v<T>; +}; + +// As an incomplete type, void* does not have a spare bit. +template <> +struct HasFreeLSB<void*> { + static const bool value = false; +}; + +// The lowest bit of a properly-aligned pointer is always zero if the pointee +// type is greater than byte-aligned. That bit is free to use if it's masked +// out of such pointers before they're dereferenced. +template <typename T> +struct HasFreeLSB<T*> { + static const bool value = (alignof(T) & 1) == 0; +}; + +// Select one of the previous result implementation based on the properties of +// the V and E types. +template <typename V, typename E> +struct SelectResultImpl { + static const PackingStrategy value = + (UnusedZero<V>::value && std::is_empty_v<E>) + ? PackingStrategy::ZeroIsEmptyError + : (HasFreeLSB<V>::value && HasFreeLSB<E>::value) + ? PackingStrategy::LowBitTagIsError + : (UnusedZero<E>::value && sizeof(E) <= sizeof(uintptr_t)) + ? PackingStrategy::NullIsOk + : (std::is_default_constructible_v<V> && + std::is_default_constructible_v<E> && IsPackableVariant<V, E>::value) + ? PackingStrategy::PackedVariant + : PackingStrategy::Variant; + + using Type = ResultImplementation<V, E, value>; +}; + +template <typename T> +struct IsResult : std::false_type {}; + +template <typename V, typename E> +struct IsResult<Result<V, E>> : std::true_type {}; + +} // namespace detail + +template <typename V, typename E> +constexpr auto ToResult(Result<V, E>&& aValue) + -> decltype(std::forward<Result<V, E>>(aValue)) { + return std::forward<Result<V, E>>(aValue); +} + +/** + * Result<V, E> represents the outcome of an operation that can either succeed + * or fail. It contains either a success value of type V or an error value of + * type E. + * + * All Result methods are const, so results are basically immutable. + * This is just like Variant<V, E> but with a slightly different API, and the + * following cases are optimized so Result can be stored more efficiently: + * + * - If both the success and error types do not use their least significant bit, + * are trivially copyable and destructible, Result<V, E> is guaranteed to be as + * large as the larger type. This is determined via the HasFreeLSB trait. By + * default, empty classes (in particular Ok) and aligned pointer types are + * assumed to have a free LSB, but you can specialize this trait for other + * types. If the success type is empty, the representation is guaranteed to be + * all zero bits on success. Do not change this representation! There is JIT + * code that depends on it. (Implementation note: The lowest bit is used as a + * tag bit: 0 to indicate the Result's bits are a success value, 1 to indicate + * the Result's bits (with the 1 masked out) encode an error value) + * + * - Else, if the error type can't have a all-zero bits representation and is + * not larger than a pointer, a CompactPair is used to represent this rather + * than a Variant. This has shown to be better optimizable, and the template + * code is much simpler than that of Variant, so it should also compile faster. + * Whether an error type can't be all-zero bits, is determined via the + * UnusedZero trait. MFBT doesn't declare any public type UnusedZero, but + * nsresult is declared UnusedZero in XPCOM. + * + * The purpose of Result is to reduce the screwups caused by using `false` or + * `nullptr` to indicate errors. + * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for + * a partial list. + * + * Result<const V, E> or Result<V, const E> are not meaningful. The success or + * error values in a Result instance are non-modifiable in-place anyway. This + * guarantee must also be maintained when evolving Result. They can be + * unwrap()ped, but this loses const qualification. However, Result<const V, E> + * or Result<V, const E> may be misleading and prevent movability. Just use + * Result<V, E>. (Result<const V*, E> may make sense though, just Result<const + * V* const, E> is not possible.) + */ +template <typename V, typename E> +class [[nodiscard]] Result final { + // See class comment on Result<const V, E> and Result<V, const E>. + static_assert(!std::is_const_v<V>); + static_assert(!std::is_const_v<E>); + static_assert(!std::is_reference_v<V>); + static_assert(!std::is_reference_v<E>); + + using Impl = typename detail::SelectResultImpl<V, E>::Type; + + Impl mImpl; + // Are you getting this error? + // > error: implicit instantiation of undefined template + // > 'mozilla::detail::ResultImplementation<$V,$E, + // > mozilla::detail::PackingStrategy::Variant>' + // You need to include "ResultVariant.h"! + + public: + static constexpr detail::PackingStrategy Strategy = Impl::Strategy; + using ok_type = V; + using err_type = E; + + /** Create a success result. */ + MOZ_IMPLICIT constexpr Result(V&& aValue) : mImpl(std::move(aValue)) { + MOZ_ASSERT(isOk()); + } + + /** Create a success result. */ + MOZ_IMPLICIT constexpr Result(const V& aValue) : mImpl(aValue) { + MOZ_ASSERT(isOk()); + } + + /** Create a success result in-place. */ + template <typename... Args> + explicit constexpr Result(std::in_place_t, Args&&... aArgs) + : mImpl(std::in_place, std::forward<Args>(aArgs)...) { + MOZ_ASSERT(isOk()); + } + + /** Create an error result. */ + explicit constexpr Result(const E& aErrorValue) : mImpl(aErrorValue) { + MOZ_ASSERT(isErr()); + } + explicit constexpr Result(E&& aErrorValue) : mImpl(std::move(aErrorValue)) { + MOZ_ASSERT(isErr()); + } + + /** + * Create a (success/error) result from another (success/error) result with + * different but convertible value and error types. + */ + template <typename V2, typename E2, + typename = std::enable_if_t<std::is_convertible_v<V2, V> && + std::is_convertible_v<E2, E>>> + MOZ_IMPLICIT constexpr Result(Result<V2, E2>&& aOther) + : mImpl(aOther.isOk() ? Impl{aOther.unwrap()} + : Impl{aOther.unwrapErr()}) {} + + /** + * Implementation detail of MOZ_TRY(). + * Create an error result from another error result. + */ + template <typename E2> + MOZ_IMPLICIT constexpr Result(GenericErrorResult<E2>&& aErrorResult) + : mImpl(std::move(aErrorResult.mErrorValue)) { + static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E"); + MOZ_ASSERT(isErr()); + } + + /** + * Implementation detail of MOZ_TRY(). + * Create an error result from another error result. + */ + template <typename E2> + MOZ_IMPLICIT constexpr Result(const GenericErrorResult<E2>& aErrorResult) + : mImpl(aErrorResult.mErrorValue) { + static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E"); + MOZ_ASSERT(isErr()); + } + + Result(const Result&) = delete; + Result(Result&&) = default; + Result& operator=(const Result&) = delete; + Result& operator=(Result&&) = default; + + /** True if this Result is a success result. */ + constexpr bool isOk() const { return mImpl.isOk(); } + + /** True if this Result is an error result. */ + constexpr bool isErr() const { return !mImpl.isOk(); } + + /** Take the success value from this Result, which must be a success result. + */ + constexpr V unwrap() { + MOZ_ASSERT(isOk()); + return mImpl.unwrap(); + } + + /** + * Take the success value from this Result, which must be a success result. + * If it is an error result, then return the aValue. + */ + constexpr V unwrapOr(V aValue) { + return MOZ_LIKELY(isOk()) ? mImpl.unwrap() : std::move(aValue); + } + + /** Take the error value from this Result, which must be an error result. */ + constexpr E unwrapErr() { + MOZ_ASSERT(isErr()); + return mImpl.unwrapErr(); + } + + /** Used only for GC tracing. If used in Rooted<Result<...>>, V must have a + * GCPolicy for tracing it. */ + constexpr void updateAfterTracing(V&& aValue) { + mImpl.updateAfterTracing(std::move(aValue)); + } + + /** Used only for GC tracing. If used in Rooted<Result<...>>, E must have a + * GCPolicy for tracing it. */ + constexpr void updateErrorAfterTracing(E&& aErrorValue) { + mImpl.updateErrorAfterTracing(std::move(aErrorValue)); + } + + /** See the success value from this Result, which must be a success result. */ + constexpr decltype(auto) inspect() const { + static_assert(!std::is_reference_v< + std::invoke_result_t<decltype(&Impl::inspect), Impl>> || + std::is_const_v<std::remove_reference_t< + std::invoke_result_t<decltype(&Impl::inspect), Impl>>>); + MOZ_ASSERT(isOk()); + return mImpl.inspect(); + } + + /** See the error value from this Result, which must be an error result. */ + constexpr decltype(auto) inspectErr() const { + static_assert( + !std::is_reference_v< + std::invoke_result_t<decltype(&Impl::inspectErr), Impl>> || + std::is_const_v<std::remove_reference_t< + std::invoke_result_t<decltype(&Impl::inspectErr), Impl>>>); + MOZ_ASSERT(isErr()); + return mImpl.inspectErr(); + } + + /** Propagate the error value from this Result, which must be an error result. + * + * This can be used to propagate an error from a function call to the caller + * with a different value type, but the same error type: + * ... etc. - the rest is truncated