123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- /*
- * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- #include <openssl/crypto.h>
- #include <openssl/err.h>
- #include <assert.h>
- #include "internal/priority_queue.h"
- #include "internal/safe_math.h"
- #include "internal/numbers.h"
- OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
- /*
- * Fundamental operations:
- * Binary Heap Fibonacci Heap
- * Get smallest O(1) O(1)
- * Delete any O(log n) O(log n) average but worst O(n)
- * Insert O(log n) O(1)
- *
- * Not supported:
- * Merge two structures O(log n) O(1)
- * Decrease key O(log n) O(1)
- * Increase key O(log n) ?
- *
- * The Fibonacci heap is quite a bit more complicated to implement and has
- * larger overhead in practice. We favour the binary heap here. A multi-way
- * (ternary or quaternary) heap might elicit a performance advantage via better
- * cache access patterns.
- */
- struct pq_heap_st {
- void *data; /* User supplied data pointer */
- size_t index; /* Constant index in elements[] */
- };
- struct pq_elem_st {
- size_t posn; /* Current index in heap[] or link in free list */
- #ifndef NDEBUG
- int used; /* Debug flag indicating that this is in use */
- #endif
- };
- struct ossl_pqueue_st
- {
- struct pq_heap_st *heap;
- struct pq_elem_st *elements;
- int (*compare)(const void *, const void *);
- size_t htop; /* Highest used heap element */
- size_t hmax; /* Allocated heap & element space */
- size_t freelist; /* Index into elements[], start of free element list */
- };
- /*
- * The initial and maximum number of elements in the heap.
- */
- static const size_t min_nodes = 8;
- static const size_t max_nodes =
- SIZE_MAX / (sizeof(struct pq_heap_st) > sizeof(struct pq_elem_st)
- ? sizeof(struct pq_heap_st) : sizeof(struct pq_elem_st));
- #ifndef NDEBUG
- /* Some basic sanity checking of the data structure */
- # define ASSERT_USED(pq, idx) \
- assert(pq->elements[pq->heap[idx].index].used); \
- assert(pq->elements[pq->heap[idx].index].posn == idx)
- # define ASSERT_ELEM_USED(pq, elem) \
- assert(pq->elements[elem].used)
- #else
- # define ASSERT_USED(pq, idx)
- # define ASSERT_ELEM_USED(pq, elem)
- #endif
- /*
- * Calculate the array growth based on the target size.
- *
- * The growth factor is a rational number and is defined by a numerator
- * and a denominator. According to Andrew Koenig in his paper "Why Are
- * Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less
- * than the golden ratio (1.618...).
- *
- * We use an expansion factor of 8 / 5 = 1.6
- */
- static ossl_inline size_t compute_pqueue_growth(size_t target, size_t current)
- {
- int err = 0;
- while (current < target) {
- if (current >= max_nodes)
- return 0;
- current = safe_muldiv_size_t(current, 8, 5, &err);
- if (err)
- return 0;
- if (current >= max_nodes)
- current = max_nodes;
- }
- return current;
- }
- static ossl_inline void pqueue_swap_elem(OSSL_PQUEUE *pq, size_t i, size_t j)
- {
- struct pq_heap_st *h = pq->heap, t_h;
- struct pq_elem_st *e = pq->elements;
- ASSERT_USED(pq, i);
- ASSERT_USED(pq, j);
- t_h = h[i];
- h[i] = h[j];
- h[j] = t_h;
- e[h[i].index].posn = i;
- e[h[j].index].posn = j;
- }
- static ossl_inline void pqueue_move_elem(OSSL_PQUEUE *pq, size_t from, size_t to)
- {
- struct pq_heap_st *h = pq->heap;
- struct pq_elem_st *e = pq->elements;
- ASSERT_USED(pq, from);
- h[to] = h[from];
- e[h[to].index].posn = to;
- }
- /*
- * Force the specified element to the front of the heap. This breaks
- * the heap partial ordering pre-condition.
- */
- static ossl_inline void pqueue_force_bottom(OSSL_PQUEUE *pq, size_t n)
- {
- ASSERT_USED(pq, n);
- while (n > 0) {
- const size_t p = (n - 1) / 2;
- ASSERT_USED(pq, p);
- pqueue_swap_elem(pq, n, p);
- n = p;
- }
- }
- /*
- * Move an element down to its correct position to restore the partial
- * order pre-condition.
- */
- static ossl_inline void pqueue_move_down(OSSL_PQUEUE *pq, size_t n)
- {
- struct pq_heap_st *h = pq->heap;
- ASSERT_USED(pq, n);
- while (n > 0) {
- const size_t p = (n - 1) / 2;
- ASSERT_USED(pq, p);
- if (pq->compare(h[n].data, h[p].data) >= 0)
- break;
- pqueue_swap_elem(pq, n, p);
- n = p;
- }
- }
- /*
- * Move an element up to its correct position to restore the partial
- * order pre-condition.
- */
- static ossl_inline void pqueue_move_up(OSSL_PQUEUE *pq, size_t n)
- {
- struct pq_heap_st *h = pq->heap;
- size_t p = n * 2 + 1;
- ASSERT_USED(pq, n);
- if (pq->htop > p + 1) {
- ASSERT_USED(pq, p);
- ASSERT_USED(pq, p + 1);
- if (pq->compare(h[p].data, h[p + 1].data) > 0)
- p++;
- }
- while (pq->htop > p && pq->compare(h[p].data, h[n].data) < 0) {
- ASSERT_USED(pq, p);
- pqueue_swap_elem(pq, n, p);
- n = p;
- p = n * 2 + 1;
- if (pq->htop > p + 1) {
- ASSERT_USED(pq, p + 1);
- if (pq->compare(h[p].data, h[p + 1].data) > 0)
- p++;
- }
- }
- }
- int ossl_pqueue_push(OSSL_PQUEUE *pq, void *data, size_t *elem)
- {
- size_t n, m;
- if (!ossl_pqueue_reserve(pq, 1))
- return 0;
- n = pq->htop++;
- m = pq->freelist;
- pq->freelist = pq->elements[m].posn;
- pq->heap[n].data = data;
- pq->heap[n].index = m;
- pq->elements[m].posn = n;
- #ifndef NDEBUG
- pq->elements[m].used = 1;
- #endif
- pqueue_move_down(pq, n);
- if (elem != NULL)
- *elem = m;
- return 1;
- }
- void *ossl_pqueue_peek(const OSSL_PQUEUE *pq)
- {
- if (pq->htop > 0) {
- ASSERT_USED(pq, 0);
- return pq->heap->data;
- }
- return NULL;
- }
- void *ossl_pqueue_pop(OSSL_PQUEUE *pq)
- {
- void *res;
- size_t elem;
- if (pq == NULL || pq->htop == 0)
- return NULL;
- ASSERT_USED(pq, 0);
- res = pq->heap->data;
- elem = pq->heap->index;
- if (--pq->htop != 0) {
- pqueue_move_elem(pq, pq->htop, 0);
- pqueue_move_up(pq, 0);
- }
- pq->elements[elem].posn = pq->freelist;
- pq->freelist = elem;
- #ifndef NDEBUG
- pq->elements[elem].used = 0;
- #endif
- return res;
- }
- void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem)
- {
- size_t n;
- if (pq == NULL || elem >= pq->hmax || pq->htop == 0)
- return 0;
- ASSERT_ELEM_USED(pq, elem);
- n = pq->elements[elem].posn;
- ASSERT_USED(pq, n);
- if (n == pq->htop - 1) {
- pq->elements[elem].posn = pq->freelist;
- pq->freelist = elem;
- #ifndef NDEBUG
- pq->elements[elem].used = 0;
- #endif
- return pq->heap[--pq->htop].data;
- }
- if (n > 0)
- pqueue_force_bottom(pq, n);
- return ossl_pqueue_pop(pq);
- }
- static void pqueue_add_freelist(OSSL_PQUEUE *pq, size_t from)
- {
- struct pq_elem_st *e = pq->elements;
- size_t i;
- #ifndef NDEBUG
- for (i = from; i < pq->hmax; i++)
- e[i].used = 0;
- #endif
- e[from].posn = pq->freelist;
- for (i = from + 1; i < pq->hmax; i++)
- e[i].posn = i - 1;
- pq->freelist = pq->hmax - 1;
- }
- int ossl_pqueue_reserve(OSSL_PQUEUE *pq, size_t n)
- {
- size_t new_max, cur_max;
- struct pq_heap_st *h;
- struct pq_elem_st *e;
- if (pq == NULL)
- return 0;
- cur_max = pq->hmax;
- if (pq->htop + n < cur_max)
- return 1;
- new_max = compute_pqueue_growth(n + cur_max, cur_max);
- if (new_max == 0) {
- ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- h = OPENSSL_realloc(pq->heap, new_max * sizeof(*pq->heap));
- if (h == NULL)
- return 0;
- pq->heap = h;
- e = OPENSSL_realloc(pq->elements, new_max * sizeof(*pq->elements));
- if (e == NULL)
- return 0;
- pq->elements = e;
- pq->hmax = new_max;
- pqueue_add_freelist(pq, cur_max);
- return 1;
- }
- OSSL_PQUEUE *ossl_pqueue_new(int (*compare)(const void *, const void *))
- {
- OSSL_PQUEUE *pq;
- if (compare == NULL)
- return NULL;
- pq = OPENSSL_malloc(sizeof(*pq));
- if (pq == NULL)
- return NULL;
- pq->compare = compare;
- pq->hmax = min_nodes;
- pq->htop = 0;
- pq->freelist = 0;
- pq->heap = OPENSSL_malloc(sizeof(*pq->heap) * min_nodes);
- pq->elements = OPENSSL_malloc(sizeof(*pq->elements) * min_nodes);
- if (pq->heap == NULL || pq->elements == NULL) {
- ossl_pqueue_free(pq);
- return NULL;
- }
- pqueue_add_freelist(pq, 0);
- return pq;
- }
- void ossl_pqueue_free(OSSL_PQUEUE *pq)
- {
- if (pq != NULL) {
- OPENSSL_free(pq->heap);
- OPENSSL_free(pq->elements);
- OPENSSL_free(pq);
- }
- }
- void ossl_pqueue_pop_free(OSSL_PQUEUE *pq, void (*freefunc)(void *))
- {
- size_t i;
- if (pq != NULL) {
- for (i = 0; i < pq->htop; i++)
- (*freefunc)(pq->heap[i].data);
- ossl_pqueue_free(pq);
- }
- }
- size_t ossl_pqueue_num(const OSSL_PQUEUE *pq)
- {
- return pq != NULL ? pq->htop : 0;
- }
|