Source code

Revision control

Copy as Markdown

Other Tools

/* 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