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/. */
8 * Implements a smart pointer asserted to remain within a range specified at
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"
18 #include "mozilla/NullPtr.h"
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.
53 typedef void (RangedPtr::* ConvertibleToBool
)();
57 MOZ_ASSERT(rangeStart
<= ptr
);
58 MOZ_ASSERT(ptr
<= rangeEnd
);
61 /* Creates a new pointer for |p|, restricted to this pointer's range. */
62 RangedPtr
<T
> create(T
*p
) const {
64 return RangedPtr
<T
>(p
, rangeStart
, rangeEnd
);
66 return RangedPtr
<T
>(p
, nullptr, size_t(0));
70 uintptr_t asUintptr() const { return uintptr_t(ptr
); }
73 RangedPtr(T
* p
, T
* start
, T
* end
)
76 , rangeStart(start
), rangeEnd(end
)
79 MOZ_ASSERT(rangeStart
<= rangeEnd
);
82 RangedPtr(T
* p
, T
* start
, size_t length
)
85 , rangeStart(start
), rangeEnd(start
+ length
)
88 MOZ_ASSERT(length
<= size_t(-1) / sizeof(T
));
89 MOZ_ASSERT(uintptr_t(rangeStart
) + length
* sizeof(T
) >= uintptr_t(rangeStart
));
93 /* Equivalent to RangedPtr(p, p, length). */
94 RangedPtr(T
* p
, size_t length
)
97 , rangeStart(p
), rangeEnd(p
+ length
)
100 MOZ_ASSERT(length
<= size_t(-1) / sizeof(T
));
101 MOZ_ASSERT(uintptr_t(rangeStart
) + length
* sizeof(T
) >= uintptr_t(rangeStart
));
105 /* Equivalent to RangedPtr(arr, arr, N). */
107 RangedPtr(T (&arr
)[N
])
110 , rangeStart(arr
), rangeEnd(arr
+ N
)
120 operator ConvertibleToBool() const { return ptr
? &RangedPtr::nonNull
: 0; }
123 * You can only assign one RangedPtr into another if the two pointers have
124 * the same valid range:
126 * char arr1[] = "hi";
127 * char arr2[] = "bye";
128 * RangedPtr<char> p1(arr1, 2);
129 * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
130 * p1 = RangedPtr<char>(arr2, 3); // asserts
132 RangedPtr
<T
>& operator=(const RangedPtr
<T
>& other
) {
133 MOZ_ASSERT(rangeStart
== other
.rangeStart
);
134 MOZ_ASSERT(rangeEnd
== other
.rangeEnd
);
140 RangedPtr
<T
> operator+(size_t inc
) {
141 MOZ_ASSERT(inc
<= size_t(-1) / sizeof(T
));
142 MOZ_ASSERT(asUintptr() + inc
* sizeof(T
) >= asUintptr());
143 return create(ptr
+ inc
);
146 RangedPtr
<T
> operator-(size_t dec
) {
147 MOZ_ASSERT(dec
<= size_t(-1) / sizeof(T
));
148 MOZ_ASSERT(asUintptr() - dec
* sizeof(T
) <= asUintptr());
149 return create(ptr
- dec
);
153 * You can assign a raw pointer into a RangedPtr if the raw pointer is
154 * within the range specified at creation.
156 template <typename U
>
157 RangedPtr
<T
>& operator=(U
* p
) {
162 template <typename U
>
163 RangedPtr
<T
>& operator=(const RangedPtr
<U
>& p
) {
164 MOZ_ASSERT(rangeStart
<= p
.ptr
);
165 MOZ_ASSERT(p
.ptr
<= rangeEnd
);
171 RangedPtr
<T
>& operator++() {
175 RangedPtr
<T
> operator++(int) {
176 RangedPtr
<T
> rcp
= *this;
181 RangedPtr
<T
>& operator--() {
185 RangedPtr
<T
> operator--(int) {
186 RangedPtr
<T
> rcp
= *this;
191 RangedPtr
<T
>& operator+=(size_t inc
) {
196 RangedPtr
<T
>& operator-=(size_t dec
) {
201 T
& operator[](int index
) const {
202 MOZ_ASSERT(size_t(index
> 0 ? index
: -index
) <= size_t(-1) / sizeof(T
));
203 return *create(ptr
+ index
);
206 T
& operator*() const {
207 MOZ_ASSERT(ptr
>= rangeStart
);
208 MOZ_ASSERT(ptr
< rangeEnd
);
212 template <typename U
>
213 bool operator==(const RangedPtr
<U
>& other
) const {
214 return ptr
== other
.ptr
;
216 template <typename U
>
217 bool operator!=(const RangedPtr
<U
>& other
) const {
218 return !(*this == other
);
222 bool operator==(const U
* u
) const {
226 bool operator!=(const U
* u
) const {
227 return !(*this == u
);
230 template <typename U
>
231 bool operator<(const RangedPtr
<U
>& other
) const {
232 return ptr
< other
.ptr
;
234 template <typename U
>
235 bool operator<=(const RangedPtr
<U
>& other
) const {
236 return ptr
<= other
.ptr
;
239 template <typename U
>
240 bool operator>(const RangedPtr
<U
>& other
) const {
241 return ptr
> other
.ptr
;
243 template <typename U
>
244 bool operator>=(const RangedPtr
<U
>& other
) const {
245 return ptr
>= other
.ptr
;
248 size_t operator-(const RangedPtr
<T
>& other
) const {
249 MOZ_ASSERT(ptr
>= other
.ptr
);
250 return PointerRangeSize(other
.ptr
, ptr
);
254 RangedPtr() MOZ_DELETE
;
255 T
* operator&() MOZ_DELETE
;
258 } /* namespace mozilla */
260 #endif /* mozilla_RangedPtr_h */