tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UniqueOrNonOwningPtr.h (4782B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef mozilla_UniqueOrNonOwningPtr_h
      6 #define mozilla_UniqueOrNonOwningPtr_h
      7 
      8 #include <cstdint>
      9 #include <utility>
     10 
     11 #include "mozilla/Assertions.h"
     12 
     13 namespace mozilla {
     14 
     15 template <typename T>
     16 class UniqueOrNonOwningPtr;
     17 
     18 namespace detail {
     19 
     20 template <typename T>
     21 struct UniqueOfUniqueOrNonOwningSelector {
     22  using SingleObject = UniqueOrNonOwningPtr<T>;
     23 };
     24 
     25 template <typename T>
     26 struct UniqueOfUniqueOrNonOwningSelector<T[]>;
     27 
     28 template <typename T, decltype(sizeof(int)) N>
     29 struct UniqueOfUniqueOrNonOwningSelector<T[N]>;
     30 
     31 }  // namespace detail
     32 
     33 // `mozilla::MakeUnique` equivalent, with the same set of advantages.
     34 // Non-owning case doesn't need this since there's no allocation.
     35 // See below as to why only SingleObject case is supported.
     36 template <typename T, typename... Args>
     37 typename detail::UniqueOfUniqueOrNonOwningSelector<T>::SingleObject
     38 MakeUniqueOfUniqueOrNonOwning(Args&&... aArgs) {
     39  return UniqueOrNonOwningPtr<T>::UniquelyOwning(
     40      new T(std::forward<Args>(aArgs)...));
     41 }
     42 
     43 /**
     44 * A pointer that is either:
     45 *   * Uniquely-owning, as if `std::unique_ptr`/`mozilla::UniquePtr`, or
     46 *   * Non-owning, as if raw pointer.
     47 *
     48 * Overall, it behaves like `mozilla::Variant<T*, UniquePtr<T>>`, but more
     49 * compact. It may be helpful if you are mostly referencing existing data type
     50 * of significant size, but sometimes generate a modified copy and refer to
     51 * that.
     52 *
     53 * Usage notes:
     54 *   * Ownership: This structure makes ownership tracking harder. It is the
     55 *     caller's responsibility to ensure that, in the non-owning case, the data
     56 *     outlives this pointer.
     57 *   * (Ab)using the lowest bit: Owning state is tagged inline in the lowest
     58 * bit, which is set for uniquely-owning data. It does not work with a
     59 * byte-aligned data types, or members of a packed struct. There are asserts to
     60 * try and catch this as early as possible.
     61 *
     62 * TODO(dshin): This lacks support for things that `mozilla::UniquePtr` supports
     63 * - however, these cases will fail to compile.
     64 *   * Deleter support (Even stateless ones)
     65 *   * Interconversion (Pointing to derived from base pointer)
     66 *   * T[]
     67 */
     68 template <typename T>
     69 class UniqueOrNonOwningPtr {
     70 public:
     71  // Check to make sure we can take on non-owning pointer to stack.
     72  static_assert(alignof(T) != 1,
     73                "Can't support data aligned to byte boundaries.");
     74  // Standard guarantees the null pointer value to be integer 0.
     75  UniqueOrNonOwningPtr() : mBits{0} {}
     76  UniqueOrNonOwningPtr(const UniqueOrNonOwningPtr&) = delete;
     77  UniqueOrNonOwningPtr(UniqueOrNonOwningPtr&& aOther) : mBits{aOther.mBits} {
     78    // "Release" the other one.
     79    aOther.mBits = 0;
     80  }
     81  ~UniqueOrNonOwningPtr() {
     82    if (IsUniquelyOwning()) {
     83      delete get();
     84    }
     85  }
     86  UniqueOrNonOwningPtr& operator=(const UniqueOrNonOwningPtr& aOther) = delete;
     87  UniqueOrNonOwningPtr& operator=(UniqueOrNonOwningPtr&& aOther) {
     88    mBits = aOther.mBits;
     89    // "Release" the other one.
     90    aOther.mBits = 0;
     91    return *this;
     92  }
     93 
     94  static UniqueOrNonOwningPtr UniquelyOwning(T* aPtr) {
     95    MOZ_ASSERT(aPtr, "Passing in null pointer as owning?");
     96    const uintptr_t bits = reinterpret_cast<uintptr_t>(aPtr);
     97    MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, "Odd-aligned owning pointer?");
     98    return UniqueOrNonOwningPtr{bits | kUniquelyOwningBit};
     99  }
    100 
    101  static UniqueOrNonOwningPtr NonOwning(T* aPtr) {
    102    const uintptr_t bits = reinterpret_cast<uintptr_t>(aPtr);
    103    MOZ_ASSERT((bits & kUniquelyOwningBit) == 0,
    104               "Odd-aligned non-owning pointer?");
    105    return UniqueOrNonOwningPtr{bits};
    106  }
    107 
    108  std::add_lvalue_reference_t<T> operator*() const {
    109    MOZ_ASSERT(
    110        get(),
    111        "dereferencing a UniqueOrNonOwningPtr containing nullptr with *");
    112    return *get();
    113  }
    114 
    115  T* operator->() const {
    116    MOZ_ASSERT(
    117        get(),
    118        "dereferencing a UniqueOrNonOwningPtr containing nullptr with ->");
    119    return get();
    120  }
    121 
    122  explicit operator bool() const { return get() != nullptr; }
    123 
    124  T* get() const { return reinterpret_cast<T*>(mBits & ~kUniquelyOwningBit); }
    125 
    126 private:
    127  bool IsUniquelyOwning() const { return (mBits & kUniquelyOwningBit) != 0; }
    128 
    129  // Bit for tracking uniquely-owning vs non-owning status. Check usage notes
    130  // in the main comment block.
    131  // NOTE: A null pointer constant has a guarantee on being integer literal 0.
    132  static constexpr uintptr_t kUniquelyOwningBit = 1;
    133  explicit UniqueOrNonOwningPtr(uintptr_t aValue) : mBits{aValue} {}
    134  uintptr_t mBits;
    135 };
    136 
    137 // Unsupported
    138 template <typename T>
    139 class UniqueOrNonOwningPtr<T[]>;
    140 
    141 }  // namespace mozilla
    142 
    143 #endif