/* * Copyright (c) 2018-2023, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #define REFPTR_SCRUB_BYTE 0xe0 #include #include #include #include #include #include #include #include namespace AK { template class [[nodiscard]] RefPtr { template friend class RefPtr; template friend class WeakPtr; template friend class NonnullRefPtr; public: enum AdoptTag { Adopt }; RefPtr() = default; RefPtr(T const* ptr) : m_ptr(const_cast(ptr)) { ref_if_not_null(m_ptr); } RefPtr(T const& object) : m_ptr(const_cast(&object)) { m_ptr->ref(); } RefPtr(AdoptTag, T& object) : m_ptr(&object) { } RefPtr(RefPtr&& other) : m_ptr(other.leak_ref()) { } ALWAYS_INLINE RefPtr(NonnullRefPtr const& other) : m_ptr(const_cast(other.ptr())) { m_ptr->ref(); } template requires(IsConvertible) ALWAYS_INLINE RefPtr(NonnullRefPtr const& other) : m_ptr(const_cast(static_cast(other.ptr()))) { m_ptr->ref(); } template requires(IsConvertible) ALWAYS_INLINE RefPtr(NonnullRefPtr&& other) : m_ptr(static_cast(&other.leak_ref())) { } template requires(IsConvertible) RefPtr(RefPtr&& other) : m_ptr(static_cast(other.leak_ref())) { } RefPtr(RefPtr const& other) : m_ptr(other.m_ptr) { ref_if_not_null(m_ptr); } template RefPtr(RefPtr const& other) requires(IsConvertible) : m_ptr(const_cast(static_cast(other.ptr()))) { ref_if_not_null(m_ptr); } ALWAYS_INLINE ~RefPtr() { clear(); #ifdef SANITIZE_PTRS m_ptr = reinterpret_cast(explode_byte(REFPTR_SCRUB_BYTE)); #endif } template RefPtr(OwnPtr const&) = delete; template RefPtr& operator=(OwnPtr const&) = delete; void swap(RefPtr& other) { AK::swap(m_ptr, other.m_ptr); } template void swap(RefPtr& other) requires(IsConvertible) { AK::swap(m_ptr, other.m_ptr); } ALWAYS_INLINE RefPtr& operator=(RefPtr&& other) { RefPtr tmp { move(other) }; swap(tmp); return *this; } template requires(IsConvertible) ALWAYS_INLINE RefPtr& operator=(RefPtr&& other) { RefPtr tmp { move(other) }; swap(tmp); return *this; } template requires(IsConvertible) ALWAYS_INLINE RefPtr& operator=(NonnullRefPtr&& other) { RefPtr tmp { move(other) }; swap(tmp); return *this; } ALWAYS_INLINE RefPtr& operator=(NonnullRefPtr const& other) { RefPtr tmp { other }; swap(tmp); return *this; } template requires(IsConvertible) ALWAYS_INLINE RefPtr& operator=(NonnullRefPtr const& other) { RefPtr tmp { other }; swap(tmp); return *this; } ALWAYS_INLINE RefPtr& operator=(RefPtr const& other) { RefPtr tmp { other }; swap(tmp); return *this; } template requires(IsConvertible) ALWAYS_INLINE RefPtr& operator=(RefPtr const& other) { RefPtr tmp { other }; swap(tmp); return *this; } ALWAYS_INLINE RefPtr& operator=(T const* ptr) { RefPtr tmp { ptr }; swap(tmp); return *this; } ALWAYS_INLINE RefPtr& operator=(T const& object) { RefPtr tmp { object }; swap(tmp); return *this; } RefPtr& operator=(nullptr_t) { clear(); return *this; } ALWAYS_INLINE bool assign_if_null(RefPtr&& other) { if (this == &other) return is_null(); *this = move(other); return true; } template ALWAYS_INLINE bool assign_if_null(RefPtr&& other) { if (this == &other) return is_null(); *this = move(other); return true; } ALWAYS_INLINE void clear() { auto* ptr = exchange(m_ptr, nullptr); unref_if_not_null(ptr); } bool operator!() const { return !m_ptr; } [[nodiscard]] T* leak_ref() { return exchange(m_ptr, nullptr); } NonnullRefPtr release_nonnull() { auto* ptr = leak_ref(); VERIFY(ptr); return NonnullRefPtr(NonnullRefPtr::Adopt, *ptr); } ALWAYS_INLINE T* ptr() const { return as_ptr(); } ALWAYS_INLINE T* operator->() const { return as_nonnull_ptr(); } ALWAYS_INLINE T& operator*() const { return *as_nonnull_ptr(); } ALWAYS_INLINE operator T*() const { return as_ptr(); } ALWAYS_INLINE operator bool() { return !is_null(); } bool operator==(nullptr_t) const { return is_null(); } bool operator==(RefPtr const& other) const { return as_ptr() == other.as_ptr(); } template bool operator==(NonnullRefPtr const& other) const { return as_ptr() == other.m_ptr; } template requires(IsPointer) bool operator==(RawPtr other) const { return as_ptr() == other; } ALWAYS_INLINE bool is_null() const { return !m_ptr; } private: ALWAYS_INLINE T* as_ptr() const { return m_ptr; } ALWAYS_INLINE T* as_nonnull_ptr() const { VERIFY(m_ptr); return m_ptr; } T* m_ptr { nullptr }; }; template struct Formatter> : Formatter { ErrorOr format(FormatBuilder& builder, RefPtr const& value) { return Formatter::format(builder, value.ptr()); } }; template struct Traits> : public DefaultTraits> { using PeekType = T*; using ConstPeekType = T const*; static unsigned hash(RefPtr const& p) { return ptr_hash(p.ptr()); } static bool equals(RefPtr const& a, RefPtr const& b) { return a.ptr() == b.ptr(); } }; template inline NonnullRefPtr static_ptr_cast(NonnullRefPtr const& ptr) { return NonnullRefPtr(static_cast(*ptr)); } template inline RefPtr static_ptr_cast(RefPtr const& ptr) { return RefPtr(static_cast(ptr.ptr())); } template requires(IsConvertible) inline void swap(RefPtr& a, RefPtr& b) { a.swap(b); } template inline RefPtr adopt_ref_if_nonnull(T* object) { if (object) return RefPtr(RefPtr::Adopt, *object); return {}; } } #if USING_AK_GLOBALLY using AK::adopt_ref_if_nonnull; using AK::RefPtr; using AK::static_ptr_cast; #endif