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   T* mPtr;
45 
46 #ifdef DEBUG
47   T* const mRangeStart;
48   T* const mRangeEnd;
49 #endif
50 
checkSanity()51   void checkSanity() {
52     MOZ_ASSERT(mRangeStart <= mPtr);
53     MOZ_ASSERT(mPtr <= mRangeEnd);
54   }
55 
56   /* Creates a new pointer for |aPtr|, restricted to this pointer's range. */
create(T * aPtr)57   RangedPtr<T> create(T* aPtr) const {
58 #ifdef DEBUG
59     return RangedPtr<T>(aPtr, mRangeStart, mRangeEnd);
60 #else
61     return RangedPtr<T>(aPtr, nullptr, size_t(0));
62 #endif
63   }
64 
asUintptr()65   uintptr_t asUintptr() const { return reinterpret_cast<uintptr_t>(mPtr); }
66 
67  public:
RangedPtr(T * aPtr,T * aStart,T * aEnd)68   RangedPtr(T* aPtr, T* aStart, T* aEnd)
69       : mPtr(aPtr)
70 #ifdef DEBUG
71         ,
72         mRangeStart(aStart),
73         mRangeEnd(aEnd)
74 #endif
75   {
76     MOZ_ASSERT(mRangeStart <= mRangeEnd);
77     checkSanity();
78   }
RangedPtr(T * aPtr,T * aStart,size_t aLength)79   RangedPtr(T* aPtr, T* aStart, size_t aLength)
80       : mPtr(aPtr)
81 #ifdef DEBUG
82         ,
83         mRangeStart(aStart),
84         mRangeEnd(aStart + aLength)
85 #endif
86   {
87     MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
88     MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
89                reinterpret_cast<uintptr_t>(mRangeStart));
90     checkSanity();
91   }
92 
93   /* Equivalent to RangedPtr(aPtr, aPtr, aLength). */
RangedPtr(T * aPtr,size_t aLength)94   RangedPtr(T* aPtr, size_t aLength)
95       : mPtr(aPtr)
96 #ifdef DEBUG
97         ,
98         mRangeStart(aPtr),
99         mRangeEnd(aPtr + aLength)
100 #endif
101   {
102     MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
103     MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
104                reinterpret_cast<uintptr_t>(mRangeStart));
105     checkSanity();
106   }
107 
108   /* Equivalent to RangedPtr(aArr, aArr, N). */
109   template <size_t N>
RangedPtr(T (& aArr)[N])110   explicit RangedPtr(T (&aArr)[N])
111       : mPtr(aArr)
112 #ifdef DEBUG
113         ,
114         mRangeStart(aArr),
115         mRangeEnd(aArr + N)
116 #endif
117   {
118     checkSanity();
119   }
120 
get()121   T* get() const { return mPtr; }
122 
123   explicit operator bool() const { return mPtr != nullptr; }
124 
checkIdenticalRange(const RangedPtr<T> & aOther)125   void checkIdenticalRange(const RangedPtr<T>& aOther) const {
126     MOZ_ASSERT(mRangeStart == aOther.mRangeStart);
127     MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd);
128   }
129 
130   /*
131    * You can only assign one RangedPtr into another if the two pointers have
132    * the same valid range:
133    *
134    *   char arr1[] = "hi";
135    *   char arr2[] = "bye";
136    *   RangedPtr<char> p1(arr1, 2);
137    *   p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
138    *   p1 = RangedPtr<char>(arr2, 3);                  // asserts
139    */
140   RangedPtr<T>& operator=(const RangedPtr<T>& aOther) {
141     checkIdenticalRange(aOther);
142     mPtr = aOther.mPtr;
143     checkSanity();
144     return *this;
145   }
146 
147   RangedPtr<T> operator+(size_t aInc) const {
148     MOZ_ASSERT(aInc <= size_t(-1) / sizeof(T));
149     MOZ_ASSERT(asUintptr() + aInc * sizeof(T) >= asUintptr());
150     return create(mPtr + aInc);
151   }
152 
153   RangedPtr<T> operator-(size_t aDec) const {
154     MOZ_ASSERT(aDec <= size_t(-1) / sizeof(T));
155     MOZ_ASSERT(asUintptr() - aDec * sizeof(T) <= asUintptr());
156     return create(mPtr - aDec);
157   }
158 
159   /*
160    * You can assign a raw pointer into a RangedPtr if the raw pointer is
161    * within the range specified at creation.
162    */
163   template <typename U>
164   RangedPtr<T>& operator=(U* aPtr) {
165     *this = create(aPtr);
166     return *this;
167   }
168 
169   template <typename U>
170   RangedPtr<T>& operator=(const RangedPtr<U>& aPtr) {
171     MOZ_ASSERT(mRangeStart <= aPtr.mPtr);
172     MOZ_ASSERT(aPtr.mPtr <= mRangeEnd);
173     mPtr = aPtr.mPtr;
174     checkSanity();
175     return *this;
176   }
177 
178   RangedPtr<T>& operator++() { return (*this += 1); }
179 
180   RangedPtr<T> operator++(int) {
181     RangedPtr<T> rcp = *this;
182     ++*this;
183     return rcp;
184   }
185 
186   RangedPtr<T>& operator--() { return (*this -= 1); }
187 
188   RangedPtr<T> operator--(int) {
189     RangedPtr<T> rcp = *this;
190     --*this;
191     return rcp;
192   }
193 
194   RangedPtr<T>& operator+=(size_t aInc) {
195     *this = *this + aInc;
196     return *this;
197   }
198 
199   RangedPtr<T>& operator-=(size_t aDec) {
200     *this = *this - aDec;
201     return *this;
202   }
203 
204   T& operator[](ptrdiff_t aIndex) const {
205     MOZ_ASSERT(size_t(aIndex > 0 ? aIndex : -aIndex) <= size_t(-1) / sizeof(T));
206     return *create(mPtr + aIndex);
207   }
208 
209   T& operator*() const {
210     MOZ_ASSERT(mPtr >= mRangeStart);
211     MOZ_ASSERT(mPtr < mRangeEnd);
212     return *mPtr;
213   }
214 
215   T* operator->() const {
216     MOZ_ASSERT(mPtr >= mRangeStart);
217     MOZ_ASSERT(mPtr < mRangeEnd);
218     return mPtr;
219   }
220 
221   template <typename U>
222   bool operator==(const RangedPtr<U>& aOther) const {
223     return mPtr == aOther.mPtr;
224   }
225   template <typename U>
226   bool operator!=(const RangedPtr<U>& aOther) const {
227     return !(*this == aOther);
228   }
229 
230   template <typename U>
231   bool operator==(const U* u) const {
232     return mPtr == u;
233   }
234   template <typename U>
235   bool operator!=(const U* u) const {
236     return !(*this == u);
237   }
238 
239   template <typename U>
240   bool operator<(const RangedPtr<U>& aOther) const {
241     return mPtr < aOther.mPtr;
242   }
243   template <typename U>
244   bool operator<=(const RangedPtr<U>& aOther) const {
245     return mPtr <= aOther.mPtr;
246   }
247 
248   template <typename U>
249   bool operator>(const RangedPtr<U>& aOther) const {
250     return mPtr > aOther.mPtr;
251   }
252   template <typename U>
253   bool operator>=(const RangedPtr<U>& aOther) const {
254     return mPtr >= aOther.mPtr;
255   }
256 
257   size_t operator-(const RangedPtr<T>& aOther) const {
258     MOZ_ASSERT(mPtr >= aOther.mPtr);
259     return PointerRangeSize(aOther.mPtr, mPtr);
260   }
261 
262  private:
263   RangedPtr() = delete;
264 };
265 
266 } /* namespace mozilla */
267 
268 #endif /* mozilla_RangedPtr_h */
269