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