// Copyright 2026 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef COMMON_PARTITION_ALLOC_NOOP_RAW_REF_H_ #define COMMON_PARTITION_ALLOC_NOOP_RAW_REF_H_ // `raw_ref` is a non-owning smart pointer that has improved memory-safety // over raw reference. See the documentation for details: // https://source.chromium.org/chromium/chromium/src/+/main:base/memory/raw_ref.md // // Here, ANGLE provides a "no-op" implementation when PartitionAlloc is absent // (e.g. in embedder checkouts). // // This file is adapted from Skia's src/partition_alloc/noop/raw_ref.h: // https://skia.googlesource.com/skia/+/refs/heads/main/src/partition_alloc/noop/raw_ref.h // and Chromium's PartitionAlloc pointers implementation: // https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ref.h #include #include #include #include #include "common/angleutils.h" #include "common/partition_alloc/noop/raw_ptr.h" namespace partition_alloc { namespace internal { template class raw_ref; template struct is_raw_ref : std::false_type {}; template struct is_raw_ref> : std::true_type {}; template constexpr inline bool is_raw_ref_v = is_raw_ref::value; // A smart pointer for a pointer which can not be null, and which provides Use-after-Free protection // in the same ways as raw_ptr. This class acts like a combination of std::reference_wrapper and // raw_ptr. // // See raw_ptr and Chrome's //base/memory/raw_ptr.md for more details on the Use-after-Free // protection. // // # Use after move // // The raw_ref type will abort if used after being moved. // // # Constness // // Use a `const raw_ref` when the smart pointer should not be able to rebind to a new reference. // Use a `const raw_ref` do the same for a const reference, which is like `const T&`. // // Unlike a native `T&` reference, a mutable `raw_ref` can be changed independent of the // underlying `T`, similar to `std::reference_wrapper`. That means the reference inside it can be // moved and reassigned. // RawPtrTraits are documented in Chromium's `base/memory/raw_ptr.md`. template class ANGLE_TRIVIAL_ABI ANGLE_GSL_POINTER raw_ref { public: ANGLE_INLINE constexpr static raw_ref from_ptr(T *ptr) noexcept { return raw_ref(*ptr); } ANGLE_INLINE constexpr explicit raw_ref(T &p) noexcept : inner_(std::addressof(p)) {} ANGLE_INLINE constexpr raw_ref &operator=(T &p) noexcept { inner_.operator=(std::addressof(p)); return *this; } explicit raw_ref(const T &&p) = delete; raw_ref &operator=(const T &&p) = delete; ANGLE_INLINE constexpr raw_ref(const raw_ref &p) noexcept = default; ANGLE_INLINE constexpr raw_ref(raw_ref &&p) noexcept : inner_(std::move(p.inner_)) { p.inner_ = nullptr; } ANGLE_INLINE constexpr raw_ref &operator=(const raw_ref &p) noexcept = default; ANGLE_INLINE constexpr raw_ref &operator=(raw_ref &&p) noexcept { if (this != &p) { inner_.operator=(std::move(p.inner_)); p.inner_ = nullptr; } return *this; } template >> // NOLINTNEXTLINE ANGLE_INLINE constexpr raw_ref(const raw_ref &p) noexcept : inner_(p.inner_) {} template >> // NOLINTNEXTLINE ANGLE_INLINE constexpr raw_ref(raw_ref &&p) noexcept : inner_(std::move(p.inner_)) { p.inner_ = nullptr; } template >> ANGLE_INLINE constexpr raw_ref &operator=(const raw_ref &p) noexcept { inner_.operator=(p.inner_); return *this; } template >> ANGLE_INLINE constexpr raw_ref &operator=(raw_ref &&p) noexcept { if (static_cast(this) != static_cast(std::addressof(p))) { inner_.operator=(std::move(p.inner_)); p.inner_ = nullptr; } return *this; } ANGLE_INLINE constexpr T &operator*() const { return inner_.operator*(); } ANGLE_INLINE constexpr T &get() const { return *inner_.get(); } ANGLE_INLINE constexpr T *operator->() const { return inner_.operator->(); } ANGLE_INLINE friend constexpr void swap(raw_ref &lhs, raw_ref &rhs) noexcept { swap(lhs.inner_, rhs.inner_); } template friend bool operator==(const raw_ref &lhs, const raw_ref &rhs); template friend bool operator!=(const raw_ref &lhs, const raw_ref &rhs); template friend bool operator<(const raw_ref &lhs, const raw_ref &rhs); template friend bool operator>(const raw_ref &lhs, const raw_ref &rhs); template friend bool operator<=(const raw_ref &lhs, const raw_ref &rhs); template friend bool operator>=(const raw_ref &lhs, const raw_ref &rhs); template , void>> ANGLE_INLINE friend bool operator==(const raw_ref &lhs, const U &rhs) { return lhs.inner_ == std::addressof(rhs); } template , void>> ANGLE_INLINE friend bool operator!=(const raw_ref &lhs, const U &rhs) { return lhs.inner_ != std::addressof(rhs); } template , void>> ANGLE_INLINE friend bool operator<(const raw_ref &lhs, const U &rhs) { return lhs.inner_ < std::addressof(rhs); } template , void>> ANGLE_INLINE friend bool operator>(const raw_ref &lhs, const U &rhs) { return lhs.inner_ > std::addressof(rhs); } template , void>> ANGLE_INLINE friend bool operator<=(const raw_ref &lhs, const U &rhs) { return lhs.inner_ <= std::addressof(rhs); } template , void>> ANGLE_INLINE friend bool operator>=(const raw_ref &lhs, const U &rhs) { return lhs.inner_ >= std::addressof(rhs); } template , void>> ANGLE_INLINE friend bool operator==(const U &lhs, const raw_ref &rhs) { return std::addressof(lhs) == rhs.inner_; } template , void>> ANGLE_INLINE friend bool operator!=(const U &lhs, const raw_ref &rhs) { return std::addressof(lhs) != rhs.inner_; } template , void>> ANGLE_INLINE friend bool operator<(const U &lhs, const raw_ref &rhs) { return std::addressof(lhs) < rhs.inner_; } template , void>> ANGLE_INLINE friend bool operator>(const U &lhs, const raw_ref &rhs) { return std::addressof(lhs) > rhs.inner_; } template , void>> ANGLE_INLINE friend bool operator<=(const U &lhs, const raw_ref &rhs) { return std::addressof(lhs) <= rhs.inner_; } template , void>> ANGLE_INLINE friend bool operator>=(const U &lhs, const raw_ref &rhs) { return std::addressof(lhs) >= rhs.inner_; } private: template friend class raw_ref; raw_ptr inner_; }; template ANGLE_INLINE bool operator==(const raw_ref &lhs, const raw_ref &rhs) { return lhs.inner_ == rhs.inner_; } template ANGLE_INLINE bool operator!=(const raw_ref &lhs, const raw_ref &rhs) { return lhs.inner_ != rhs.inner_; } template ANGLE_INLINE bool operator<(const raw_ref &lhs, const raw_ref &rhs) { return lhs.inner_ < rhs.inner_; } template ANGLE_INLINE bool operator>(const raw_ref &lhs, const raw_ref &rhs) { return lhs.inner_ > rhs.inner_; } template ANGLE_INLINE bool operator<=(const raw_ref &lhs, const raw_ref &rhs) { return lhs.inner_ <= rhs.inner_; } template ANGLE_INLINE bool operator>=(const raw_ref &lhs, const raw_ref &rhs) { return lhs.inner_ >= rhs.inner_; } template raw_ref(T &) -> raw_ref; template raw_ref(const T &) -> raw_ref; } // namespace internal } // namespace partition_alloc using partition_alloc::internal::raw_ref; namespace std { // Override so set/map lookups do not create extra raw_ref. This also // allows C++ references to be used for lookup. template struct less> { using is_transparent = void; bool operator()(const raw_ref &lhs, const raw_ref &rhs) const { return lhs < rhs; } bool operator()(T &lhs, const raw_ref &rhs) const { return lhs < rhs; } bool operator()(const raw_ref &lhs, T &rhs) const { return lhs < rhs; } }; // Specialize std::pointer_traits. The latter is required to obtain the // underlying raw pointer in the std::to_address(pointer) overload. // Implementing the pointer_traits is the standard blessed way to customize // `std::to_address(pointer)` in C++20 [1]. // // [1] https://wg21.link/pointer.traits.optmem template struct pointer_traits<::raw_ref> { using pointer = ::raw_ref; using element_type = T; using difference_type = ptrdiff_t; template using rebind = ::raw_ref; static constexpr pointer pointer_to(element_type &r) noexcept { return pointer(r); } static constexpr element_type *to_address(pointer p) noexcept { return std::addressof(p.get()); } }; } // namespace std #endif // COMMON_PARTITION_ALLOC_NOOP_RAW_REF_H_