priority_queue.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License 2.0 (the "License"). You may not use
  5. * this file except in compliance with the License. You can obtain a copy
  6. * in the file LICENSE in the source distribution or at
  7. * https://www.openssl.org/source/license.html
  8. */
  9. #include <openssl/crypto.h>
  10. #include <openssl/err.h>
  11. #include <assert.h>
  12. #include "internal/priority_queue.h"
  13. #include "internal/safe_math.h"
  14. #include "internal/numbers.h"
  15. OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
  16. /*
  17. * Fundamental operations:
  18. * Binary Heap Fibonacci Heap
  19. * Get smallest O(1) O(1)
  20. * Delete any O(log n) O(log n) average but worst O(n)
  21. * Insert O(log n) O(1)
  22. *
  23. * Not supported:
  24. * Merge two structures O(log n) O(1)
  25. * Decrease key O(log n) O(1)
  26. * Increase key O(log n) ?
  27. *
  28. * The Fibonacci heap is quite a bit more complicated to implement and has
  29. * larger overhead in practice. We favour the binary heap here. A multi-way
  30. * (ternary or quaternary) heap might elicit a performance advantage via better
  31. * cache access patterns.
  32. */
  33. struct pq_heap_st {
  34. void *data; /* User supplied data pointer */
  35. size_t index; /* Constant index in elements[] */
  36. };
  37. struct pq_elem_st {
  38. size_t posn; /* Current index in heap[] or link in free list */
  39. #ifndef NDEBUG
  40. int used; /* Debug flag indicating that this is in use */
  41. #endif
  42. };
  43. struct ossl_pqueue_st
  44. {
  45. struct pq_heap_st *heap;
  46. struct pq_elem_st *elements;
  47. int (*compare)(const void *, const void *);
  48. size_t htop; /* Highest used heap element */
  49. size_t hmax; /* Allocated heap & element space */
  50. size_t freelist; /* Index into elements[], start of free element list */
  51. };
  52. /*
  53. * The initial and maximum number of elements in the heap.
  54. */
  55. static const size_t min_nodes = 8;
  56. static const size_t max_nodes =
  57. SIZE_MAX / (sizeof(struct pq_heap_st) > sizeof(struct pq_elem_st)
  58. ? sizeof(struct pq_heap_st) : sizeof(struct pq_elem_st));
  59. #ifndef NDEBUG
  60. /* Some basic sanity checking of the data structure */
  61. # define ASSERT_USED(pq, idx) \
  62. assert(pq->elements[pq->heap[idx].index].used); \
  63. assert(pq->elements[pq->heap[idx].index].posn == idx)
  64. # define ASSERT_ELEM_USED(pq, elem) \
  65. assert(pq->elements[elem].used)
  66. #else
  67. # define ASSERT_USED(pq, idx)
  68. # define ASSERT_ELEM_USED(pq, elem)
  69. #endif
  70. /*
  71. * Calculate the array growth based on the target size.
  72. *
  73. * The growth factor is a rational number and is defined by a numerator
  74. * and a denominator. According to Andrew Koenig in his paper "Why Are
  75. * Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less
  76. * than the golden ratio (1.618...).
  77. *
  78. * We use an expansion factor of 8 / 5 = 1.6
  79. */
  80. static ossl_inline size_t compute_pqueue_growth(size_t target, size_t current)
  81. {
  82. int err = 0;
  83. while (current < target) {
  84. if (current >= max_nodes)
  85. return 0;
  86. current = safe_muldiv_size_t(current, 8, 5, &err);
  87. if (err)
  88. return 0;
  89. if (current >= max_nodes)
  90. current = max_nodes;
  91. }
  92. return current;
  93. }
  94. static ossl_inline void pqueue_swap_elem(OSSL_PQUEUE *pq, size_t i, size_t j)
  95. {
  96. struct pq_heap_st *h = pq->heap, t_h;
  97. struct pq_elem_st *e = pq->elements;
  98. ASSERT_USED(pq, i);
  99. ASSERT_USED(pq, j);
  100. t_h = h[i];
  101. h[i] = h[j];
  102. h[j] = t_h;
  103. e[h[i].index].posn = i;
  104. e[h[j].index].posn = j;
  105. }
  106. static ossl_inline void pqueue_move_elem(OSSL_PQUEUE *pq, size_t from, size_t to)
  107. {
  108. struct pq_heap_st *h = pq->heap;
  109. struct pq_elem_st *e = pq->elements;
  110. ASSERT_USED(pq, from);
  111. h[to] = h[from];
  112. e[h[to].index].posn = to;
  113. }
  114. /*
  115. * Force the specified element to the front of the heap. This breaks
  116. * the heap partial ordering pre-condition.
  117. */
  118. static ossl_inline void pqueue_force_bottom(OSSL_PQUEUE *pq, size_t n)
  119. {
  120. ASSERT_USED(pq, n);
  121. while (n > 0) {
  122. const size_t p = (n - 1) / 2;
  123. ASSERT_USED(pq, p);
  124. pqueue_swap_elem(pq, n, p);
  125. n = p;
  126. }
  127. }
  128. /*
  129. * Move an element down to its correct position to restore the partial
  130. * order pre-condition.
  131. */
  132. static ossl_inline void pqueue_move_down(OSSL_PQUEUE *pq, size_t n)
  133. {
  134. struct pq_heap_st *h = pq->heap;
  135. ASSERT_USED(pq, n);
  136. while (n > 0) {
  137. const size_t p = (n - 1) / 2;
  138. ASSERT_USED(pq, p);
  139. if (pq->compare(h[n].data, h[p].data) >= 0)
  140. break;
  141. pqueue_swap_elem(pq, n, p);
  142. n = p;
  143. }
  144. }
  145. /*
  146. * Move an element up to its correct position to restore the partial
  147. * order pre-condition.
  148. */
  149. static ossl_inline void pqueue_move_up(OSSL_PQUEUE *pq, size_t n)
  150. {
  151. struct pq_heap_st *h = pq->heap;
  152. size_t p = n * 2 + 1;
  153. ASSERT_USED(pq, n);
  154. if (pq->htop > p + 1) {
  155. ASSERT_USED(pq, p);
  156. ASSERT_USED(pq, p + 1);
  157. if (pq->compare(h[p].data, h[p + 1].data) > 0)
  158. p++;
  159. }
  160. while (pq->htop > p && pq->compare(h[p].data, h[n].data) < 0) {
  161. ASSERT_USED(pq, p);
  162. pqueue_swap_elem(pq, n, p);
  163. n = p;
  164. p = n * 2 + 1;
  165. if (pq->htop > p + 1) {
  166. ASSERT_USED(pq, p + 1);
  167. if (pq->compare(h[p].data, h[p + 1].data) > 0)
  168. p++;
  169. }
  170. }
  171. }
  172. int ossl_pqueue_push(OSSL_PQUEUE *pq, void *data, size_t *elem)
  173. {
  174. size_t n, m;
  175. if (!ossl_pqueue_reserve(pq, 1))
  176. return 0;
  177. n = pq->htop++;
  178. m = pq->freelist;
  179. pq->freelist = pq->elements[m].posn;
  180. pq->heap[n].data = data;
  181. pq->heap[n].index = m;
  182. pq->elements[m].posn = n;
  183. #ifndef NDEBUG
  184. pq->elements[m].used = 1;
  185. #endif
  186. pqueue_move_down(pq, n);
  187. if (elem != NULL)
  188. *elem = m;
  189. return 1;
  190. }
  191. void *ossl_pqueue_peek(const OSSL_PQUEUE *pq)
  192. {
  193. if (pq->htop > 0) {
  194. ASSERT_USED(pq, 0);
  195. return pq->heap->data;
  196. }
  197. return NULL;
  198. }
  199. void *ossl_pqueue_pop(OSSL_PQUEUE *pq)
  200. {
  201. void *res;
  202. size_t elem;
  203. if (pq == NULL || pq->htop == 0)
  204. return NULL;
  205. ASSERT_USED(pq, 0);
  206. res = pq->heap->data;
  207. elem = pq->heap->index;
  208. if (--pq->htop != 0) {
  209. pqueue_move_elem(pq, pq->htop, 0);
  210. pqueue_move_up(pq, 0);
  211. }
  212. pq->elements[elem].posn = pq->freelist;
  213. pq->freelist = elem;
  214. #ifndef NDEBUG
  215. pq->elements[elem].used = 0;
  216. #endif
  217. return res;
  218. }
  219. void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem)
  220. {
  221. size_t n;
  222. if (pq == NULL || elem >= pq->hmax || pq->htop == 0)
  223. return 0;
  224. ASSERT_ELEM_USED(pq, elem);
  225. n = pq->elements[elem].posn;
  226. ASSERT_USED(pq, n);
  227. if (n == pq->htop - 1) {
  228. pq->elements[elem].posn = pq->freelist;
  229. pq->freelist = elem;
  230. #ifndef NDEBUG
  231. pq->elements[elem].used = 0;
  232. #endif
  233. return pq->heap[--pq->htop].data;
  234. }
  235. if (n > 0)
  236. pqueue_force_bottom(pq, n);
  237. return ossl_pqueue_pop(pq);
  238. }
  239. static void pqueue_add_freelist(OSSL_PQUEUE *pq, size_t from)
  240. {
  241. struct pq_elem_st *e = pq->elements;
  242. size_t i;
  243. #ifndef NDEBUG
  244. for (i = from; i < pq->hmax; i++)
  245. e[i].used = 0;
  246. #endif
  247. e[from].posn = pq->freelist;
  248. for (i = from + 1; i < pq->hmax; i++)
  249. e[i].posn = i - 1;
  250. pq->freelist = pq->hmax - 1;
  251. }
  252. int ossl_pqueue_reserve(OSSL_PQUEUE *pq, size_t n)
  253. {
  254. size_t new_max, cur_max;
  255. struct pq_heap_st *h;
  256. struct pq_elem_st *e;
  257. if (pq == NULL)
  258. return 0;
  259. cur_max = pq->hmax;
  260. if (pq->htop + n < cur_max)
  261. return 1;
  262. new_max = compute_pqueue_growth(n + cur_max, cur_max);
  263. if (new_max == 0) {
  264. ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
  265. return 0;
  266. }
  267. h = OPENSSL_realloc(pq->heap, new_max * sizeof(*pq->heap));
  268. if (h == NULL)
  269. return 0;
  270. pq->heap = h;
  271. e = OPENSSL_realloc(pq->elements, new_max * sizeof(*pq->elements));
  272. if (e == NULL)
  273. return 0;
  274. pq->elements = e;
  275. pq->hmax = new_max;
  276. pqueue_add_freelist(pq, cur_max);
  277. return 1;
  278. }
  279. OSSL_PQUEUE *ossl_pqueue_new(int (*compare)(const void *, const void *))
  280. {
  281. OSSL_PQUEUE *pq;
  282. if (compare == NULL)
  283. return NULL;
  284. pq = OPENSSL_malloc(sizeof(*pq));
  285. if (pq == NULL)
  286. return NULL;
  287. pq->compare = compare;
  288. pq->hmax = min_nodes;
  289. pq->htop = 0;
  290. pq->freelist = 0;
  291. pq->heap = OPENSSL_malloc(sizeof(*pq->heap) * min_nodes);
  292. pq->elements = OPENSSL_malloc(sizeof(*pq->elements) * min_nodes);
  293. if (pq->heap == NULL || pq->elements == NULL) {
  294. ossl_pqueue_free(pq);
  295. return NULL;
  296. }
  297. pqueue_add_freelist(pq, 0);
  298. return pq;
  299. }
  300. void ossl_pqueue_free(OSSL_PQUEUE *pq)
  301. {
  302. if (pq != NULL) {
  303. OPENSSL_free(pq->heap);
  304. OPENSSL_free(pq->elements);
  305. OPENSSL_free(pq);
  306. }
  307. }
  308. void ossl_pqueue_pop_free(OSSL_PQUEUE *pq, void (*freefunc)(void *))
  309. {
  310. size_t i;
  311. if (pq != NULL) {
  312. for (i = 0; i < pq->htop; i++)
  313. (*freefunc)(pq->heap[i].data);
  314. ossl_pqueue_free(pq);
  315. }
  316. }
  317. size_t ossl_pqueue_num(const OSSL_PQUEUE *pq)
  318. {
  319. return pq != NULL ? pq->htop : 0;
  320. }