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