Bug 1577282 - Part 2: Move functions outside of the Spectrum prototype. r=Maliha
[gecko.git] / mfbt / RangedPtr.h
bloba32696f20ac27be0075e36ef85cbdf5e60e30d91
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 T* get() const { return mPtr; }
124 explicit operator bool() const { return mPtr != nullptr; }
126 void checkIdenticalRange(const RangedPtr<T>& aOther) const {
127 MOZ_ASSERT(mRangeStart == aOther.mRangeStart);
128 MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd);
131 template <typename U>
132 RangedPtr<U> ReinterpretCast() const {
133 #ifdef DEBUG
134 return {reinterpret_cast<U*>(mPtr), reinterpret_cast<U*>(mRangeStart),
135 reinterpret_cast<U*>(mRangeEnd)};
136 #else
137 return {reinterpret_cast<U*>(mPtr), nullptr, nullptr};
138 #endif
142 * You can only assign one RangedPtr into another if the two pointers have
143 * the same valid range:
145 * char arr1[] = "hi";
146 * char arr2[] = "bye";
147 * RangedPtr<char> p1(arr1, 2);
148 * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
149 * p1 = RangedPtr<char>(arr2, 3); // asserts
151 RangedPtr<T>& operator=(const RangedPtr<T>& aOther) {
152 checkIdenticalRange(aOther);
153 mPtr = aOther.mPtr;
154 checkSanity();
155 return *this;
158 RangedPtr<T> operator+(size_t aInc) const {
159 MOZ_ASSERT(aInc <= size_t(-1) / sizeof(T));
160 MOZ_ASSERT(asUintptr() + aInc * sizeof(T) >= asUintptr());
161 return create(mPtr + aInc);
164 RangedPtr<T> operator-(size_t aDec) const {
165 MOZ_ASSERT(aDec <= size_t(-1) / sizeof(T));
166 MOZ_ASSERT(asUintptr() - aDec * sizeof(T) <= asUintptr());
167 return create(mPtr - aDec);
171 * You can assign a raw pointer into a RangedPtr if the raw pointer is
172 * within the range specified at creation.
174 template <typename U>
175 RangedPtr<T>& operator=(U* aPtr) {
176 *this = create(aPtr);
177 return *this;
180 template <typename U>
181 RangedPtr<T>& operator=(const RangedPtr<U>& aPtr) {
182 MOZ_ASSERT(mRangeStart <= aPtr.mPtr);
183 MOZ_ASSERT(aPtr.mPtr <= mRangeEnd);
184 mPtr = aPtr.mPtr;
185 checkSanity();
186 return *this;
189 RangedPtr<T>& operator++() { return (*this += 1); }
191 RangedPtr<T> operator++(int) {
192 RangedPtr<T> rcp = *this;
193 ++*this;
194 return rcp;
197 RangedPtr<T>& operator--() { return (*this -= 1); }
199 RangedPtr<T> operator--(int) {
200 RangedPtr<T> rcp = *this;
201 --*this;
202 return rcp;
205 RangedPtr<T>& operator+=(size_t aInc) {
206 *this = *this + aInc;
207 return *this;
210 RangedPtr<T>& operator-=(size_t aDec) {
211 *this = *this - aDec;
212 return *this;
215 T& operator[](ptrdiff_t aIndex) const {
216 MOZ_ASSERT(size_t(aIndex > 0 ? aIndex : -aIndex) <= size_t(-1) / sizeof(T));
217 return *create(mPtr + aIndex);
220 T& operator*() const {
221 MOZ_ASSERT(mPtr >= mRangeStart);
222 MOZ_ASSERT(mPtr < mRangeEnd);
223 return *mPtr;
226 T* operator->() const {
227 MOZ_ASSERT(mPtr >= mRangeStart);
228 MOZ_ASSERT(mPtr < mRangeEnd);
229 return mPtr;
232 template <typename U>
233 bool operator==(const RangedPtr<U>& aOther) const {
234 return mPtr == aOther.mPtr;
236 template <typename U>
237 bool operator!=(const RangedPtr<U>& aOther) const {
238 return !(*this == aOther);
241 template <typename U>
242 bool operator==(const U* u) const {
243 return mPtr == u;
245 template <typename U>
246 bool operator!=(const U* u) const {
247 return !(*this == u);
250 bool operator==(std::nullptr_t) const { return mPtr == nullptr; }
251 bool operator!=(std::nullptr_t) const { return mPtr != nullptr; }
253 template <typename U>
254 bool operator<(const RangedPtr<U>& aOther) const {
255 return mPtr < aOther.mPtr;
257 template <typename U>
258 bool operator<=(const RangedPtr<U>& aOther) const {
259 return mPtr <= aOther.mPtr;
262 template <typename U>
263 bool operator>(const RangedPtr<U>& aOther) const {
264 return mPtr > aOther.mPtr;
266 template <typename U>
267 bool operator>=(const RangedPtr<U>& aOther) const {
268 return mPtr >= aOther.mPtr;
271 size_t operator-(const RangedPtr<T>& aOther) const {
272 MOZ_ASSERT(mPtr >= aOther.mPtr);
273 return PointerRangeSize(aOther.mPtr, mPtr);
276 private:
277 RangedPtr() = delete;
280 } /* namespace mozilla */
282 #endif /* mozilla_RangedPtr_h */