SafePointer.h 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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. // Returns the pointer in modulo byteAlignment
  89. // Returns 0 if the pointer is aligned with byteAlignment
  90. inline int getAlignmentOffset(int byteAlignment) const {
  91. return ((uintptr_t)this->data) % byteAlignment;
  92. }
  93. inline bool isNull() const {
  94. return this->data == nullptr;
  95. }
  96. inline bool isNotNull() const {
  97. return this->data != nullptr;
  98. }
  99. // Get a new safe pointer from data to data + size
  100. inline SafePointer<T> slice(const char* name, int byteOffset, int size) {
  101. T *newStart = (T*)(((uint8_t*)(this->data)) + (intptr_t)byteOffset);
  102. #ifdef SAFE_POINTER_CHECKS
  103. assertInside("getSlice", newStart, size);
  104. return SafePointer<T>(name, newStart, size);
  105. #else
  106. return SafePointer<T>(name, newStart);
  107. #endif
  108. }
  109. inline const SafePointer<T> slice(const char* name, int byteOffset, int size) const {
  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. // Dereference
  119. template <typename S = T>
  120. inline S& get() {
  121. #ifdef SAFE_POINTER_CHECKS
  122. assertInside("get", this->data, sizeof(S));
  123. #endif
  124. return *((S*)this->data);
  125. }
  126. template <typename S = T>
  127. inline const S& get() const {
  128. #ifdef SAFE_POINTER_CHECKS
  129. assertInside("get", this->data, sizeof(S));
  130. #endif
  131. return *((const S*)this->data);
  132. }
  133. inline T& operator*() {
  134. #ifdef SAFE_POINTER_CHECKS
  135. assertInside("operator*");
  136. #endif
  137. return *(this->data);
  138. }
  139. inline const T& operator*() const {
  140. #ifdef SAFE_POINTER_CHECKS
  141. assertInside("operator*");
  142. #endif
  143. return *(this->data);
  144. }
  145. inline T& operator[] (int index) {
  146. T* address = this->data + index;
  147. #ifdef SAFE_POINTER_CHECKS
  148. assertInside("operator[]", address);
  149. #endif
  150. return *address;
  151. }
  152. inline const T& operator[] (int index) const {
  153. T* address = this->data + index;
  154. #ifdef SAFE_POINTER_CHECKS
  155. assertInside("operator[]", address);
  156. #endif
  157. return *address;
  158. }
  159. inline void increaseBytes(intptr_t byteOffset) const {
  160. this->data = (T*)(((uint8_t*)(this->data)) + byteOffset);
  161. }
  162. inline void increaseElements(intptr_t elementOffset) const {
  163. this->data += elementOffset;
  164. }
  165. inline SafePointer<T>& operator+=(intptr_t elementOffset) {
  166. this->data += elementOffset;
  167. return *this;
  168. }
  169. inline const SafePointer<T>& operator+=(intptr_t elementOffset) const {
  170. this->data += elementOffset;
  171. return *this;
  172. }
  173. inline SafePointer<T>& operator-=(intptr_t elementOffset) {
  174. this->data -= elementOffset;
  175. return *this;
  176. }
  177. inline const SafePointer<T>& operator-=(intptr_t elementOffset) const {
  178. this->data -= elementOffset;
  179. return *this;
  180. }
  181. inline SafePointer<T> operator+(intptr_t elementOffset) {
  182. SafePointer<T> result = *this;
  183. result += elementOffset;
  184. return result;
  185. }
  186. inline const SafePointer<T> operator+(intptr_t elementOffset) const {
  187. SafePointer<T> result = *this;
  188. result += elementOffset;
  189. return result;
  190. }
  191. inline SafePointer<T> operator-(intptr_t elementOffset) {
  192. SafePointer<T> result = *this;
  193. result -= elementOffset;
  194. return result;
  195. }
  196. inline const SafePointer<T> operator-(intptr_t elementOffset) const {
  197. SafePointer<T> result = *this;
  198. result -= elementOffset;
  199. return result;
  200. }
  201. inline const SafePointer<T>& operator=(const SafePointer<T>& source) const {
  202. this->data = source.data;
  203. #ifdef SAFE_POINTER_CHECKS
  204. this->regionStart = source.regionStart;
  205. this->regionEnd = source.regionEnd;
  206. this->name = source.name;
  207. #endif
  208. return *this;
  209. }
  210. };
  211. template <typename T, typename S>
  212. inline void safeMemoryCopy(SafePointer<T> target, const SafePointer<S>& source, int byteSize) {
  213. #ifdef SAFE_POINTER_CHECKS
  214. // Both target and source must be in valid memory
  215. target.assertInside("memoryCopy (target)", target.getUnsafe(), (size_t)byteSize);
  216. source.assertInside("memoryCopy (source)", source.getUnsafe(), (size_t)byteSize);
  217. // memcpy doesn't allow pointer aliasing
  218. // TODO: Make a general assertion with the same style as out of bound exceptions
  219. assert(((const uint8_t*)target.getUnsafe()) + byteSize < (uint8_t*)source.getUnsafe() || ((const uint8_t*)source.getUnsafe()) + byteSize < (uint8_t*)target.getUnsafe());
  220. #endif
  221. std::memcpy(target.getUnsafe(), source.getUnsafe(), (size_t)byteSize);
  222. }
  223. template <typename T>
  224. inline void safeMemorySet(SafePointer<T>& target, uint8_t value, int byteSize) {
  225. #ifdef SAFE_POINTER_CHECKS
  226. // Target must be in valid memory
  227. target.assertInside("memoryCopy (target)", target.getUnsafe(), byteSize);
  228. #endif
  229. std::memset((char*)(target.getUnsafe()), value, (size_t)byteSize);
  230. }
  231. }
  232. #endif