/* 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 intl_components_ICUError_h #define intl_components_ICUError_h #include "mozilla/Attributes.h" #include "mozilla/Result.h" #include #include namespace mozilla::intl { /** * General purpose error type for operations that can result in an ICU error. */ enum class ICUError : uint8_t { // Since we claim UnusedZero::value and // HasFreeLSB::value == true below, we must only use positive, // even enum values. OutOfMemory = 2, InternalError = 4, OverflowError = 6, }; /** * Error type when a method call can only result in an internal ICU error. */ struct InternalError { // Since we claim UnusedZero::value and // HasFreeLSB::value == true below, we must only use positive, // even enum values. enum class ErrorKind : uint8_t { Unspecified = 2 }; const ErrorKind kind = ErrorKind::Unspecified; constexpr InternalError() = default; private: friend struct mozilla::detail::UnusedZero; constexpr MOZ_IMPLICIT InternalError(ErrorKind aKind) : kind(aKind) {} }; } // namespace mozilla::intl namespace mozilla::detail { // Provide specializations for UnusedZero and HasFreeLSB to enable more // efficient packing for mozilla::Result. This also avoids having to include // the ResultVariant.h header. // // UnusedZero specialization: // // The UnusedZero specialization makes it possible to use CompactPair as the // underlying storage type for Result. For this optimization to work, it is // necessary that a distinct null-value is present for the error type. The // null-value represents the success case and must be different from all actual // error values. // This optimization can be easily enabled when the error type is a scoped enum. // No enum value must use zero as its value and UnusedZero must be specialized // through the helper struct UnusedZeroEnum. // For non-enum error types, a more complicated setup is necessary. The // UnusedZero specialization must implement all necessary interface methods // (i.e. `Inspect`, `Unwrap`, and `Store`) as well as all necessary constants // and types (i.e. `StorageType`, `value`, and `nullValue`). // // HasFreeLSB specialization: // // When the value and the error type are both providing specializations for // HasFreeLSB, Result uses an optimization to store both types within a single // storage location. This optimization uses the least significant bit as a tag // bit to mark the error case. And because the least significant bit is used for // tagging, it can't be used by the error type. That means for example when the // error type is an enum, all enum values must be even, because odd integer // values have the least significant bit set. // The actual HasFreeLSB specialization just needs to define `value` as a static // constant with the value `true`. template <> struct UnusedZero : UnusedZeroEnum {}; template <> struct UnusedZero { using Error = mozilla::intl::InternalError; using StorageType = std::underlying_type_t; static constexpr bool value = true; static constexpr StorageType nullValue = 0; static constexpr Error Inspect(const StorageType& aValue) { return static_cast(aValue); } static constexpr Error Unwrap(StorageType aValue) { return static_cast(aValue); } static constexpr StorageType Store(Error aValue) { return static_cast(aValue.kind); } }; template <> struct HasFreeLSB { static constexpr bool value = true; }; template <> struct HasFreeLSB { static constexpr bool value = true; }; } // namespace mozilla::detail #endif