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.

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