list.h 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /// @file
  2. /// @ingroup cgraph_utils
  3. #pragma once
  4. #include <assert.h>
  5. #include <errno.h>
  6. #include <stdbool.h>
  7. #include <stdint.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <util/alloc.h>
  12. #include <util/exit.h>
  13. #ifdef __GNUC__
  14. #define LIST_UNUSED __attribute__((unused))
  15. #else
  16. #define LIST_UNUSED /* nothing */
  17. #endif
  18. /** create a new list type and its associated member functions
  19. *
  20. * \param name Type name to give the list container
  21. * \param type Type of the elements the list will store
  22. */
  23. #define DEFINE_LIST(name, type) DEFINE_LIST_WITH_DTOR(name, type, name##_noop_)
  24. /** \p DEFINE_LIST but with a custom element destructor
  25. *
  26. * \param name Type name to give the list container
  27. * \param type Type of the elements the list will store
  28. * \param dtor Destructor to be called on elements being released
  29. */
  30. #define DEFINE_LIST_WITH_DTOR(name, type, dtor) \
  31. \
  32. /** list container \
  33. * \
  34. * All members of this type are considered private. They should only be \
  35. * accessed through the functions below. \
  36. */ \
  37. typedef struct { \
  38. type *base; /* start of the allocation for backing memory */ \
  39. /* (base == NULL && capacity == 0) || (base != NULL && capacity > 0) */ \
  40. size_t head; /* index of the first element */ \
  41. /* (capacity == 0 && head == 0) || (capacity > 0 && head < capacity) */ \
  42. size_t size; /* number of elements in the list */ \
  43. /* size <= capacity */ \
  44. size_t capacity; /* available storage slots */ \
  45. } name##_t; \
  46. \
  47. /* default “do nothing” destructor */ \
  48. static inline LIST_UNUSED void name##_noop_(type item) { (void)item; } \
  49. \
  50. /** get the number of elements in a list */ \
  51. static inline LIST_UNUSED size_t name##_size(const name##_t *list) { \
  52. assert(list != NULL); \
  53. return list->size; \
  54. } \
  55. \
  56. /** does this list contain no elements? */ \
  57. static inline LIST_UNUSED bool name##_is_empty(const name##_t *list) { \
  58. assert(list != NULL); \
  59. return name##_size(list) == 0; \
  60. } \
  61. \
  62. static inline int name##_try_append(name##_t *list, type item) { \
  63. assert(list != NULL); \
  64. \
  65. /* do we need to expand the backing storage? */ \
  66. if (list->size == list->capacity) { \
  67. const size_t c = list->capacity == 0 ? 1 : (list->capacity * 2); \
  68. \
  69. /* will the calculation of the new size overflow? */ \
  70. if (SIZE_MAX / c < sizeof(type)) { \
  71. return ERANGE; \
  72. } \
  73. \
  74. type *base = (type *)realloc(list->base, c * sizeof(type)); \
  75. if (base == NULL) { \
  76. return ENOMEM; \
  77. } \
  78. \
  79. /* zero the new memory */ \
  80. memset((char *)base + list->capacity * sizeof(type), 0, \
  81. (c - list->capacity) * sizeof(type)); \
  82. \
  83. /* Do we need to shuffle the prefix upwards? E.g. */ \
  84. /* */ \
  85. /* ┌───┬───┬───┬───┐ */ \
  86. /* old: │ 3 │ 4 │ 1 │ 2 │ */ \
  87. /* └───┴───┴─┼─┴─┼─┘ */ \
  88. /* │ └───────────────┐ */ \
  89. /* └───────────────┐ │ */ \
  90. /* ▼ ▼ */ \
  91. /* ┌───┬───┬───┬───┬───┬───┬───┬───┐ */ \
  92. /* new: │ 3 │ 4 │ │ │ │ │ 1 │ 2 │ */ \
  93. /* └───┴───┴───┴───┴───┴───┴───┴───┘ */ \
  94. if (list->head + list->size > list->capacity) { \
  95. const size_t prefix = list->capacity - list->head; \
  96. const size_t new_head = c - prefix; \
  97. memmove(&base[new_head], &base[list->head], prefix * sizeof(type)); \
  98. list->head = new_head; \
  99. } \
  100. \
  101. list->base = base; \
  102. list->capacity = c; \
  103. } \
  104. \
  105. list->base[(list->head + list->size) % list->capacity] = item; \
  106. ++list->size; \
  107. \
  108. return 0; \
  109. } \
  110. \
  111. static inline void name##_append(name##_t *list, type item) { \
  112. int rc = name##_try_append(list, item); \
  113. if (rc != 0) { \
  114. fprintf(stderr, "realloc failed: %s\n", strerror(rc)); \
  115. graphviz_exit(EXIT_FAILURE); \
  116. } \
  117. } \
  118. \
  119. /** retrieve an element from a list \
  120. * \
  121. * \param list List to operate on \
  122. * \param index Element index to get \
  123. * \return Element at the given index \
  124. */ \
  125. static inline type name##_get(const name##_t *list, size_t index) { \
  126. assert(list != NULL); \
  127. assert(index < list->size && "index out of bounds"); \
  128. return list->base[(list->head + index) % list->capacity]; \
  129. } \
  130. \
  131. /** access an element in a list for the purpose of modification \
  132. * \
  133. * Because this acquires an internal pointer into the list structure, `get` \
  134. * and `set` should be preferred over this function. `get` and `set` are \
  135. * easier to reason about. In particular, the pointer returned by this \
  136. * function is invalidated by any list operation that may reallocate the \
  137. * backing storage (e.g. `shrink_to_fit`). \
  138. * \
  139. * \param list List to operate on \
  140. * \param index Element to get a pointer to \
  141. * \return Pointer to the requested element \
  142. */ \
  143. static inline type *name##_at(name##_t *list, size_t index) { \
  144. assert(list != NULL); \
  145. assert(index < list->size && "index out of bounds"); \
  146. return &list->base[(list->head + index) % list->capacity]; \
  147. } \
  148. \
  149. /** get a handle to the first element */ \
  150. static inline LIST_UNUSED type *name##_front(name##_t *list) { \
  151. assert(list != NULL); \
  152. assert(!name##_is_empty(list)); \
  153. return name##_at(list, 0); \
  154. } \
  155. \
  156. /** get a handle to the last element */ \
  157. static inline LIST_UNUSED type *name##_back(name##_t *list) { \
  158. assert(list != NULL); \
  159. assert(!name##_is_empty(list)); \
  160. return name##_at(list, name##_size(list) - 1); \
  161. } \
  162. \
  163. /** assign to an element in a list \
  164. * \
  165. * \param list List to operate on \
  166. * \param index Element to assign to \
  167. * \param item Value to assign \
  168. */ \
  169. static inline void name##_set(name##_t *list, size_t index, type item) { \
  170. assert(list != NULL); \
  171. assert(index < name##_size(list) && "index out of bounds"); \
  172. type *target = name##_at(list, index); \
  173. dtor(*target); \
  174. *target = item; \
  175. } \
  176. \
  177. /** remove an element from a list \
  178. * \
  179. * \param list List to operate on \
  180. * \param item Value of element to remove \
  181. */ \
  182. static inline LIST_UNUSED void name##_remove(name##_t *list, \
  183. const type item) { \
  184. assert(list != NULL); \
  185. \
  186. for (size_t i = 0; i < list->size; ++i) { \
  187. /* is this the element we are looking for? */ \
  188. type *candidate = name##_at(list, i); \
  189. if (memcmp(candidate, &item, sizeof(type)) == 0) { \
  190. \
  191. /* destroy the element we are about to remove */ \
  192. dtor(*candidate); \
  193. \
  194. /* shrink the list */ \
  195. for (size_t j = i + 1; j < list->size; ++j) { \
  196. type *replacement = name##_at(list, j); \
  197. *candidate = *replacement; \
  198. candidate = replacement; \
  199. } \
  200. --list->size; \
  201. return; \
  202. } \
  203. } \
  204. } \
  205. \
  206. /** remove all elements from a list */ \
  207. static inline LIST_UNUSED void name##_clear(name##_t *list) { \
  208. assert(list != NULL); \
  209. \
  210. for (size_t i = 0; i < list->size; ++i) { \
  211. dtor(name##_get(list, i)); \
  212. } \
  213. \
  214. list->size = 0; \
  215. \
  216. /* opportunistically re-sync the list */ \
  217. list->head = 0; \
  218. } \
  219. \
  220. /** ensure the list can fit a given number of items without reallocation \
  221. * \
  222. * \param list List to operate on \
  223. * \param capacity Number of items the list should be able to contain \
  224. */ \
  225. static inline LIST_UNUSED void name##_reserve(name##_t *list, \
  226. size_t capacity) { \
  227. assert(list != NULL); \
  228. \
  229. /* if we can already fit enough items, nothing to do */ \
  230. if (list->capacity >= capacity) { \
  231. return; \
  232. } \
  233. \
  234. list->base = (type *)gv_recalloc(list->base, list->capacity, capacity, \
  235. sizeof(type)); \
  236. \
  237. /* Do we need to shuffle the prefix upwards? E.g. */ \
  238. /* */ \
  239. /* ┌───┬───┬───┬───┐ */ \
  240. /* old: │ 3 │ 4 │ 1 │ 2 │ */ \
  241. /* └───┴───┴─┼─┴─┼─┘ */ \
  242. /* │ └───────────────┐ */ \
  243. /* └───────────────┐ │ */ \
  244. /* ▼ ▼ */ \
  245. /* ┌───┬───┬───┬───┬───┬───┬───┬───┐ */ \
  246. /* new: │ 3 │ 4 │ │ │ │ │ 1 │ 2 │ */ \
  247. /* └───┴───┴───┴───┴───┴───┴───┴───┘ */ \
  248. if (list->head + list->size > list->capacity) { \
  249. const size_t prefix = list->capacity - list->head; \
  250. const size_t new_head = capacity - prefix; \
  251. memmove(&list->base[new_head], &list->base[list->head], \
  252. prefix * sizeof(type)); \
  253. list->head = new_head; \
  254. } \
  255. \
  256. list->capacity = capacity; \
  257. } \
  258. \
  259. /** shrink or grow the list to the given size \
  260. * \
  261. * \param list List to operate on \
  262. * \param size New size of the list \
  263. * \param value Default to assign to any new elements \
  264. */ \
  265. static inline LIST_UNUSED void name##_resize(name##_t *list, size_t size, \
  266. type value) { \
  267. assert(list != NULL); \
  268. \
  269. if (list->size < size) { \
  270. /* we are expanding the list */ \
  271. while (list->size < size) { \
  272. name##_append(list, value); \
  273. } \
  274. } else if (list->size > size) { \
  275. /* we are shrinking the list */ \
  276. while (list->size > size) { \
  277. dtor(name##_get(list, list->size - 1)); \
  278. --list->size; \
  279. } \
  280. } \
  281. } \
  282. \
  283. /** is the given element in the list? */ \
  284. static inline LIST_UNUSED bool name##_contains( \
  285. const name##_t *haystack, const type needle, \
  286. bool (*eq)(const type a, const type b)) { \
  287. assert(haystack != NULL); \
  288. assert(eq != NULL); \
  289. \
  290. for (size_t i = 0; i < name##_size(haystack); ++i) { \
  291. if (eq(name##_get(haystack, i), needle)) { \
  292. return true; \
  293. } \
  294. } \
  295. return false; \
  296. } \
  297. \
  298. /** replicate a list */ \
  299. static inline LIST_UNUSED name##_t name##_copy(const name##_t *source) { \
  300. assert(source != NULL); \
  301. \
  302. name##_t destination = {(type *)gv_calloc(source->capacity, sizeof(type)), \
  303. 0, 0, source->capacity}; \
  304. for (size_t i = 0; i < source->size; ++i) { \
  305. name##_append(&destination, name##_get(source, i)); \
  306. } \
  307. return destination; \
  308. } \
  309. \
  310. /** does the list not wrap past its end? */ \
  311. /** */ \
  312. /** This checks whether the list is discontiguous in how its elements */ \
  313. /** appear in memory: */ \
  314. /** */ \
  315. /** ┌───┬───┬───┬───┬───┬───┬───┬───┐ */ \
  316. /** a contiguous list: │ │ │ w │ x │ y │ z │ │ │ */ \
  317. /** └───┴───┴───┴───┴───┴───┴───┴───┘ */ \
  318. /** 0 1 2 3 */ \
  319. /** */ \
  320. /** ┌───┬───┬───┬───┬───┬───┬───┬───┐ */ \
  321. /** a discontiguous list: │ y │ z │ │ │ │ │ x │ y │ */ \
  322. /** └───┴───┴───┴───┴───┴───┴───┴───┘ */ \
  323. /** 2 3 0 1 */ \
  324. static inline LIST_UNUSED bool name##_is_contiguous(const name##_t *list) { \
  325. assert(list != NULL); \
  326. return list->head + list->size <= list->capacity; \
  327. } \
  328. \
  329. /** shuffle the populated contents to reset `head` to 0 */ \
  330. static inline void name##_sync(name##_t *list) { \
  331. assert(list != NULL); \
  332. \
  333. /* Shuffle the list 1-1 until it is aligned. This is not efficient, but */ \
  334. /* we assume this is a relatively rare operation. */ \
  335. while (list->head != 0) { \
  336. /* rotate the list leftwards by 1 */ \
  337. assert(list->capacity > 0); \
  338. type replacement = list->base[0]; \
  339. for (size_t i = list->capacity - 1; i != SIZE_MAX; --i) { \
  340. type temp = list->base[i]; \
  341. list->base[i] = replacement; \
  342. replacement = temp; \
  343. } \
  344. --list->head; \
  345. } \
  346. \
  347. /* synchronization should have ensured the list no longer wraps */ \
  348. assert(name##_is_contiguous(list)); \
  349. } \
  350. \
  351. /** sort the list using the given comparator */ \
  352. static inline LIST_UNUSED void name##_sort( \
  353. name##_t *list, int (*cmp)(const type *a, const type *b)) { \
  354. assert(list != NULL); \
  355. assert(cmp != NULL); \
  356. \
  357. name##_sync(list); \
  358. \
  359. int (*compar)(const void *, const void *) = \
  360. (int (*)(const void *, const void *))cmp; \
  361. if (list->size > 0) { \
  362. qsort(list->base, list->size, sizeof(type), compar); \
  363. } \
  364. } \
  365. \
  366. /** flip the order of elements in the list */ \
  367. static inline LIST_UNUSED void name##_reverse(name##_t *list) { \
  368. assert(list != NULL); \
  369. \
  370. for (size_t i = 0; i < name##_size(list) / 2; ++i) { \
  371. type const temp1 = name##_get(list, i); \
  372. type const temp2 = name##_get(list, name##_size(list) - i - 1); \
  373. name##_set(list, i, temp2); \
  374. name##_set(list, name##_size(list) - i - 1, temp1); \
  375. } \
  376. } \
  377. \
  378. /** deallocate unused backing storage, shrinking capacity to size */ \
  379. static inline LIST_UNUSED void name##_shrink_to_fit(name##_t *list) { \
  380. assert(list != NULL); \
  381. \
  382. name##_sync(list); \
  383. \
  384. if (list->capacity > list->size) { \
  385. list->base = (type *)gv_recalloc(list->base, list->capacity, list->size, \
  386. sizeof(type)); \
  387. list->capacity = list->size; \
  388. } \
  389. } \
  390. \
  391. /** free resources associated with a list */ \
  392. static inline LIST_UNUSED void name##_free(name##_t *list) { \
  393. assert(list != NULL); \
  394. name##_clear(list); \
  395. free(list->base); \
  396. memset(list, 0, sizeof(*list)); \
  397. } \
  398. \
  399. /** alias for append */ \
  400. static inline LIST_UNUSED void name##_push_back(name##_t *list, \
  401. type value) { \
  402. name##_append(list, value); \
  403. } \
  404. \
  405. /** remove and return first element */ \
  406. static inline LIST_UNUSED type name##_pop_front(name##_t *list) { \
  407. assert(list != NULL); \
  408. assert(list->size > 0); \
  409. \
  410. type value = name##_get(list, 0); \
  411. \
  412. /* do not call `dtor` because we are transferring ownership of the removed \
  413. * element to the caller \
  414. */ \
  415. list->head = (list->head + 1) % list->capacity; \
  416. --list->size; \
  417. \
  418. return value; \
  419. } \
  420. \
  421. /** remove and return last element */ \
  422. static inline LIST_UNUSED type name##_pop_back(name##_t *list) { \
  423. assert(list != NULL); \
  424. assert(list->size > 0); \
  425. \
  426. type value = name##_get(list, list->size - 1); \
  427. \
  428. /* do not call `dtor` because we are transferring ownership of the removed \
  429. * element to the caller \
  430. */ \
  431. --list->size; \
  432. \
  433. return value; \
  434. } \
  435. \
  436. /** create a new list from a bare array and element count \
  437. * \
  438. * This can be useful when receiving data from a caller who does not use \
  439. * this API, but the callee wants to. Note that the backing data for the \
  440. * array must have been heap-allocated. \
  441. * \
  442. * \param data Array of existing elements \
  443. * \param size Number of elements pointed to by `data` \
  444. * \return A managed list containing the provided elements \
  445. */ \
  446. static inline LIST_UNUSED name##_t name##_attach(type *data, size_t size) { \
  447. assert(data != NULL || size == 0); \
  448. name##_t list = {data, 0, size, size}; \
  449. return list; \
  450. } \
  451. \
  452. /** transform a managed list into a bare array \
  453. * \
  454. * This can be useful when needing to pass data to a callee who does not \
  455. * use this API. The managed list is emptied and left in a state where it \
  456. * can be reused for other purposes. \
  457. * \
  458. * \param list List to operate on \
  459. * \return A pointer to an array of the `list->size` elements \
  460. */ \
  461. static inline LIST_UNUSED type *name##_detach(name##_t *list) { \
  462. assert(list != NULL); \
  463. name##_sync(list); \
  464. type *data = list->base; \
  465. memset(list, 0, sizeof(*list)); \
  466. return data; \
  467. }