/* 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 https://mozilla.org/MPL/2.0/. */ #ifndef mozilla_UniqueOrNonOwningPtr_h #define mozilla_UniqueOrNonOwningPtr_h #include #include #include "mozilla/Assertions.h" namespace mozilla { template class UniqueOrNonOwningPtr; namespace detail { template struct UniqueOfUniqueOrNonOwningSelector { using SingleObject = UniqueOrNonOwningPtr; }; template struct UniqueOfUniqueOrNonOwningSelector; template struct UniqueOfUniqueOrNonOwningSelector; } // namespace detail // `mozilla::MakeUnique` equivalent, with the same set of advantages. // Non-owning case doesn't need this since there's no allocation. // See below as to why only SingleObject case is supported. template typename detail::UniqueOfUniqueOrNonOwningSelector::SingleObject MakeUniqueOfUniqueOrNonOwning(Args&&... aArgs) { return UniqueOrNonOwningPtr::UniquelyOwning( new T(std::forward(aArgs)...)); } /** * A pointer that is either: * * Uniquely-owning, as if `std::unique_ptr`/`mozilla::UniquePtr`, or * * Non-owning, as if raw pointer. * * Overall, it behaves like `mozilla::Variant>`, but more * compact. It may be helpful if you are mostly referencing existing data type * of significant size, but sometimes generate a modified copy and refer to * that. * * Usage notes: * * Ownership: This structure makes ownership tracking harder. It is the * caller's responsibility to ensure that, in the non-owning case, the data * outlives this pointer. * * (Ab)using the lowest bit: Owning state is tagged inline in the lowest * bit, which is set for uniquely-owning data. It does not work with a * byte-aligned data types, or members of a packed struct. There are asserts to * try and catch this as early as possible. * * TODO(dshin): This lacks support for things that `mozilla::UniquePtr` supports * - however, these cases will fail to compile. * * Deleter support (Even stateless ones) * * Interconversion (Pointing to derived from base pointer) * * T[] */ template class UniqueOrNonOwningPtr { public: // Check to make sure we can take on non-owning pointer to stack. static_assert(alignof(T) != 1, "Can't support data aligned to byte boundaries."); // Standard guarantees the null pointer value to be integer 0. UniqueOrNonOwningPtr() : mBits{0} {} UniqueOrNonOwningPtr(const UniqueOrNonOwningPtr&) = delete; UniqueOrNonOwningPtr(UniqueOrNonOwningPtr&& aOther) : mBits{aOther.mBits} { // "Release" the other one. aOther.mBits = 0; } ~UniqueOrNonOwningPtr() { if (IsUniquelyOwning()) { delete get(); } } UniqueOrNonOwningPtr& operator=(const UniqueOrNonOwningPtr& aOther) = delete; UniqueOrNonOwningPtr& operator=(UniqueOrNonOwningPtr&& aOther) { mBits = aOther.mBits; // "Release" the other one. aOther.mBits = 0; return *this; } static UniqueOrNonOwningPtr UniquelyOwning(T* aPtr) { MOZ_ASSERT(aPtr, "Passing in null pointer as owning?"); const uintptr_t bits = reinterpret_cast(aPtr); MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, "Odd-aligned owning pointer?"); return UniqueOrNonOwningPtr{bits | kUniquelyOwningBit}; } static UniqueOrNonOwningPtr NonOwning(T* aPtr) { const uintptr_t bits = reinterpret_cast(aPtr); MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, "Odd-aligned non-owning pointer?"); return UniqueOrNonOwningPtr{bits}; } std::add_lvalue_reference_t operator*() const { MOZ_ASSERT( get(), "dereferencing a UniqueOrNonOwningPtr containing nullptr with *"); return *get(); } T* operator->() const { MOZ_ASSERT( get(), "dereferencing a UniqueOrNonOwningPtr containing nullptr with ->"); return get(); } explicit operator bool() const { return get() != nullptr; } T* get() const { return reinterpret_cast(mBits & ~kUniquelyOwningBit); } private: bool IsUniquelyOwning() const { return (mBits & kUniquelyOwningBit) != 0; } // Bit for tracking uniquely-owning vs non-owning status. Check usage notes // in the main comment block. // NOTE: A null pointer constant has a guarantee on being integer literal 0. static constexpr uintptr_t kUniquelyOwningBit = 1; explicit UniqueOrNonOwningPtr(uintptr_t aValue) : mBits{aValue} {} uintptr_t mBits; }; // Unsupported template class UniqueOrNonOwningPtr; } // namespace mozilla #endif