| /* |
| * Copyright 2026 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SRC_PARTITION_ALLOC_NOOP_RAW_REF_H_ |
| #define SRC_PARTITION_ALLOC_NOOP_RAW_REF_H_ |
| |
| // `raw_ref<T>` 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, skia provides a "no-op" implementation, because partition_alloc dependendency is missing. |
| |
| #include <functional> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "src/partition_alloc/raw_ptr.h" |
| |
| namespace partition_alloc::internal { |
| |
| template <class T, RawPtrTraits Traits> |
| class raw_ref; |
| |
| template <class T> |
| struct is_raw_ref : std::false_type {}; |
| |
| template <class T, RawPtrTraits Traits> |
| struct is_raw_ref<raw_ref<T, Traits>> : std::true_type {}; |
| |
| template <class T> |
| constexpr inline bool is_raw_ref_v = is_raw_ref<T>::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<T>` when the smart pointer should not be able to rebind to a new reference. |
| // Use a `const raw_ref<const T>` do the same for a const reference, which is like `const T&`. |
| // |
| // Unlike a native `T&` reference, a mutable `raw_ref<T>` 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 T, RawPtrTraits Traits = 0> |
| class SK_PA_TRIVIAL_ABI raw_ref { |
| public: |
| using sk_is_trivially_relocatable = std::true_type; |
| |
| // Construct a raw_ref from a pointer, which must not be null. |
| SK_PA_ALWAYS_INLINE constexpr static raw_ref from_ptr(T* ptr) noexcept { return raw_ref(*ptr); } |
| |
| // Construct a raw_ref from a reference. |
| SK_PA_ALWAYS_INLINE constexpr explicit raw_ref(T& p) noexcept : inner_(std::addressof(p)) {} |
| |
| // Assign a new reference to the raw_ref, replacing the existing reference. |
| SK_PA_ALWAYS_INLINE constexpr raw_ref& operator=(T& p) noexcept { |
| inner_.operator=(&p); |
| return *this; |
| } |
| |
| // Disallow holding references to temporaries. |
| explicit raw_ref(const T&& p) = delete; |
| raw_ref& operator=(const T&& p) = delete; |
| |
| SK_PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref& p) noexcept : inner_(p.inner_) {} |
| |
| SK_PA_ALWAYS_INLINE constexpr raw_ref(raw_ref&& p) noexcept : inner_(std::move(p.inner_)) { |
| p.inner_ = nullptr; |
| } |
| |
| SK_PA_ALWAYS_INLINE constexpr raw_ref& operator=(const raw_ref& p) noexcept { |
| inner_.operator=(p.inner_); |
| return *this; |
| } |
| |
| SK_PA_ALWAYS_INLINE constexpr raw_ref& operator=(raw_ref&& p) noexcept { |
| inner_.operator=(std::move(p.inner_)); |
| p.inner_ = nullptr; |
| return *this; |
| } |
| |
| // Deliberately implicit in order to support implicit upcast. |
| // Delegate cross-kind conversion to the inner raw_ptr, which decides when to allow it. |
| template <class U, |
| RawPtrTraits PassedTraits, |
| class = std::enable_if_t<std::is_convertible_v<U&, T&>>> |
| // NOLINTNEXTLINE |
| SK_PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref<U, PassedTraits>& p) noexcept |
| : inner_(p.inner_) {} |
| // Deliberately implicit in order to support implicit upcast. |
| // Delegate cross-kind conversion to the inner raw_ptr, which decides when to allow it. |
| template <class U, |
| RawPtrTraits PassedTraits, |
| class = std::enable_if_t<std::is_convertible_v<U&, T&>>> |
| // NOLINTNEXTLINE |
| SK_PA_ALWAYS_INLINE constexpr raw_ref(raw_ref<U, PassedTraits>&& p) noexcept |
| : inner_(std::move(p.inner_)) { |
| p.inner_ = nullptr; |
| } |
| |
| // Upcast assignment |
| // Delegate cross-kind conversion to the inner raw_ptr, which decides when to allow it. |
| template <class U, |
| RawPtrTraits PassedTraits, |
| class = std::enable_if_t<std::is_convertible_v<U&, T&>>> |
| SK_PA_ALWAYS_INLINE constexpr raw_ref& operator=(const raw_ref<U, PassedTraits>& p) noexcept { |
| inner_.operator=(p.inner_); |
| return *this; |
| } |
| // Delegate cross-kind conversion to the inner raw_ptr, which decides when to |
| // allow it. |
| template <class U, |
| RawPtrTraits PassedTraits, |
| class = std::enable_if_t<std::is_convertible_v<U&, T&>>> |
| SK_PA_ALWAYS_INLINE constexpr raw_ref& operator=(raw_ref<U, PassedTraits>&& p) noexcept { |
| inner_.operator=(std::move(p.inner_)); |
| p.inner_ = nullptr; |
| return *this; |
| } |
| |
| SK_PA_ALWAYS_INLINE constexpr T& operator*() const { return inner_.operator*(); } |
| SK_PA_ALWAYS_INLINE constexpr T& get() const { return *inner_.get(); } |
| SK_PA_ALWAYS_INLINE constexpr T* operator->() const { |
| return inner_.operator->(); |
| } |
| |
| SK_PA_ALWAYS_INLINE friend constexpr void swap(raw_ref& lhs, raw_ref& rhs) noexcept { |
| swap(lhs.inner_, rhs.inner_); |
| } |
| |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| friend bool operator==(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs); |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| friend bool operator!=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs); |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| friend bool operator<(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs); |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| friend bool operator>(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs); |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| friend bool operator<=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs); |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| friend bool operator>=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs); |
| |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator==(const raw_ref& lhs, const U& rhs) { |
| return lhs.inner_ == &rhs; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator!=(const raw_ref& lhs, const U& rhs) { |
| return lhs.inner_ != &rhs; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator<(const raw_ref& lhs, const U& rhs) { |
| return lhs.inner_ < &rhs; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator>(const raw_ref& lhs, const U& rhs) { |
| return lhs.inner_ > &rhs; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator<=(const raw_ref& lhs, const U& rhs) { |
| return lhs.inner_ <= &rhs; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator>=(const raw_ref& lhs, const U& rhs) { |
| return lhs.inner_ >= &rhs; |
| } |
| |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator==(const U& lhs, const raw_ref& rhs) { |
| return &lhs == rhs.inner_; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator!=(const U& lhs, const raw_ref& rhs) { |
| return &lhs != rhs.inner_; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator<(const U& lhs, const raw_ref& rhs) { |
| return &lhs < rhs.inner_; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator>(const U& lhs, const raw_ref& rhs) { |
| return &lhs > rhs.inner_; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator<=(const U& lhs, const raw_ref& rhs) { |
| return &lhs <= rhs.inner_; |
| } |
| template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> |
| SK_PA_ALWAYS_INLINE friend bool operator>=(const U& lhs, const raw_ref& rhs) { |
| return &lhs >= rhs.inner_; |
| } |
| |
| private: |
| template <class U, RawPtrTraits R> |
| friend class raw_ref; |
| |
| raw_ptr<T, Traits> inner_; |
| }; |
| |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| SK_PA_ALWAYS_INLINE bool operator==(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) { |
| return lhs.inner_ == rhs.inner_; |
| } |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| SK_PA_ALWAYS_INLINE bool operator!=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) { |
| return lhs.inner_ != rhs.inner_; |
| } |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| SK_PA_ALWAYS_INLINE bool operator<(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) { |
| return lhs.inner_ < rhs.inner_; |
| } |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| SK_PA_ALWAYS_INLINE bool operator>(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) { |
| return lhs.inner_ > rhs.inner_; |
| } |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| SK_PA_ALWAYS_INLINE bool operator<=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) { |
| return lhs.inner_ <= rhs.inner_; |
| } |
| template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> |
| SK_PA_ALWAYS_INLINE bool operator>=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) { |
| return lhs.inner_ >= rhs.inner_; |
| } |
| |
| // CTAD deduction guide. |
| template <class T> |
| raw_ref(T&) -> raw_ref<T>; |
| template <class T> |
| raw_ref(const T&) -> raw_ref<const T>; |
| |
| } // namespace partition_alloc::internal |
| |
| 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 <typename T, RawPtrTraits Traits> |
| struct less<raw_ref<T, Traits>> { |
| using is_transparent = void; |
| |
| bool operator()(const raw_ref<T, Traits>& lhs, const raw_ref<T, Traits>& rhs) const { |
| return lhs < rhs; |
| } |
| bool operator()(T& lhs, const raw_ref<T, Traits>& rhs) const { return lhs < rhs; } |
| bool operator()(const raw_ref<T, Traits>& 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 [3]. |
| // |
| // [1] https://wg21.link/pointer.traits.optmem |
| template <typename T, ::RawPtrTraits Traits> |
| struct pointer_traits<::raw_ref<T, Traits>> { |
| using pointer = ::raw_ref<T, Traits>; |
| using element_type = T; |
| using difference_type = ptrdiff_t; |
| |
| template <typename U> |
| using rebind = ::raw_ref<U, Traits>; |
| |
| static constexpr pointer pointer_to(element_type& r) noexcept { return pointer(r); } |
| |
| static constexpr element_type* to_address(pointer p) noexcept { |
| // `raw_ref::get` is used instead of raw_ref::operator*`. It provides GetForExtraction |
| // rather rather than GetForDereference semantics (see raw_ptr.h). This should be used when |
| // we we don't know the memory will be accessed. |
| return &(p.get()); |
| } |
| }; |
| |
| } // namespace std |
| |
| #endif // SRC_PARTITION_ALLOC_NOOP_RAW_REF_H_ |