File: UniqueOrNonOwningPtr.h

package info (click to toggle)
firefox 141.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,550,616 kB
  • sloc: cpp: 7,426,508; javascript: 6,367,238; ansic: 3,707,354; python: 1,368,984; xml: 623,983; asm: 426,916; java: 184,324; sh: 64,488; makefile: 19,203; objc: 13,059; perl: 12,955; yacc: 4,583; cs: 3,846; pascal: 3,352; lex: 1,720; ruby: 1,071; exp: 762; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10
file content (143 lines) | stat: -rw-r--r-- 4,782 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* 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