RangedPtr.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. * Implements a smart pointer asserted to remain within a range specified at
  8. * construction.
  9. */
  10. #ifndef mozilla_RangedPtr_h
  11. #define mozilla_RangedPtr_h
  12. #include "mozilla/ArrayUtils.h"
  13. #include "mozilla/Assertions.h"
  14. #include "mozilla/Attributes.h"
  15. #include <stdint.h>
  16. namespace mozilla {
  17. /*
  18. * RangedPtr is a smart pointer restricted to an address range specified at
  19. * creation. The pointer (and any smart pointers derived from it) must remain
  20. * within the range [start, end] (inclusive of end to facilitate use as
  21. * sentinels). Dereferencing or indexing into the pointer (or pointers derived
  22. * from it) must remain within the range [start, end). All the standard pointer
  23. * operators are defined on it; in debug builds these operations assert that the
  24. * range specified at construction is respected.
  25. *
  26. * In theory passing a smart pointer instance as an argument can be slightly
  27. * slower than passing a T* (due to ABI requirements for passing structs versus
  28. * passing pointers), if the method being called isn't inlined. If you are in
  29. * extremely performance-critical code, you may want to be careful using this
  30. * smart pointer as an argument type.
  31. *
  32. * RangedPtr<T> intentionally does not implicitly convert to T*. Use get() to
  33. * explicitly convert to T*. Keep in mind that the raw pointer of course won't
  34. * implement bounds checking in debug builds.
  35. */
  36. template<typename T>
  37. class RangedPtr
  38. {
  39. T* mPtr;
  40. #ifdef DEBUG
  41. T* const mRangeStart;
  42. T* const mRangeEnd;
  43. #endif
  44. void checkSanity()
  45. {
  46. MOZ_ASSERT(mRangeStart <= mPtr);
  47. MOZ_ASSERT(mPtr <= mRangeEnd);
  48. }
  49. /* Creates a new pointer for |aPtr|, restricted to this pointer's range. */
  50. RangedPtr<T> create(T* aPtr) const
  51. {
  52. #ifdef DEBUG
  53. return RangedPtr<T>(aPtr, mRangeStart, mRangeEnd);
  54. #else
  55. return RangedPtr<T>(aPtr, nullptr, size_t(0));
  56. #endif
  57. }
  58. uintptr_t asUintptr() const { return reinterpret_cast<uintptr_t>(mPtr); }
  59. public:
  60. RangedPtr(T* aPtr, T* aStart, T* aEnd)
  61. : mPtr(aPtr)
  62. #ifdef DEBUG
  63. , mRangeStart(aStart), mRangeEnd(aEnd)
  64. #endif
  65. {
  66. MOZ_ASSERT(mRangeStart <= mRangeEnd);
  67. checkSanity();
  68. }
  69. RangedPtr(T* aPtr, T* aStart, size_t aLength)
  70. : mPtr(aPtr)
  71. #ifdef DEBUG
  72. , mRangeStart(aStart), mRangeEnd(aStart + aLength)
  73. #endif
  74. {
  75. MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
  76. MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
  77. reinterpret_cast<uintptr_t>(mRangeStart));
  78. checkSanity();
  79. }
  80. /* Equivalent to RangedPtr(aPtr, aPtr, aLength). */
  81. RangedPtr(T* aPtr, size_t aLength)
  82. : mPtr(aPtr)
  83. #ifdef DEBUG
  84. , mRangeStart(aPtr), mRangeEnd(aPtr + 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. /* Equivalent to RangedPtr(aArr, aArr, N). */
  93. template<size_t N>
  94. explicit RangedPtr(T (&aArr)[N])
  95. : mPtr(aArr)
  96. #ifdef DEBUG
  97. , mRangeStart(aArr), mRangeEnd(aArr + N)
  98. #endif
  99. {
  100. checkSanity();
  101. }
  102. T* get() const { return mPtr; }
  103. explicit operator bool() const { return mPtr != nullptr; }
  104. void checkIdenticalRange(const RangedPtr<T>& aOther) const
  105. {
  106. MOZ_ASSERT(mRangeStart == aOther.mRangeStart);
  107. MOZ_ASSERT(mRangeEnd == aOther.mRangeEnd);
  108. }
  109. /*
  110. * You can only assign one RangedPtr into another if the two pointers have
  111. * the same valid range:
  112. *
  113. * char arr1[] = "hi";
  114. * char arr2[] = "bye";
  115. * RangedPtr<char> p1(arr1, 2);
  116. * p1 = RangedPtr<char>(arr1 + 1, arr1, arr1 + 2); // works
  117. * p1 = RangedPtr<char>(arr2, 3); // asserts
  118. */
  119. RangedPtr<T>& operator=(const RangedPtr<T>& aOther)
  120. {
  121. checkIdenticalRange(aOther);
  122. mPtr = aOther.mPtr;
  123. checkSanity();
  124. return *this;
  125. }
  126. RangedPtr<T> operator+(size_t aInc) const
  127. {
  128. MOZ_ASSERT(aInc <= size_t(-1) / sizeof(T));
  129. MOZ_ASSERT(asUintptr() + aInc * sizeof(T) >= asUintptr());
  130. return create(mPtr + aInc);
  131. }
  132. RangedPtr<T> operator-(size_t aDec) const
  133. {
  134. MOZ_ASSERT(aDec <= size_t(-1) / sizeof(T));
  135. MOZ_ASSERT(asUintptr() - aDec * sizeof(T) <= asUintptr());
  136. return create(mPtr - aDec);
  137. }
  138. /*
  139. * You can assign a raw pointer into a RangedPtr if the raw pointer is
  140. * within the range specified at creation.
  141. */
  142. template <typename U>
  143. RangedPtr<T>& operator=(U* aPtr)
  144. {
  145. *this = create(aPtr);
  146. return *this;
  147. }
  148. template <typename U>
  149. RangedPtr<T>& operator=(const RangedPtr<U>& aPtr)
  150. {
  151. MOZ_ASSERT(mRangeStart <= aPtr.mPtr);
  152. MOZ_ASSERT(aPtr.mPtr <= mRangeEnd);
  153. mPtr = aPtr.mPtr;
  154. checkSanity();
  155. return *this;
  156. }
  157. RangedPtr<T>& operator++()
  158. {
  159. return (*this += 1);
  160. }
  161. RangedPtr<T> operator++(int)
  162. {
  163. RangedPtr<T> rcp = *this;
  164. ++*this;
  165. return rcp;
  166. }
  167. RangedPtr<T>& operator--()
  168. {
  169. return (*this -= 1);
  170. }
  171. RangedPtr<T> operator--(int)
  172. {
  173. RangedPtr<T> rcp = *this;
  174. --*this;
  175. return rcp;
  176. }
  177. RangedPtr<T>& operator+=(size_t aInc)
  178. {
  179. *this = *this + aInc;
  180. return *this;
  181. }
  182. RangedPtr<T>& operator-=(size_t aDec)
  183. {
  184. *this = *this - aDec;
  185. return *this;
  186. }
  187. T& operator[](int aIndex) const
  188. {
  189. MOZ_ASSERT(size_t(aIndex > 0 ? aIndex : -aIndex) <= size_t(-1) / sizeof(T));
  190. return *create(mPtr + aIndex);
  191. }
  192. T& operator*() const
  193. {
  194. MOZ_ASSERT(mPtr >= mRangeStart);
  195. MOZ_ASSERT(mPtr < mRangeEnd);
  196. return *mPtr;
  197. }
  198. T* operator->() const
  199. {
  200. MOZ_ASSERT(mPtr >= mRangeStart);
  201. MOZ_ASSERT(mPtr < mRangeEnd);
  202. return mPtr;
  203. }
  204. template <typename U>
  205. bool operator==(const RangedPtr<U>& aOther) const
  206. {
  207. return mPtr == aOther.mPtr;
  208. }
  209. template <typename U>
  210. bool operator!=(const RangedPtr<U>& aOther) const
  211. {
  212. return !(*this == aOther);
  213. }
  214. template<typename U>
  215. bool operator==(const U* u) const
  216. {
  217. return mPtr == u;
  218. }
  219. template<typename U>
  220. bool operator!=(const U* u) const
  221. {
  222. return !(*this == u);
  223. }
  224. template <typename U>
  225. bool operator<(const RangedPtr<U>& aOther) const
  226. {
  227. return mPtr < aOther.mPtr;
  228. }
  229. template <typename U>
  230. bool operator<=(const RangedPtr<U>& aOther) const
  231. {
  232. return mPtr <= aOther.mPtr;
  233. }
  234. template <typename U>
  235. bool operator>(const RangedPtr<U>& aOther) const
  236. {
  237. return mPtr > aOther.mPtr;
  238. }
  239. template <typename U>
  240. bool operator>=(const RangedPtr<U>& aOther) const
  241. {
  242. return mPtr >= aOther.mPtr;
  243. }
  244. size_t operator-(const RangedPtr<T>& aOther) const
  245. {
  246. MOZ_ASSERT(mPtr >= aOther.mPtr);
  247. return PointerRangeSize(aOther.mPtr, mPtr);
  248. }
  249. private:
  250. RangedPtr() = delete;
  251. T* operator&() = delete;
  252. };
  253. } /* namespace mozilla */
  254. #endif /* mozilla_RangedPtr_h */