layout/base/UniqueOrNonOwningPtr.h (78 lines of code) (raw):

/* 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 <cstdint> #include <utility> #include "mozilla/Assertions.h" namespace mozilla { template <typename T> class UniqueOrNonOwningPtr; namespace detail { template <typename T> struct UniqueOfUniqueOrNonOwningSelector { using SingleObject = UniqueOrNonOwningPtr<T>; }; template <typename T> struct UniqueOfUniqueOrNonOwningSelector<T[]>; template <typename T, decltype(sizeof(int)) N> struct UniqueOfUniqueOrNonOwningSelector<T[N]>; } // 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 T, typename... Args> typename detail::UniqueOfUniqueOrNonOwningSelector<T>::SingleObject MakeUniqueOfUniqueOrNonOwning(Args&&... aArgs) { return UniqueOrNonOwningPtr<T>::UniquelyOwning(new T(std::forward<Args>(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<T*, UniquePtr<T>>`, 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 <typename T> 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<uintptr_t>(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<uintptr_t>(aPtr); MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, "Odd-aligned non-owning pointer?"); return UniqueOrNonOwningPtr{bits}; } std::add_lvalue_reference_t<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<T*>(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 <typename T> class UniqueOrNonOwningPtr<T[]>; } // namespace mozilla #endif