A collection of basic/generally desirable code I use across multiple C++ projects.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
8.1 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. #ifndef ULE_ARRAY_H
  2. #define ULE_ARRAY_H
  3. #include <new> // operator new, operator delete
  4. #include "config.h"
  5. #include "alloc.h" // allocators...
  6. #include "serialize.h" // serialization
  7. #include "string.h" // String::memcpy
  8. #include "types.h" // type definitions
  9. // this is a dynamic array (grows as needed)
  10. // should work with any data type for T including primitive types
  11. // some initial |capacity| is heap-allocated and a pointer is stored to it as |data|
  12. // the |length| of the array, or number of filled slots is also tracked.
  13. //
  14. // it implements a single constructor, and operator new. no destructor, or operator overloading besides that.
  15. // remember to use ->data or .data to actually access the underlying array.
  16. //
  17. // overhead:
  18. // size of the struct will be 128 bits on 64-bit platforms
  19. // note that if you heap allocate this structure and store it on another struct, you will have to chase two pointers to get at the data.
  20. // to avoid this, I often include this struct in other structs so there's only one pointer dereference,
  21. // just like including a raw pointer array + length in your struct.
  22. // because I like to do this, automatic destructors are not useful.
  23. //
  24. template <typename T>
  25. struct Array {
  26. u32 length;
  27. u32 capacity;
  28. T* data;
  29. Array<T>(u32 _capacity = 8) {
  30. ULE_TYPES_H_FTAG;
  31. this->length = 0;
  32. this->capacity = _capacity;
  33. this->data = (T*) pCalloc(sizeof (T), _capacity);
  34. }
  35. void* operator new(size_t size) {
  36. ULE_TYPES_H_FTAG;
  37. return pMalloc((u32) size);
  38. }
  39. void checkIfShouldGrow() {
  40. ULE_TYPES_H_FTAG;
  41. if (this->isFull()) {
  42. // optimal number as you approach infinite elements approaches PHI, but 1.5 sometimes works better for finite sizes
  43. //
  44. // it seems, that a commonly chosen growth rate of '2' is perhaps the worst possible choice.
  45. // if you grow at a rate of 2x, you end up (likely) never being able to re-use the freed 'hole' in the heap
  46. // for a future allocation of the same kind.
  47. // useful reading for those interested in their own dynamic array implementations:
  48. // (facebook's vector impl, a strictly better std::vector)
  49. // https://github.com/facebook/folly/blob/main/folly/docs/FBVector.md
  50. //
  51. this->capacity = (u32) (this->capacity * 1.5);
  52. this->data = (T*) pRealloc(data, sizeof(T) * this->capacity);
  53. }
  54. }
  55. // for when the order in the array doesn't matter, move the end of the array into the removed slot
  56. void removeSwapWithEnd(u32 index) {
  57. ULE_TYPES_H_FTAG;
  58. if (this->isEmpty()) return; // overhead, maybe assert instead?
  59. u32 end = this->length - 1;
  60. if (index != end) {
  61. this->data[index] = this->data[end];
  62. }
  63. this->pop();
  64. }
  65. void removeSwapWithEnd(T* addr) {
  66. ULE_TYPES_H_FTAG;
  67. for (u32 i = 0; i < this->length; i++) {
  68. if ((this->data + i) == addr) {
  69. removeSwapWithEnd(i);
  70. return;
  71. }
  72. }
  73. }
  74. void removeAndShrink(u32 index) {
  75. ULE_TYPES_H_FTAG;
  76. for (u32 i = index + 1; i < this->length; i++) {
  77. String::memcpy(&this->data[i - 1], &this->data[i], sizeof(T));
  78. }
  79. this->length--;
  80. }
  81. void removeAndShrink(T* elementAddr) {
  82. ULE_TYPES_H_FTAG;
  83. s32 index = -1;
  84. for (u32 i = 0; i < this->length; i++) {
  85. if ((this->data + i) == elementAddr) {
  86. index = i;
  87. break;
  88. }
  89. }
  90. if (index == -1) {
  91. return;
  92. }
  93. for (u32 i = index + 1; i < this->length; i++) {
  94. String::memcpy((void*)(this->data + i - 1), (void*)(this->data + i), sizeof(T));
  95. }
  96. this->length--;
  97. }
  98. T pop() {
  99. ULE_TYPES_H_FTAG;
  100. if (this->isEmpty()) {
  101. die("empty");
  102. }
  103. return this->data[--this->length];
  104. }
  105. // sometimes, you want to copy some POD data on the stack to the next position in the internal array
  106. // that's what this does
  107. u32 pushCopy(T* e) {
  108. ULE_TYPES_H_FTAG;
  109. this->checkIfShouldGrow();
  110. String::memcpy((void*) &this->data[this->length++], e, sizeof(T));
  111. return this->length - 1;
  112. }
  113. // returns the next address into which you can store a T. makes sure there's enough room first.
  114. // it is irresponsible to call this and then not store a T in that address. this increments length,
  115. // reserving the next spot for you.
  116. T* pushNextAddrPromise() {
  117. ULE_TYPES_H_FTAG;
  118. this->checkIfShouldGrow();
  119. return &this->data[this->length++];
  120. }
  121. u32 push(T e) {
  122. ULE_TYPES_H_FTAG;
  123. this->checkIfShouldGrow();
  124. this->data[this->length++] = e;
  125. return this->length - 1;
  126. }
  127. u32 pushMany(T* elements, u32 count) {
  128. ULE_TYPES_H_FTAG;
  129. // ensure we have capacity. if we have to realloc multiple times that can suck,
  130. // but should be avoidable in practice by having an appropriately large initial capacity
  131. while (this->capacity < (this->length + count)) {
  132. this->capacity *= 1.5;
  133. this->data = (T*) pRealloc(data, sizeof (T) * this->capacity);
  134. }
  135. u32 start = this->length;
  136. for (u32 i1 = start, i2 = 0; i1 < count; i1++, i2++) {
  137. this->data[this->length++] = elements[i2];
  138. }
  139. return start;
  140. }
  141. void reverse() {
  142. ULE_TYPES_H_FTAG;
  143. u32 count = this->length / 2;
  144. for (u32 i = 0; i < count; i++) {
  145. u32 offset = this->length - 1 - i;
  146. T temp = this->data[i];
  147. this->data[i] = this->data[offset];
  148. this->data[offset] = temp;
  149. }
  150. }
  151. T shift() {
  152. ULE_TYPES_H_FTAG;
  153. if (this->length == 0) {
  154. return null;
  155. }
  156. T out = this->data[0];
  157. this->length -= 1;
  158. for (u32 i = 0; i < this->length; i++) {
  159. *(this->data + i) = *(this->data + i + 1);
  160. }
  161. return out;
  162. }
  163. T unshift(T e) {
  164. ULE_TYPES_H_FTAG;
  165. this->checkIfShouldGrow();
  166. for (u32 i = 0; i < this->length; i++) {
  167. *(this->data + i + 1) = *(this->data + i);
  168. }
  169. this->data[0] = e;
  170. this->length += 1;
  171. return this->length;
  172. }
  173. T peek() const {
  174. ULE_TYPES_H_FTAG;
  175. if (this->isEmpty()) {
  176. return null;
  177. }
  178. return this->data[this->length - 1];
  179. }
  180. bool isEmpty() const {
  181. ULE_TYPES_H_FTAG;
  182. return this->length == 0;
  183. }
  184. bool isFull() const {
  185. ULE_TYPES_H_FTAG;
  186. return this->length == this->capacity;
  187. }
  188. void clear() {
  189. ULE_TYPES_H_FTAG;
  190. this->length = 0;
  191. }
  192. };
  193. #ifdef ULE_CONFIG_OPTION_SERIALIZATION
  194. template <typename T>
  195. static void serialize(String* str, Array<T> array) {
  196. ULE_TYPES_H_FTAG;
  197. serialize(str, array.length);
  198. serialize(str, array.capacity);
  199. for (u32 i = 0; i < array.length; i++) {
  200. serialize(str, array.data[i]);
  201. }
  202. }
  203. template <typename T>
  204. static void serialize(String* str, Array<T>* array) {
  205. ULE_TYPES_H_FTAG;
  206. SERIALIZE_HANDLE_NULL(str, array);
  207. serialize(str, array->length);
  208. serialize(str, array->capacity);
  209. for (u32 i = 0; i < array->length; i++) {
  210. serialize(str, array->data[i]);
  211. }
  212. }
  213. template <typename T>
  214. static void deserialize(char** buffer, Array<T>* array) {
  215. ULE_TYPES_H_FTAG;
  216. deserialize(buffer, &array->length);
  217. deserialize(buffer, &array->capacity);
  218. for (u32 i = 0; i < array->length; i++) {
  219. deserialize(buffer, array->data + i);
  220. }
  221. }
  222. template <typename T>
  223. static void deserialize(char** buffer, Array<T>** array) {
  224. ULE_TYPES_H_FTAG;
  225. DESERIALIZE_HANDLE_NULL(buffer, array);
  226. u32 length, capacity;
  227. deserialize(buffer, &length);
  228. deserialize(buffer, &capacity);
  229. Array<T>* _array = new Array<T>(capacity);
  230. _array->length = length;
  231. for (u32 i = 0; i < _array->length; i++) {
  232. deserialize(buffer, _array->data + i);
  233. }
  234. *array = _array;
  235. }
  236. #endif // ULE_CONFIG_OPTION_SERIALIZATION
  237. #endif