Bug 1642579 [wpt PR 23909] - [COOP access reporting] Preliminary WPT tests., a=testonly
[gecko.git] / mfbt / RangedPtr.h
blob7f6e8c99de1fb2eb1cc1f60e692575127a2aec21
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Implements a smart pointer asserted to remain within a range specified at
9 * construction.
12 #ifndef mozilla_RangedPtr_h
13 #define mozilla_RangedPtr_h
15 #include "mozilla/ArrayUtils.h"
16 #include "mozilla/Assertions.h"
17 #include "mozilla/Attributes.h"
19 #include <stdint.h>
20 #include <cstddef>
22 namespace mozilla {
25 * RangedPtr is a smart pointer restricted to an address range specified at
26 * creation. The pointer (and any smart pointers derived from it) must remain
27 * within the range [start, end] (inclusive of end to facilitate use as
28 * sentinels). Dereferencing or indexing into the pointer (or pointers derived
29 * from it) must remain within the range [start, end). All the standard pointer
30 * operators are defined on it; in debug builds these operations assert that the
31 * range specified at construction is respected.
33 * In theory passing a smart pointer instance as an argument can be slightly
34 * slower than passing a T* (due to ABI requirements for passing structs versus
35 * passing pointers), if the method being called isn't inlined. If you are in
36 * extremely performance-critical code, you may want to be careful using this
37 * smart pointer as an argument type.
39 * RangedPtr<T> intentionally does not implicitly convert to T*. Use get() to
40 * explicitly convert to T*. Keep in mind that the raw pointer of course won't
41 * implement bounds checking in debug builds.
43 template <typename T>
44 class RangedPtr {
45 T* mPtr;
47 #ifdef DEBUG
48 T* const mRangeStart;
49 T* const mRangeEnd;
50 #endif
52 void checkSanity() {
53 MOZ_ASSERT(mRangeStart <= mPtr);
54 MOZ_ASSERT(mPtr <= mRangeEnd);
57 /* Creates a new pointer for |aPtr|, restricted to this pointer's range. */
58 RangedPtr<T> create(T* aPtr) const {
59 #ifdef DEBUG
60 return RangedPtr<T>(aPtr, mRangeStart, mRangeEnd);
61 #else
62 return RangedPtr<T>(aPtr, nullptr, size_t(0));
63 #endif
66 uintptr_t asUintptr() const { return reinterpret_cast<uintptr_t>(mPtr); }
68 public:
69 RangedPtr(T* aPtr, T* aStart, T* aEnd)
70 : mPtr(aPtr)
71 #ifdef DEBUG
73 mRangeStart(aStart),
74 mRangeEnd(aEnd)
75 #endif
77 MOZ_ASSERT(mRangeStart <= mRangeEnd);
78 checkSanity();
80 RangedPtr(T* aPtr, T* aStart, size_t aLength)
81 : mPtr(aPtr)
82 #ifdef DEBUG
84 mRangeStart(aStart),
85 mRangeEnd(aStart + aLength)
86 #endif
88 MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
89 MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
90 reinterpret_cast<uintptr_t>(mRangeStart));
91 checkSanity();
94 /* Equivalent to RangedPtr(aPtr, aPtr, aLength). */
95 RangedPtr(T* aPtr, size_t aLength)
96 : mPtr(aPtr)
97 #ifdef DEBUG
99 mRangeStart(aPtr),
100 mRangeEnd(aPtr + aLength)
101 #endif
103 MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
104 MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
105 reinterpret_cast<uintptr_t>(mRangeStart));
106 checkSanity();
109 /* Equivalent to RangedPtr(aArr, aArr, N). */
110 template <size_t N>
111 explicit RangedPtr(T (&aArr)[N])
112 : mPtr(aArr)
113 #ifdef DEBUG
115 mRangeStart(aArr),
116 mRangeEnd(aArr + N)
117 #endif
119 checkSanity();
122 MOZ_IMPLICIT RangedPtr(const RangedPtr<T>& aOther)
123 : mPtr(aOther.mPtr)
124 #ifdef DEBUG
126 mRangeStart(aOther.mRangeStart),
127 mRangeEnd(aOther.mRangeEnd)
128 #endif
130 checkSanity();
133 T* get() const { return mPtr; }
135 explicit operator bool() const { return mPtr != nullptr; }
137 void checkIdenticalRange(const RangedPtr<T>& aOther) const {
138 MOZ_ASSERT(mRangeStart == aOther.mRangeStart);
139 MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd);
142 template <typename U>
143 RangedPtr<U> ReinterpretCast() const {
144 #ifdef DEBUG
145 return {reinterpret_cast<U*>(mPtr), reinterpret_cast<U*>(mRangeStart),
146 reinterpret_cast<U*>(mRangeEnd)};
147 #else
148 return {reinterpret_cast<U*>(mPtr), nullptr, nullptr};
149 #endif
153 * You can only assign one RangedPtr into another if the two pointers have
154 * the same valid range:
156 * char arr1[] = "hi";
157 * char arr2[] = "bye";
158 * RangedPtr<char> p1(arr1, 2);
159 * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
160 * p1 = RangedPtr<char>(arr2, 3); // asserts
162 RangedPtr<T>& operator=(const RangedPtr<T>& aOther) {
163 checkIdenticalRange(aOther);
164 mPtr = aOther.mPtr;
165 checkSanity();
166 return *this;
169 RangedPtr<T> operator+(size_t aInc) const {
170 MOZ_ASSERT(aInc <= size_t(-1) / sizeof(T));
171 MOZ_ASSERT(asUintptr() + aInc * sizeof(T) >= asUintptr());
172 return create(mPtr + aInc);
175 RangedPtr<T> operator-(size_t aDec) const {
176 MOZ_ASSERT(aDec <= size_t(-1) / sizeof(T));
177 MOZ_ASSERT(asUintptr() - aDec * sizeof(T) <= asUintptr());
178 return create(mPtr - aDec);
182 * You can assign a raw pointer into a RangedPtr if the raw pointer is
183 * within the range specified at creation.
185 template <typename U>
186 RangedPtr<T>& operator=(U* aPtr) {
187 *this = create(aPtr);
188 return *this;
191 template <typename U>
192 RangedPtr<T>& operator=(const RangedPtr<U>& aPtr) {
193 MOZ_ASSERT(mRangeStart <= aPtr.mPtr);
194 MOZ_ASSERT(aPtr.mPtr <= mRangeEnd);
195 mPtr = aPtr.mPtr;
196 checkSanity();
197 return *this;
200 RangedPtr<T>& operator++() { return (*this += 1); }
202 RangedPtr<T> operator++(int) {
203 RangedPtr<T> rcp = *this;
204 ++*this;
205 return rcp;
208 RangedPtr<T>& operator--() { return (*this -= 1); }
210 RangedPtr<T> operator--(int) {
211 RangedPtr<T> rcp = *this;
212 --*this;
213 return rcp;
216 RangedPtr<T>& operator+=(size_t aInc) {
217 *this = *this + aInc;
218 return *this;
221 RangedPtr<T>& operator-=(size_t aDec) {
222 *this = *this - aDec;
223 return *this;
226 T& operator[](ptrdiff_t aIndex) const {
227 MOZ_ASSERT(size_t(aIndex > 0 ? aIndex : -aIndex) <= size_t(-1) / sizeof(T));
228 return *create(mPtr + aIndex);
231 T& operator*() const {
232 MOZ_ASSERT(mPtr >= mRangeStart);
233 MOZ_ASSERT(mPtr < mRangeEnd);
234 return *mPtr;
237 T* operator->() const {
238 MOZ_ASSERT(mPtr >= mRangeStart);
239 MOZ_ASSERT(mPtr < mRangeEnd);
240 return mPtr;
243 template <typename U>
244 bool operator==(const RangedPtr<U>& aOther) const {
245 return mPtr == aOther.mPtr;
247 template <typename U>
248 bool operator!=(const RangedPtr<U>& aOther) const {
249 return !(*this == aOther);
252 template <typename U>
253 bool operator==(const U* u) const {
254 return mPtr == u;
256 template <typename U>
257 bool operator!=(const U* u) const {
258 return !(*this == u);
261 bool operator==(std::nullptr_t) const { return mPtr == nullptr; }
262 bool operator!=(std::nullptr_t) const { return mPtr != nullptr; }
264 template <typename U>
265 bool operator<(const RangedPtr<U>& aOther) const {
266 return mPtr < aOther.mPtr;
268 template <typename U>
269 bool operator<=(const RangedPtr<U>& aOther) const {
270 return mPtr <= aOther.mPtr;
273 template <typename U>
274 bool operator>(const RangedPtr<U>& aOther) const {
275 return mPtr > aOther.mPtr;
277 template <typename U>
278 bool operator>=(const RangedPtr<U>& aOther) const {
279 return mPtr >= aOther.mPtr;
282 size_t operator-(const RangedPtr<T>& aOther) const {
283 MOZ_ASSERT(mPtr >= aOther.mPtr);
284 return PointerRangeSize(aOther.mPtr, mPtr);
287 private:
288 RangedPtr() = delete;
291 } /* namespace mozilla */
293 #endif /* mozilla_RangedPtr_h */