/* * Copyright (c) 2018-2023, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #define NONNULLREFPTR_SCRUB_BYTE 0xe1 #include #include #include #include namespace AK { template class RefPtr; template ALWAYS_INLINE void ref_if_not_null(T* ptr) { if (ptr) ptr->ref(); } template ALWAYS_INLINE void unref_if_not_null(T* ptr) { if (ptr) ptr->unref(); } template class [[nodiscard]] NonnullRefPtr { template friend class RefPtr; template friend class NonnullRefPtr; template friend class WeakPtr; public: using ElementType = T; enum AdoptTag { Adopt }; ALWAYS_INLINE NonnullRefPtr(T const& object) : m_ptr(const_cast(&object)) { m_ptr->ref(); } template requires(IsConvertible) ALWAYS_INLINE NonnullRefPtr(U const& object) : m_ptr(const_cast(static_cast(&object))) { m_ptr->ref(); } ALWAYS_INLINE NonnullRefPtr(AdoptTag, T& object) : m_ptr(&object) { } ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr&& other) : m_ptr(&other.leak_ref()) { } template requires(IsConvertible) ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr&& other) : m_ptr(static_cast(&other.leak_ref())) { } ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr const& other) : m_ptr(const_cast(other.ptr())) { m_ptr->ref(); } template requires(IsConvertible) ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr const& other) : m_ptr(const_cast(static_cast(other.ptr()))) { m_ptr->ref(); } ALWAYS_INLINE ~NonnullRefPtr() { auto* ptr = exchange(m_ptr, nullptr); unref_if_not_null(ptr); #ifdef SANITIZE_PTRS m_ptr = reinterpret_cast(explode_byte(NONNULLREFPTR_SCRUB_BYTE)); #endif } template NonnullRefPtr(OwnPtr const&) = delete; template NonnullRefPtr& operator=(OwnPtr const&) = delete; template NonnullRefPtr(RefPtr const&) = delete; template NonnullRefPtr& operator=(RefPtr const&) = delete; NonnullRefPtr(RefPtr const&) = delete; NonnullRefPtr& operator=(RefPtr const&) = delete; NonnullRefPtr& operator=(NonnullRefPtr const& other) { NonnullRefPtr tmp { other }; swap(tmp); return *this; } template requires(IsConvertible) NonnullRefPtr& operator=(NonnullRefPtr const& other) { NonnullRefPtr tmp { other }; swap(tmp); return *this; } ALWAYS_INLINE NonnullRefPtr& operator=(NonnullRefPtr&& other) { NonnullRefPtr tmp { move(other) }; swap(tmp); return *this; } template requires(IsConvertible) NonnullRefPtr& operator=(NonnullRefPtr&& other) { NonnullRefPtr tmp { move(other) }; swap(tmp); return *this; } NonnullRefPtr& operator=(T const& object) { NonnullRefPtr tmp { object }; swap(tmp); return *this; } [[nodiscard]] ALWAYS_INLINE T& leak_ref() { T* ptr = exchange(m_ptr, nullptr); VERIFY(ptr); return *ptr; } ALWAYS_INLINE RETURNS_NONNULL T* ptr() const { return as_nonnull_ptr(); } ALWAYS_INLINE RETURNS_NONNULL T* operator->() const { return as_nonnull_ptr(); } [[nodiscard]] ALWAYS_INLINE T& operator*() const { return *as_nonnull_ptr(); } ALWAYS_INLINE RETURNS_NONNULL operator T*() const { return as_nonnull_ptr(); } ALWAYS_INLINE operator T&() const { return *as_nonnull_ptr(); } operator bool() const = delete; bool operator!() const = delete; void swap(NonnullRefPtr& other) { AK::swap(m_ptr, other.m_ptr); } template void swap(NonnullRefPtr& other) requires(IsConvertible) { AK::swap(m_ptr, other.m_ptr); } bool operator==(NonnullRefPtr const& other) const { return m_ptr == other.m_ptr; } template requires(IsPointer) bool operator==(RawPtr other) const { return m_ptr == other; } private: NonnullRefPtr() = delete; ALWAYS_INLINE RETURNS_NONNULL T* as_nonnull_ptr() const { VERIFY(m_ptr); return m_ptr; } T* m_ptr { nullptr }; }; template inline NonnullRefPtr adopt_ref(T& object) { return NonnullRefPtr(NonnullRefPtr::Adopt, object); } // Use like `adopt_nonnull_ref_or_enomem(new (nothrow) T(args...))`. template inline ErrorOr> adopt_nonnull_ref_or_enomem(T* object) { if (!object) return Error::from_errno(ENOMEM); return NonnullRefPtr(NonnullRefPtr::Adopt, *object); } template requires(IsConstructible) inline ErrorOr> try_make_ref_counted(Args&&... args) { return adopt_nonnull_ref_or_enomem(new (nothrow) T(forward(args)...)); } #ifdef AK_COMPILER_APPLE_CLANG // FIXME: Remove once P0960R3 is available in Apple Clang. template inline ErrorOr> try_make_ref_counted(Args&&... args) { return adopt_nonnull_ref_or_enomem(new (nothrow) T { forward(args)... }); } #endif template struct Formatter> : Formatter { ErrorOr format(FormatBuilder& builder, NonnullRefPtr const& value) { return Formatter::format(builder, *value); } }; template requires(!HasFormatter) struct Formatter> : Formatter { ErrorOr format(FormatBuilder& builder, NonnullRefPtr const& value) { return Formatter::format(builder, value.ptr()); } }; template requires(IsConvertible) inline void swap(NonnullRefPtr& a, NonnullRefPtr& b) { a.swap(b); } template requires(IsConstructible) inline NonnullRefPtr make_ref_counted(Args&&... args) { return NonnullRefPtr(NonnullRefPtr::Adopt, *new T(forward(args)...)); } #ifdef AK_COMPILER_APPLE_CLANG // FIXME: Remove once P0960R3 is available in Apple Clang. template inline NonnullRefPtr make_ref_counted(Args&&... args) { return NonnullRefPtr(NonnullRefPtr::Adopt, *new T { forward(args)... }); } #endif template struct Traits> : public DefaultTraits> { using PeekType = T*; using ConstPeekType = T const*; static unsigned hash(NonnullRefPtr const& p) { return ptr_hash(p.ptr()); } static bool equals(NonnullRefPtr const& a, NonnullRefPtr const& b) { return a.ptr() == b.ptr(); } }; } #if USING_AK_GLOBALLY using AK::adopt_nonnull_ref_or_enomem; using AK::adopt_ref; using AK::make_ref_counted; using AK::NonnullRefPtr; using AK::try_make_ref_counted; #endif