143 lines
4.7 KiB
C++
143 lines
4.7 KiB
C++
/* 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
|