Line data Source code
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/. */
6 :
7 : /*
8 : * Implements a smart pointer asserted to remain within a range specified at
9 : * construction.
10 : */
11 :
12 : #ifndef mozilla_RangedPtr_h
13 : #define mozilla_RangedPtr_h
14 :
15 : #include "mozilla/ArrayUtils.h"
16 : #include "mozilla/Assertions.h"
17 : #include "mozilla/Attributes.h"
18 :
19 : #include <stdint.h>
20 :
21 : namespace mozilla {
22 :
23 : /*
24 : * RangedPtr is a smart pointer restricted to an address range specified at
25 : * creation. The pointer (and any smart pointers derived from it) must remain
26 : * within the range [start, end] (inclusive of end to facilitate use as
27 : * sentinels). Dereferencing or indexing into the pointer (or pointers derived
28 : * from it) must remain within the range [start, end). All the standard pointer
29 : * operators are defined on it; in debug builds these operations assert that the
30 : * range specified at construction is respected.
31 : *
32 : * In theory passing a smart pointer instance as an argument can be slightly
33 : * slower than passing a T* (due to ABI requirements for passing structs versus
34 : * passing pointers), if the method being called isn't inlined. If you are in
35 : * extremely performance-critical code, you may want to be careful using this
36 : * smart pointer as an argument type.
37 : *
38 : * RangedPtr<T> intentionally does not implicitly convert to T*. Use get() to
39 : * explicitly convert to T*. Keep in mind that the raw pointer of course won't
40 : * implement bounds checking in debug builds.
41 : */
42 : template<typename T>
43 : class RangedPtr
44 : {
45 : T* mPtr;
46 :
47 : #ifdef DEBUG
48 : T* const mRangeStart;
49 : T* const mRangeEnd;
50 : #endif
51 :
52 1676850 : void checkSanity()
53 : {
54 1676850 : MOZ_ASSERT(mRangeStart <= mPtr);
55 1676850 : MOZ_ASSERT(mPtr <= mRangeEnd);
56 1676850 : }
57 :
58 : /* Creates a new pointer for |aPtr|, restricted to this pointer's range. */
59 1385169 : RangedPtr<T> create(T* aPtr) const
60 : {
61 : #ifdef DEBUG
62 1385169 : return RangedPtr<T>(aPtr, mRangeStart, mRangeEnd);
63 : #else
64 : return RangedPtr<T>(aPtr, nullptr, size_t(0));
65 : #endif
66 : }
67 :
68 258446 : uintptr_t asUintptr() const { return reinterpret_cast<uintptr_t>(mPtr); }
69 :
70 : public:
71 1523746 : RangedPtr(T* aPtr, T* aStart, T* aEnd)
72 : : mPtr(aPtr)
73 : #ifdef DEBUG
74 1523746 : , mRangeStart(aStart), mRangeEnd(aEnd)
75 : #endif
76 : {
77 1523746 : MOZ_ASSERT(mRangeStart <= mRangeEnd);
78 1523746 : checkSanity();
79 1523747 : }
80 2625 : RangedPtr(T* aPtr, T* aStart, size_t aLength)
81 : : mPtr(aPtr)
82 : #ifdef DEBUG
83 2625 : , mRangeStart(aStart), mRangeEnd(aStart + aLength)
84 : #endif
85 : {
86 700 : MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
87 2625 : MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
88 : reinterpret_cast<uintptr_t>(mRangeStart));
89 2625 : checkSanity();
90 2625 : }
91 :
92 : /* Equivalent to RangedPtr(aPtr, aPtr, aLength). */
93 12762 : RangedPtr(T* aPtr, size_t aLength)
94 : : mPtr(aPtr)
95 : #ifdef DEBUG
96 12762 : , mRangeStart(aPtr), mRangeEnd(aPtr + aLength)
97 : #endif
98 : {
99 1019 : MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
100 12762 : MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
101 : reinterpret_cast<uintptr_t>(mRangeStart));
102 12762 : checkSanity();
103 12762 : }
104 :
105 : /* Equivalent to RangedPtr(aArr, aArr, N). */
106 : template<size_t N>
107 : explicit RangedPtr(T (&aArr)[N])
108 : : mPtr(aArr)
109 : #ifdef DEBUG
110 : , mRangeStart(aArr), mRangeEnd(aArr + N)
111 : #endif
112 : {
113 : checkSanity();
114 : }
115 :
116 60243 : T* get() const { return mPtr; }
117 :
118 0 : explicit operator bool() const { return mPtr != nullptr; }
119 :
120 137403 : void checkIdenticalRange(const RangedPtr<T>& aOther) const
121 : {
122 137403 : MOZ_ASSERT(mRangeStart == aOther.mRangeStart);
123 137403 : MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd);
124 137403 : }
125 :
126 : /*
127 : * You can only assign one RangedPtr into another if the two pointers have
128 : * the same valid range:
129 : *
130 : * char arr1[] = "hi";
131 : * char arr2[] = "bye";
132 : * RangedPtr<char> p1(arr1, 2);
133 : * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
134 : * p1 = RangedPtr<char>(arr2, 3); // asserts
135 : */
136 137191 : RangedPtr<T>& operator=(const RangedPtr<T>& aOther)
137 : {
138 137191 : checkIdenticalRange(aOther);
139 137191 : mPtr = aOther.mPtr;
140 137191 : checkSanity();
141 137191 : return *this;
142 : }
143 :
144 127132 : RangedPtr<T> operator+(size_t aInc) const
145 : {
146 24370 : MOZ_ASSERT(aInc <= size_t(-1) / sizeof(T));
147 127132 : MOZ_ASSERT(asUintptr() + aInc * sizeof(T) >= asUintptr());
148 127132 : return create(mPtr + aInc);
149 : }
150 :
151 2091 : RangedPtr<T> operator-(size_t aDec) const
152 : {
153 2 : MOZ_ASSERT(aDec <= size_t(-1) / sizeof(T));
154 2091 : MOZ_ASSERT(asUintptr() - aDec * sizeof(T) <= asUintptr());
155 2091 : return create(mPtr - aDec);
156 : }
157 :
158 : /*
159 : * You can assign a raw pointer into a RangedPtr if the raw pointer is
160 : * within the range specified at creation.
161 : */
162 : template <typename U>
163 : RangedPtr<T>& operator=(U* aPtr)
164 : {
165 : *this = create(aPtr);
166 : return *this;
167 : }
168 :
169 : template <typename U>
170 : RangedPtr<T>& operator=(const RangedPtr<U>& aPtr)
171 : {
172 : MOZ_ASSERT(mRangeStart <= aPtr.mPtr);
173 : MOZ_ASSERT(aPtr.mPtr <= mRangeEnd);
174 : mPtr = aPtr.mPtr;
175 : checkSanity();
176 : return *this;
177 : }
178 :
179 126414 : RangedPtr<T>& operator++()
180 : {
181 126414 : return (*this += 1);
182 : }
183 :
184 98976 : RangedPtr<T> operator++(int)
185 : {
186 98976 : RangedPtr<T> rcp = *this;
187 98976 : ++*this;
188 98976 : return rcp;
189 : }
190 :
191 1626 : RangedPtr<T>& operator--()
192 : {
193 1626 : return (*this -= 1);
194 : }
195 :
196 0 : RangedPtr<T> operator--(int)
197 : {
198 0 : RangedPtr<T> rcp = *this;
199 0 : --*this;
200 0 : return rcp;
201 : }
202 :
203 126502 : RangedPtr<T>& operator+=(size_t aInc)
204 : {
205 126502 : *this = *this + aInc;
206 126502 : return *this;
207 : }
208 :
209 1626 : RangedPtr<T>& operator-=(size_t aDec)
210 : {
211 1626 : *this = *this - aDec;
212 1626 : return *this;
213 : }
214 :
215 1253176 : T& operator[](int aIndex) const
216 : {
217 400479 : MOZ_ASSERT(size_t(aIndex > 0 ? aIndex : -aIndex) <= size_t(-1) / sizeof(T));
218 1253176 : return *create(mPtr + aIndex);
219 : }
220 :
221 1461374 : T& operator*() const
222 : {
223 1461374 : MOZ_ASSERT(mPtr >= mRangeStart);
224 1461374 : MOZ_ASSERT(mPtr < mRangeEnd);
225 1461374 : return *mPtr;
226 : }
227 :
228 6 : T* operator->() const
229 : {
230 6 : MOZ_ASSERT(mPtr >= mRangeStart);
231 6 : MOZ_ASSERT(mPtr < mRangeEnd);
232 6 : return mPtr;
233 : }
234 :
235 : template <typename U>
236 34062 : bool operator==(const RangedPtr<U>& aOther) const
237 : {
238 34062 : return mPtr == aOther.mPtr;
239 : }
240 : template <typename U>
241 23892 : bool operator!=(const RangedPtr<U>& aOther) const
242 : {
243 23892 : return !(*this == aOther);
244 : }
245 :
246 : template<typename U>
247 4 : bool operator==(const U* u) const
248 : {
249 4 : return mPtr == u;
250 : }
251 : template<typename U>
252 4 : bool operator!=(const U* u) const
253 : {
254 4 : return !(*this == u);
255 : }
256 :
257 : template <typename U>
258 28241 : bool operator<(const RangedPtr<U>& aOther) const
259 : {
260 28241 : return mPtr < aOther.mPtr;
261 : }
262 : template <typename U>
263 222 : bool operator<=(const RangedPtr<U>& aOther) const
264 : {
265 222 : return mPtr <= aOther.mPtr;
266 : }
267 :
268 : template <typename U>
269 208 : bool operator>(const RangedPtr<U>& aOther) const
270 : {
271 208 : return mPtr > aOther.mPtr;
272 : }
273 : template <typename U>
274 1119 : bool operator>=(const RangedPtr<U>& aOther) const
275 : {
276 1119 : return mPtr >= aOther.mPtr;
277 : }
278 :
279 907928 : size_t operator-(const RangedPtr<T>& aOther) const
280 : {
281 907928 : MOZ_ASSERT(mPtr >= aOther.mPtr);
282 907928 : return PointerRangeSize(aOther.mPtr, mPtr);
283 : }
284 :
285 : private:
286 : RangedPtr() = delete;
287 : };
288 :
289 : } /* namespace mozilla */
290 :
291 : #endif /* mozilla_RangedPtr_h */
|