SafePointer.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2017 to 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_SAFE_POINTER
  24. #define DFPSR_SAFE_POINTER
  25. #include <cstring>
  26. #include <cassert>
  27. #include <stdint.h>
  28. // Disabled in release mode
  29. #ifndef NDEBUG
  30. #define SAFE_POINTER_CHECKS
  31. #endif
  32. namespace dsr {
  33. // Generic implementaions
  34. void assertInsideSafePointer(const char* method, const char* name, const uint8_t* pointer, const uint8_t* data, const uint8_t* regionStart, const uint8_t* regionEnd, int claimedSize, int elementSize);
  35. void assertNonNegativeSize(int size);
  36. template<typename T>
  37. class SafePointer {
  38. private:
  39. // A pointer from regionStart to regionEnd
  40. // Mutable because only the data being pointed to is write protected in a const SafePointer
  41. mutable T *data;
  42. #ifdef SAFE_POINTER_CHECKS
  43. mutable T *regionStart;
  44. mutable T *regionEnd;
  45. mutable const char * name;
  46. #endif
  47. public:
  48. #ifdef SAFE_POINTER_CHECKS
  49. SafePointer() : data(nullptr), regionStart(nullptr), regionEnd(nullptr), name("Unnamed null pointer") {}
  50. explicit SafePointer(const char* name) : data(nullptr), regionStart(nullptr), regionEnd(nullptr), name(name) {}
  51. SafePointer(const char* name, T* regionStart, int regionByteSize = sizeof(T)) : data(regionStart), regionStart(regionStart), regionEnd((T*)(((uint8_t*)regionStart) + (intptr_t)regionByteSize)), name(name) {
  52. assertNonNegativeSize(regionByteSize);
  53. }
  54. SafePointer(const char* name, T* regionStart, int regionByteSize, T* data) : data(data), regionStart(regionStart), regionEnd((T*)(((uint8_t*)regionStart) + (intptr_t)regionByteSize)), name(name) {
  55. assertNonNegativeSize(regionByteSize);
  56. }
  57. #else
  58. SafePointer() : data(nullptr) {}
  59. explicit SafePointer(const char* name) : data(nullptr) {}
  60. SafePointer(const char* name, T* regionStart, int regionByteSize = sizeof(T)) : data(regionStart) {}
  61. SafePointer(const char* name, T* regionStart, int regionByteSize, T* data) : data(data) {}
  62. #endif
  63. public:
  64. #ifdef SAFE_POINTER_CHECKS
  65. inline void assertInside(const char* method, const T* pointer, int size = (int)sizeof(T)) const {
  66. assertInsideSafePointer(method, this->name, (const uint8_t*)pointer, (const uint8_t*)this->data, (const uint8_t*)this->regionStart, (const uint8_t*)this->regionEnd, size, sizeof(T));
  67. }
  68. inline void assertInside(const char* method) const {
  69. this->assertInside(method, this->data);
  70. }
  71. #endif
  72. public:
  73. // Back to unsafe pointer with a clearly visible method name as a warning
  74. // The same can be done by mistake using the & operator on a reference
  75. // p.getUnsafe() = &(*p) = &(p[0])
  76. inline T* getUnsafe() {
  77. #ifdef SAFE_POINTER_CHECKS
  78. this->assertInside("getUnsafe");
  79. #endif
  80. return this->data;
  81. }
  82. inline const T* getUnsafe() const {
  83. #ifdef SAFE_POINTER_CHECKS
  84. this->assertInside("getUnsafe");
  85. #endif
  86. return this->data;
  87. }
  88. // Get unsafe pointer without bound checks for implementing your own safety
  89. inline T* getUnchecked() {
  90. return this->data;
  91. }
  92. inline const T* getUnchecked() const {
  93. return this->data;
  94. }
  95. // Returns the pointer in modulo byteAlignment
  96. // Returns 0 if the pointer is aligned with byteAlignment
  97. inline int getAlignmentOffset(int byteAlignment) const {
  98. return ((uintptr_t)this->data) % byteAlignment;
  99. }
  100. inline bool isNull() const {
  101. return this->data == nullptr;
  102. }
  103. inline bool isNotNull() const {
  104. return this->data != nullptr;
  105. }
  106. // Get a new safe pointer from a sub-set of data
  107. // byteOffset is which byte in the source will be index zero in the new pointer
  108. // size is the new pointer's size, which may not exceed the remaining available space
  109. inline SafePointer<T> slice(const char* name, int byteOffset, int size) {
  110. T *newStart = (T*)(((uint8_t*)(this->data)) + (intptr_t)byteOffset);
  111. #ifdef SAFE_POINTER_CHECKS
  112. assertInside("getSlice", newStart, size);
  113. return SafePointer<T>(name, newStart, size);
  114. #else
  115. return SafePointer<T>(name, newStart);
  116. #endif
  117. }
  118. inline const SafePointer<T> slice(const char* name, int byteOffset, int size) const {
  119. T *newStart = (T*)(((uint8_t*)(this->data)) + (intptr_t)byteOffset);
  120. #ifdef SAFE_POINTER_CHECKS
  121. assertInside("getSlice", newStart, size);
  122. return SafePointer<T>(name, newStart, size);
  123. #else
  124. return SafePointer<T>(name, newStart);
  125. #endif
  126. }
  127. // Dereference
  128. template <typename S = T>
  129. inline S& get() {
  130. #ifdef SAFE_POINTER_CHECKS
  131. assertInside("get", this->data, sizeof(S));
  132. #endif
  133. return *((S*)this->data);
  134. }
  135. template <typename S = T>
  136. inline const S& get() const {
  137. #ifdef SAFE_POINTER_CHECKS
  138. assertInside("get", this->data, sizeof(S));
  139. #endif
  140. return *((const S*)this->data);
  141. }
  142. inline T& operator*() {
  143. #ifdef SAFE_POINTER_CHECKS
  144. assertInside("operator*");
  145. #endif
  146. return *(this->data);
  147. }
  148. inline const T& operator*() const {
  149. #ifdef SAFE_POINTER_CHECKS
  150. assertInside("operator*");
  151. #endif
  152. return *(this->data);
  153. }
  154. inline T& operator[] (int index) {
  155. T* address = this->data + index;
  156. #ifdef SAFE_POINTER_CHECKS
  157. assertInside("operator[]", address);
  158. #endif
  159. return *address;
  160. }
  161. inline const T& operator[] (int index) const {
  162. T* address = this->data + index;
  163. #ifdef SAFE_POINTER_CHECKS
  164. assertInside("operator[]", address);
  165. #endif
  166. return *address;
  167. }
  168. inline void increaseBytes(intptr_t byteOffset) const {
  169. this->data = (T*)(((uint8_t*)(this->data)) + byteOffset);
  170. }
  171. inline void increaseElements(intptr_t elementOffset) const {
  172. this->data += elementOffset;
  173. }
  174. inline SafePointer<T>& operator+=(intptr_t elementOffset) {
  175. this->data += elementOffset;
  176. return *this;
  177. }
  178. inline const SafePointer<T>& operator+=(intptr_t elementOffset) const {
  179. this->data += elementOffset;
  180. return *this;
  181. }
  182. inline SafePointer<T>& operator-=(intptr_t elementOffset) {
  183. this->data -= elementOffset;
  184. return *this;
  185. }
  186. inline const SafePointer<T>& operator-=(intptr_t elementOffset) const {
  187. this->data -= elementOffset;
  188. return *this;
  189. }
  190. inline SafePointer<T> operator+(intptr_t elementOffset) {
  191. SafePointer<T> result = *this;
  192. result += elementOffset;
  193. return result;
  194. }
  195. inline const SafePointer<T> operator+(intptr_t elementOffset) const {
  196. SafePointer<T> result = *this;
  197. result += elementOffset;
  198. return result;
  199. }
  200. inline SafePointer<T> operator-(intptr_t elementOffset) {
  201. SafePointer<T> result = *this;
  202. result -= elementOffset;
  203. return result;
  204. }
  205. inline const SafePointer<T> operator-(intptr_t elementOffset) const {
  206. SafePointer<T> result = *this;
  207. result -= elementOffset;
  208. return result;
  209. }
  210. inline const SafePointer<T>& operator=(const SafePointer<T>& source) const {
  211. this->data = source.data;
  212. #ifdef SAFE_POINTER_CHECKS
  213. this->regionStart = source.regionStart;
  214. this->regionEnd = source.regionEnd;
  215. this->name = source.name;
  216. #endif
  217. return *this;
  218. }
  219. };
  220. template <typename T, typename S>
  221. inline void safeMemoryCopy(SafePointer<T> target, const SafePointer<S>& source, int64_t byteSize) {
  222. #ifdef SAFE_POINTER_CHECKS
  223. // Both target and source must be in valid memory
  224. target.assertInside("memoryCopy (target)", target.getUnchecked(), (size_t)byteSize);
  225. source.assertInside("memoryCopy (source)", source.getUnchecked(), (size_t)byteSize);
  226. // memcpy doesn't allow pointer aliasing
  227. // TODO: Make a general assertion with the same style as out of bound exceptions
  228. assert(((const uint8_t*)target.getUnchecked()) + byteSize <= (uint8_t*)source.getUnchecked() || ((const uint8_t*)source.getUnchecked()) + byteSize <= (uint8_t*)target.getUnchecked());
  229. #endif
  230. std::memcpy(target.getUnchecked(), source.getUnchecked(), (size_t)byteSize);
  231. }
  232. template <typename T>
  233. inline void safeMemorySet(SafePointer<T>& target, uint8_t value, int64_t byteSize) {
  234. #ifdef SAFE_POINTER_CHECKS
  235. // Target must be in valid memory
  236. target.assertInside("memoryCopy (target)", target.getUnchecked(), byteSize);
  237. #endif
  238. std::memset((char*)(target.getUnchecked()), value, (size_t)byteSize);
  239. }
  240. }
  241. #endif