SafePointer.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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 data to data + size
  107. inline SafePointer<T> slice(const char* name, int byteOffset, int size) {
  108. T *newStart = (T*)(((uint8_t*)(this->data)) + (intptr_t)byteOffset);
  109. #ifdef SAFE_POINTER_CHECKS
  110. assertInside("getSlice", newStart, size);
  111. return SafePointer<T>(name, newStart, size);
  112. #else
  113. return SafePointer<T>(name, newStart);
  114. #endif
  115. }
  116. inline const SafePointer<T> slice(const char* name, int byteOffset, int size) const {
  117. T *newStart = (T*)(((uint8_t*)(this->data)) + (intptr_t)byteOffset);
  118. #ifdef SAFE_POINTER_CHECKS
  119. assertInside("getSlice", newStart, size);
  120. return SafePointer<T>(name, newStart, size);
  121. #else
  122. return SafePointer<T>(name, newStart);
  123. #endif
  124. }
  125. // Dereference
  126. template <typename S = T>
  127. inline S& get() {
  128. #ifdef SAFE_POINTER_CHECKS
  129. assertInside("get", this->data, sizeof(S));
  130. #endif
  131. return *((S*)this->data);
  132. }
  133. template <typename S = T>
  134. inline const S& get() const {
  135. #ifdef SAFE_POINTER_CHECKS
  136. assertInside("get", this->data, sizeof(S));
  137. #endif
  138. return *((const S*)this->data);
  139. }
  140. inline T& operator*() {
  141. #ifdef SAFE_POINTER_CHECKS
  142. assertInside("operator*");
  143. #endif
  144. return *(this->data);
  145. }
  146. inline const T& operator*() const {
  147. #ifdef SAFE_POINTER_CHECKS
  148. assertInside("operator*");
  149. #endif
  150. return *(this->data);
  151. }
  152. inline T& operator[] (int index) {
  153. T* address = this->data + index;
  154. #ifdef SAFE_POINTER_CHECKS
  155. assertInside("operator[]", address);
  156. #endif
  157. return *address;
  158. }
  159. inline const T& operator[] (int index) const {
  160. T* address = this->data + index;
  161. #ifdef SAFE_POINTER_CHECKS
  162. assertInside("operator[]", address);
  163. #endif
  164. return *address;
  165. }
  166. inline void increaseBytes(intptr_t byteOffset) const {
  167. this->data = (T*)(((uint8_t*)(this->data)) + byteOffset);
  168. }
  169. inline void increaseElements(intptr_t elementOffset) const {
  170. this->data += elementOffset;
  171. }
  172. inline SafePointer<T>& operator+=(intptr_t elementOffset) {
  173. this->data += elementOffset;
  174. return *this;
  175. }
  176. inline const SafePointer<T>& operator+=(intptr_t elementOffset) const {
  177. this->data += elementOffset;
  178. return *this;
  179. }
  180. inline SafePointer<T>& operator-=(intptr_t elementOffset) {
  181. this->data -= elementOffset;
  182. return *this;
  183. }
  184. inline const SafePointer<T>& operator-=(intptr_t elementOffset) const {
  185. this->data -= elementOffset;
  186. return *this;
  187. }
  188. inline SafePointer<T> operator+(intptr_t elementOffset) {
  189. SafePointer<T> result = *this;
  190. result += elementOffset;
  191. return result;
  192. }
  193. inline const SafePointer<T> operator+(intptr_t elementOffset) const {
  194. SafePointer<T> result = *this;
  195. result += elementOffset;
  196. return result;
  197. }
  198. inline SafePointer<T> operator-(intptr_t elementOffset) {
  199. SafePointer<T> result = *this;
  200. result -= elementOffset;
  201. return result;
  202. }
  203. inline const SafePointer<T> operator-(intptr_t elementOffset) const {
  204. SafePointer<T> result = *this;
  205. result -= elementOffset;
  206. return result;
  207. }
  208. inline const SafePointer<T>& operator=(const SafePointer<T>& source) const {
  209. this->data = source.data;
  210. #ifdef SAFE_POINTER_CHECKS
  211. this->regionStart = source.regionStart;
  212. this->regionEnd = source.regionEnd;
  213. this->name = source.name;
  214. #endif
  215. return *this;
  216. }
  217. };
  218. template <typename T, typename S>
  219. inline void safeMemoryCopy(SafePointer<T> target, const SafePointer<S>& source, int byteSize) {
  220. #ifdef SAFE_POINTER_CHECKS
  221. // Both target and source must be in valid memory
  222. target.assertInside("memoryCopy (target)", target.getUnchecked(), (size_t)byteSize);
  223. source.assertInside("memoryCopy (source)", source.getUnchecked(), (size_t)byteSize);
  224. // memcpy doesn't allow pointer aliasing
  225. // TODO: Make a general assertion with the same style as out of bound exceptions
  226. assert(((const uint8_t*)target.getUnchecked()) + byteSize <= (uint8_t*)source.getUnchecked() || ((const uint8_t*)source.getUnchecked()) + byteSize <= (uint8_t*)target.getUnchecked());
  227. #endif
  228. std::memcpy(target.getUnchecked(), source.getUnchecked(), (size_t)byteSize);
  229. }
  230. template <typename T>
  231. inline void safeMemorySet(SafePointer<T>& target, uint8_t value, int byteSize) {
  232. #ifdef SAFE_POINTER_CHECKS
  233. // Target must be in valid memory
  234. target.assertInside("memoryCopy (target)", target.getUnchecked(), byteSize);
  235. #endif
  236. std::memset((char*)(target.getUnchecked()), value, (size_t)byteSize);
  237. }
  238. }
  239. #endif