Path: blob/main/crypto/openssl/ssl/priority_queue.c
48150 views
/*1* Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#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"1516OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)1718/*19* Fundamental operations:20* Binary Heap Fibonacci Heap21* Get smallest O(1) O(1)22* Delete any O(log n) O(log n) average but worst O(n)23* Insert O(log n) O(1)24*25* Not supported:26* Merge two structures O(log n) O(1)27* Decrease key O(log n) O(1)28* Increase key O(log n) ?29*30* The Fibonacci heap is quite a bit more complicated to implement and has31* larger overhead in practice. We favour the binary heap here. A multi-way32* (ternary or quaternary) heap might elicit a performance advantage via better33* cache access patterns.34*/3536struct pq_heap_st {37void *data; /* User supplied data pointer */38size_t index; /* Constant index in elements[] */39};4041struct pq_elem_st {42size_t posn; /* Current index in heap[] or link in free list */43#ifndef NDEBUG44int used; /* Debug flag indicating that this is in use */45#endif46};4748struct ossl_pqueue_st {49struct pq_heap_st *heap;50struct pq_elem_st *elements;51int (*compare)(const void *, const void *);52size_t htop; /* Highest used heap element */53size_t hmax; /* Allocated heap & element space */54size_t freelist; /* Index into elements[], start of free element list */55};5657/*58* The initial and maximum number of elements in the heap.59*/60static const size_t min_nodes = 8;61static const size_t max_nodes =62SIZE_MAX / (sizeof(struct pq_heap_st) > sizeof(struct pq_elem_st)63? sizeof(struct pq_heap_st) : sizeof(struct pq_elem_st));6465#ifndef NDEBUG66/* Some basic sanity checking of the data structure */67# define ASSERT_USED(pq, idx) \68assert(pq->elements[pq->heap[idx].index].used); \69assert(pq->elements[pq->heap[idx].index].posn == idx)70# define ASSERT_ELEM_USED(pq, elem) \71assert(pq->elements[elem].used)72#else73# define ASSERT_USED(pq, idx)74# define ASSERT_ELEM_USED(pq, elem)75#endif7677/*78* Calculate the array growth based on the target size.79*80* The growth factor is a rational number and is defined by a numerator81* and a denominator. According to Andrew Koenig in his paper "Why Are82* Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less83* than the golden ratio (1.618...).84*85* We use an expansion factor of 8 / 5 = 1.686*/87static ossl_inline size_t compute_pqueue_growth(size_t target, size_t current)88{89int err = 0;9091while (current < target) {92if (current >= max_nodes)93return 0;9495current = safe_muldiv_size_t(current, 8, 5, &err);96if (err)97return 0;98if (current >= max_nodes)99current = max_nodes;100}101return current;102}103104static ossl_inline void pqueue_swap_elem(OSSL_PQUEUE *pq, size_t i, size_t j)105{106struct pq_heap_st *h = pq->heap, t_h;107struct pq_elem_st *e = pq->elements;108109ASSERT_USED(pq, i);110ASSERT_USED(pq, j);111112t_h = h[i];113h[i] = h[j];114h[j] = t_h;115116e[h[i].index].posn = i;117e[h[j].index].posn = j;118}119120static ossl_inline void pqueue_move_elem(OSSL_PQUEUE *pq, size_t from, size_t to)121{122struct pq_heap_st *h = pq->heap;123struct pq_elem_st *e = pq->elements;124125ASSERT_USED(pq, from);126127h[to] = h[from];128e[h[to].index].posn = to;129}130131/*132* Force the specified element to the front of the heap. This breaks133* the heap partial ordering pre-condition.134*/135static ossl_inline void pqueue_force_bottom(OSSL_PQUEUE *pq, size_t n)136{137ASSERT_USED(pq, n);138while (n > 0) {139const size_t p = (n - 1) / 2;140141ASSERT_USED(pq, p);142pqueue_swap_elem(pq, n, p);143n = p;144}145}146147/*148* Move an element down to its correct position to restore the partial149* order pre-condition.150*/151static ossl_inline void pqueue_move_down(OSSL_PQUEUE *pq, size_t n)152{153struct pq_heap_st *h = pq->heap;154155ASSERT_USED(pq, n);156while (n > 0) {157const size_t p = (n - 1) / 2;158159ASSERT_USED(pq, p);160if (pq->compare(h[n].data, h[p].data) >= 0)161break;162pqueue_swap_elem(pq, n, p);163n = p;164}165}166167/*168* Move an element up to its correct position to restore the partial169* order pre-condition.170*/171static ossl_inline void pqueue_move_up(OSSL_PQUEUE *pq, size_t n)172{173struct pq_heap_st *h = pq->heap;174size_t p = n * 2 + 1;175176ASSERT_USED(pq, n);177if (pq->htop > p + 1) {178ASSERT_USED(pq, p);179ASSERT_USED(pq, p + 1);180if (pq->compare(h[p].data, h[p + 1].data) > 0)181p++;182}183while (pq->htop > p && pq->compare(h[p].data, h[n].data) < 0) {184ASSERT_USED(pq, p);185pqueue_swap_elem(pq, n, p);186n = p;187p = n * 2 + 1;188if (pq->htop > p + 1) {189ASSERT_USED(pq, p + 1);190if (pq->compare(h[p].data, h[p + 1].data) > 0)191p++;192}193}194}195196int ossl_pqueue_push(OSSL_PQUEUE *pq, void *data, size_t *elem)197{198size_t n, m;199200if (!ossl_pqueue_reserve(pq, 1))201return 0;202203n = pq->htop++;204m = pq->freelist;205pq->freelist = pq->elements[m].posn;206207pq->heap[n].data = data;208pq->heap[n].index = m;209210pq->elements[m].posn = n;211#ifndef NDEBUG212pq->elements[m].used = 1;213#endif214pqueue_move_down(pq, n);215if (elem != NULL)216*elem = m;217return 1;218}219220void *ossl_pqueue_peek(const OSSL_PQUEUE *pq)221{222if (pq->htop > 0) {223ASSERT_USED(pq, 0);224return pq->heap->data;225}226return NULL;227}228229void *ossl_pqueue_pop(OSSL_PQUEUE *pq)230{231void *res;232size_t elem;233234if (pq == NULL || pq->htop == 0)235return NULL;236237ASSERT_USED(pq, 0);238res = pq->heap->data;239elem = pq->heap->index;240241if (--pq->htop != 0) {242pqueue_move_elem(pq, pq->htop, 0);243pqueue_move_up(pq, 0);244}245246pq->elements[elem].posn = pq->freelist;247pq->freelist = elem;248#ifndef NDEBUG249pq->elements[elem].used = 0;250#endif251return res;252}253254void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem)255{256size_t n;257258if (pq == NULL || elem >= pq->hmax || pq->htop == 0)259return 0;260261ASSERT_ELEM_USED(pq, elem);262n = pq->elements[elem].posn;263264ASSERT_USED(pq, n);265266if (n == pq->htop - 1) {267pq->elements[elem].posn = pq->freelist;268pq->freelist = elem;269#ifndef NDEBUG270pq->elements[elem].used = 0;271#endif272return pq->heap[--pq->htop].data;273}274if (n > 0)275pqueue_force_bottom(pq, n);276return ossl_pqueue_pop(pq);277}278279static void pqueue_add_freelist(OSSL_PQUEUE *pq, size_t from)280{281struct pq_elem_st *e = pq->elements;282size_t i;283284#ifndef NDEBUG285for (i = from; i < pq->hmax; i++)286e[i].used = 0;287#endif288e[from].posn = pq->freelist;289for (i = from + 1; i < pq->hmax; i++)290e[i].posn = i - 1;291pq->freelist = pq->hmax - 1;292}293294int ossl_pqueue_reserve(OSSL_PQUEUE *pq, size_t n)295{296size_t new_max, cur_max;297struct pq_heap_st *h;298struct pq_elem_st *e;299300if (pq == NULL)301return 0;302cur_max = pq->hmax;303if (pq->htop + n < cur_max)304return 1;305306new_max = compute_pqueue_growth(n + cur_max, cur_max);307if (new_max == 0) {308ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);309return 0;310}311312h = OPENSSL_realloc(pq->heap, new_max * sizeof(*pq->heap));313if (h == NULL)314return 0;315pq->heap = h;316317e = OPENSSL_realloc(pq->elements, new_max * sizeof(*pq->elements));318if (e == NULL)319return 0;320pq->elements = e;321322pq->hmax = new_max;323pqueue_add_freelist(pq, cur_max);324return 1;325}326327OSSL_PQUEUE *ossl_pqueue_new(int (*compare)(const void *, const void *))328{329OSSL_PQUEUE *pq;330331if (compare == NULL)332return NULL;333334pq = OPENSSL_malloc(sizeof(*pq));335if (pq == NULL)336return NULL;337pq->compare = compare;338pq->hmax = min_nodes;339pq->htop = 0;340pq->freelist = 0;341pq->heap = OPENSSL_malloc(sizeof(*pq->heap) * min_nodes);342pq->elements = OPENSSL_malloc(sizeof(*pq->elements) * min_nodes);343if (pq->heap == NULL || pq->elements == NULL) {344ossl_pqueue_free(pq);345return NULL;346}347pqueue_add_freelist(pq, 0);348return pq;349}350351void ossl_pqueue_free(OSSL_PQUEUE *pq)352{353if (pq != NULL) {354OPENSSL_free(pq->heap);355OPENSSL_free(pq->elements);356OPENSSL_free(pq);357}358}359360void ossl_pqueue_pop_free(OSSL_PQUEUE *pq, void (*freefunc)(void *))361{362size_t i;363364if (pq != NULL) {365for (i = 0; i < pq->htop; i++)366(*freefunc)(pq->heap[i].data);367ossl_pqueue_free(pq);368}369}370371size_t ossl_pqueue_num(const OSSL_PQUEUE *pq)372{373return pq != NULL ? pq->htop : 0;374}375376377