priority_queue.c 8.9 KB

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