1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at:
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * The Mozilla Foundation
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Jeff Walden <jwalden+code@mit.edu> (original author)
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * Implements a smart pointer asserted to remain within a range specified at
43 : * construction.
44 : */
45 :
46 : #ifndef mozilla_RangedPtr_h_
47 : #define mozilla_RangedPtr_h_
48 :
49 : #include "mozilla/Assertions.h"
50 : #include "mozilla/Attributes.h"
51 : #include "mozilla/Util.h"
52 :
53 : namespace mozilla {
54 :
55 : /*
56 : * RangedPtr is a smart pointer restricted to an address range specified at
57 : * creation. The pointer (and any smart pointers derived from it) must remain
58 : * within the range [start, end] (inclusive of end to facilitate use as
59 : * sentinels). Dereferencing or indexing into the pointer (or pointers derived
60 : * from it) must remain within the range [start, end). All the standard pointer
61 : * operators are defined on it; in debug builds these operations assert that the
62 : * range specified at construction is respected.
63 : *
64 : * In theory passing a smart pointer instance as an argument can be slightly
65 : * slower than passing a T* (due to ABI requirements for passing structs versus
66 : * passing pointers), if the method being called isn't inlined. If you are in
67 : * extremely performance-critical code, you may want to be careful using this
68 : * smart pointer as an argument type.
69 : *
70 : * RangedPtr<T> intentionally does not implicitly convert to T*. Use get() to
71 : * explicitly convert to T*. Keep in mind that the raw pointer of course won't
72 : * implement bounds checking in debug builds.
73 : */
74 : template <typename T>
75 : class RangedPtr
76 : {
77 : T* ptr;
78 :
79 : #ifdef DEBUG
80 : T* const rangeStart;
81 : T* const rangeEnd;
82 : #endif
83 :
84 699637705 : void checkSanity() {
85 699637705 : MOZ_ASSERT(rangeStart <= ptr);
86 699637705 : MOZ_ASSERT(ptr <= rangeEnd);
87 699637705 : }
88 :
89 : /* Creates a new pointer for |ptr|, restricted to this pointer's range. */
90 346691296 : RangedPtr<T> create(T *ptr) const {
91 : #ifdef DEBUG
92 346691296 : return RangedPtr<T>(ptr, rangeStart, rangeEnd);
93 : #else
94 : return RangedPtr<T>(ptr, NULL, size_t(0));
95 : #endif
96 : }
97 :
98 : public:
99 346691529 : RangedPtr(T* p, T* start, T* end)
100 : : ptr(p)
101 : #ifdef DEBUG
102 346691529 : , rangeStart(start), rangeEnd(end)
103 : #endif
104 : {
105 346691529 : MOZ_ASSERT(rangeStart <= rangeEnd);
106 346691529 : checkSanity();
107 346691529 : }
108 46653353 : RangedPtr(T* p, T* start, size_t length)
109 : : ptr(p)
110 : #ifdef DEBUG
111 46653353 : , rangeStart(start), rangeEnd(start + length)
112 : #endif
113 : {
114 45964685 : MOZ_ASSERT(length <= size_t(-1) / sizeof(T));
115 46653353 : MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart));
116 46653353 : checkSanity();
117 46653353 : }
118 :
119 : /* Equivalent to RangedPtr(p, p, length). */
120 3889863 : RangedPtr(T* p, size_t length)
121 : : ptr(p)
122 : #ifdef DEBUG
123 3889863 : , rangeStart(p), rangeEnd(p + length)
124 : #endif
125 : {
126 3536511 : MOZ_ASSERT(length <= size_t(-1) / sizeof(T));
127 3889863 : MOZ_ASSERT(uintptr_t(rangeStart) + length * sizeof(T) >= uintptr_t(rangeStart));
128 3889863 : checkSanity();
129 3889863 : }
130 :
131 : /* Equivalent to RangedPtr(arr, arr, N). */
132 : template<size_t N>
133 : RangedPtr(T arr[N])
134 : : ptr(arr)
135 : #ifdef DEBUG
136 : , rangeStart(arr), rangeEnd(arr + N)
137 : #endif
138 : {
139 : checkSanity();
140 : }
141 :
142 43748632 : T* get() const {
143 43748632 : return ptr;
144 : }
145 :
146 : /*
147 : * You can only assign one RangedPtr into another if the two pointers have
148 : * the same valid range:
149 : *
150 : * char arr1[] = "hi";
151 : * char arr2[] = "bye";
152 : * RangedPtr<char> p1(arr1, 2);
153 : * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
154 : * p1 = RangedPtr<char>(arr2, 3); // asserts
155 : */
156 740726 : RangedPtr<T>& operator=(const RangedPtr<T>& other) {
157 740726 : MOZ_ASSERT(rangeStart == other.rangeStart);
158 740726 : MOZ_ASSERT(rangeEnd == other.rangeEnd);
159 740726 : ptr = other.ptr;
160 740726 : checkSanity();
161 740726 : return *this;
162 : }
163 :
164 25071908 : RangedPtr<T> operator+(size_t inc) {
165 23788571 : MOZ_ASSERT(inc <= size_t(-1) / sizeof(T));
166 25071908 : MOZ_ASSERT(ptr + inc > ptr);
167 25071908 : return create(ptr + inc);
168 : }
169 :
170 319655379 : RangedPtr<T> operator-(size_t dec) {
171 317397412 : MOZ_ASSERT(dec <= size_t(-1) / sizeof(T));
172 319655379 : MOZ_ASSERT(ptr - dec < ptr);
173 319655379 : return create(ptr - dec);
174 : }
175 :
176 : /*
177 : * You can assign a raw pointer into a RangedPtr if the raw pointer is
178 : * within the range specified at creation.
179 : */
180 : template <typename U>
181 : RangedPtr<T>& operator=(U* p) {
182 : *this = create(p);
183 : return *this;
184 : }
185 :
186 : template <typename U>
187 301662234 : RangedPtr<T>& operator=(const RangedPtr<U>& p) {
188 301662234 : MOZ_ASSERT(rangeStart <= p.ptr);
189 301662234 : MOZ_ASSERT(p.ptr <= rangeEnd);
190 301662234 : ptr = p.ptr;
191 301662234 : checkSanity();
192 301662234 : return *this;
193 : }
194 :
195 25064455 : RangedPtr<T>& operator++() {
196 25064455 : return (*this += 1);
197 : }
198 :
199 24699281 : RangedPtr<T> operator++(int) {
200 24699281 : RangedPtr<T> rcp = *this;
201 24699281 : ++*this;
202 : return rcp;
203 : }
204 :
205 276590326 : RangedPtr<T>& operator--() {
206 276590326 : return (*this -= 1);
207 : }
208 :
209 : RangedPtr<T> operator--(int) {
210 : RangedPtr<T> rcp = *this;
211 : --*this;
212 : return rcp;
213 : }
214 :
215 25071908 : RangedPtr<T>& operator+=(size_t inc) {
216 25071908 : this->operator=<T>(*this + inc);
217 25071908 : return *this;
218 : }
219 :
220 276590326 : RangedPtr<T>& operator-=(size_t dec) {
221 276590326 : this->operator=<T>(*this - dec);
222 276590326 : return *this;
223 : }
224 :
225 1964009 : T& operator[](int index) const {
226 1964009 : MOZ_ASSERT(size_t(index > 0 ? index : -index) <= size_t(-1) / sizeof(T));
227 1964009 : return *create(ptr + index);
228 : }
229 :
230 423010626 : T& operator*() const {
231 423010626 : return *ptr;
232 : }
233 :
234 : template <typename U>
235 3951353 : bool operator==(const RangedPtr<U>& other) const {
236 3951353 : return ptr == other.ptr;
237 : }
238 : template <typename U>
239 3523005 : bool operator!=(const RangedPtr<U>& other) const {
240 3523005 : return !(*this == other);
241 : }
242 :
243 : template<typename U>
244 63617 : bool operator==(const U* u) const {
245 63617 : return ptr == u;
246 : }
247 : template<typename U>
248 : bool operator!=(const U* u) const {
249 : return !(*this == u);
250 : }
251 :
252 : template <typename U>
253 12850242 : bool operator<(const RangedPtr<U>& other) const {
254 12850242 : return ptr < other.ptr;
255 : }
256 : template <typename U>
257 13506 : bool operator<=(const RangedPtr<U>& other) const {
258 13506 : return ptr <= other.ptr;
259 : }
260 :
261 : template <typename U>
262 : bool operator>(const RangedPtr<U>& other) const {
263 : return ptr > other.ptr;
264 : }
265 : template <typename U>
266 1166904 : bool operator>=(const RangedPtr<U>& other) const {
267 1166904 : return ptr >= other.ptr;
268 : }
269 :
270 42783791 : size_t operator-(const RangedPtr<T>& other) const {
271 42783791 : MOZ_ASSERT(ptr >= other.ptr);
272 42783791 : return PointerRangeSize(other.ptr, ptr);
273 : }
274 :
275 : private:
276 : RangedPtr() MOZ_DELETE;
277 : T* operator&() MOZ_DELETE;
278 : operator T*() const MOZ_DELETE;
279 : };
280 :
281 : } /* namespace mozilla */
282 :
283 : #endif /* mozilla_RangedPtr_h_ */
|