Path: blob/main/crypto/openssl/ssl/priority_queue.c
102349 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 = SIZE_MAX / (sizeof(struct pq_heap_st) > sizeof(struct pq_elem_st) ? sizeof(struct pq_heap_st) : sizeof(struct pq_elem_st));6263#ifndef NDEBUG64/* Some basic sanity checking of the data structure */65#define ASSERT_USED(pq, idx) \66assert(pq->elements[pq->heap[idx].index].used); \67assert(pq->elements[pq->heap[idx].index].posn == idx)68#define ASSERT_ELEM_USED(pq, elem) \69assert(pq->elements[elem].used)70#else71#define ASSERT_USED(pq, idx)72#define ASSERT_ELEM_USED(pq, elem)73#endif7475/*76* Calculate the array growth based on the target size.77*78* The growth factor is a rational number and is defined by a numerator79* and a denominator. According to Andrew Koenig in his paper "Why Are80* Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less81* than the golden ratio (1.618...).82*83* We use an expansion factor of 8 / 5 = 1.684*/85static ossl_inline size_t compute_pqueue_growth(size_t target, size_t current)86{87int err = 0;8889while (current < target) {90if (current >= max_nodes)91return 0;9293current = safe_muldiv_size_t(current, 8, 5, &err);94if (err)95return 0;96if (current >= max_nodes)97current = max_nodes;98}99return current;100}101102static ossl_inline void pqueue_swap_elem(OSSL_PQUEUE *pq, size_t i, size_t j)103{104struct pq_heap_st *h = pq->heap, t_h;105struct pq_elem_st *e = pq->elements;106107ASSERT_USED(pq, i);108ASSERT_USED(pq, j);109110t_h = h[i];111h[i] = h[j];112h[j] = t_h;113114e[h[i].index].posn = i;115e[h[j].index].posn = j;116}117118static ossl_inline void pqueue_move_elem(OSSL_PQUEUE *pq, size_t from, size_t to)119{120struct pq_heap_st *h = pq->heap;121struct pq_elem_st *e = pq->elements;122123ASSERT_USED(pq, from);124125h[to] = h[from];126e[h[to].index].posn = to;127}128129/*130* Force the specified element to the front of the heap. This breaks131* the heap partial ordering pre-condition.132*/133static ossl_inline void pqueue_force_bottom(OSSL_PQUEUE *pq, size_t n)134{135ASSERT_USED(pq, n);136while (n > 0) {137const size_t p = (n - 1) / 2;138139ASSERT_USED(pq, p);140pqueue_swap_elem(pq, n, p);141n = p;142}143}144145/*146* Move an element down to its correct position to restore the partial147* order pre-condition.148*/149static ossl_inline void pqueue_move_down(OSSL_PQUEUE *pq, size_t n)150{151struct pq_heap_st *h = pq->heap;152153ASSERT_USED(pq, n);154while (n > 0) {155const size_t p = (n - 1) / 2;156157ASSERT_USED(pq, p);158if (pq->compare(h[n].data, h[p].data) >= 0)159break;160pqueue_swap_elem(pq, n, p);161n = p;162}163}164165/*166* Move an element up to its correct position to restore the partial167* order pre-condition.168*/169static ossl_inline void pqueue_move_up(OSSL_PQUEUE *pq, size_t n)170{171struct pq_heap_st *h = pq->heap;172size_t p = n * 2 + 1;173174ASSERT_USED(pq, n);175if (pq->htop > p + 1) {176ASSERT_USED(pq, p);177ASSERT_USED(pq, p + 1);178if (pq->compare(h[p].data, h[p + 1].data) > 0)179p++;180}181while (pq->htop > p && pq->compare(h[p].data, h[n].data) < 0) {182ASSERT_USED(pq, p);183pqueue_swap_elem(pq, n, p);184n = p;185p = n * 2 + 1;186if (pq->htop > p + 1) {187ASSERT_USED(pq, p + 1);188if (pq->compare(h[p].data, h[p + 1].data) > 0)189p++;190}191}192}193194int ossl_pqueue_push(OSSL_PQUEUE *pq, void *data, size_t *elem)195{196size_t n, m;197198if (!ossl_pqueue_reserve(pq, 1))199return 0;200201n = pq->htop++;202m = pq->freelist;203pq->freelist = pq->elements[m].posn;204205pq->heap[n].data = data;206pq->heap[n].index = m;207208pq->elements[m].posn = n;209#ifndef NDEBUG210pq->elements[m].used = 1;211#endif212pqueue_move_down(pq, n);213if (elem != NULL)214*elem = m;215return 1;216}217218void *ossl_pqueue_peek(const OSSL_PQUEUE *pq)219{220if (pq->htop > 0) {221ASSERT_USED(pq, 0);222return pq->heap->data;223}224return NULL;225}226227void *ossl_pqueue_pop(OSSL_PQUEUE *pq)228{229void *res;230size_t elem;231232if (pq == NULL || pq->htop == 0)233return NULL;234235ASSERT_USED(pq, 0);236res = pq->heap->data;237elem = pq->heap->index;238239if (--pq->htop != 0) {240pqueue_move_elem(pq, pq->htop, 0);241pqueue_move_up(pq, 0);242}243244pq->elements[elem].posn = pq->freelist;245pq->freelist = elem;246#ifndef NDEBUG247pq->elements[elem].used = 0;248#endif249return res;250}251252void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem)253{254size_t n;255256if (pq == NULL || elem >= pq->hmax || pq->htop == 0)257return 0;258259ASSERT_ELEM_USED(pq, elem);260n = pq->elements[elem].posn;261262ASSERT_USED(pq, n);263264if (n == pq->htop - 1) {265pq->elements[elem].posn = pq->freelist;266pq->freelist = elem;267#ifndef NDEBUG268pq->elements[elem].used = 0;269#endif270return pq->heap[--pq->htop].data;271}272if (n > 0)273pqueue_force_bottom(pq, n);274return ossl_pqueue_pop(pq);275}276277static void pqueue_add_freelist(OSSL_PQUEUE *pq, size_t from)278{279struct pq_elem_st *e = pq->elements;280size_t i;281282#ifndef NDEBUG283for (i = from; i < pq->hmax; i++)284e[i].used = 0;285#endif286e[from].posn = pq->freelist;287for (i = from + 1; i < pq->hmax; i++)288e[i].posn = i - 1;289pq->freelist = pq->hmax - 1;290}291292int ossl_pqueue_reserve(OSSL_PQUEUE *pq, size_t n)293{294size_t new_max, cur_max;295struct pq_heap_st *h;296struct pq_elem_st *e;297298if (pq == NULL)299return 0;300cur_max = pq->hmax;301if (pq->htop + n < cur_max)302return 1;303304new_max = compute_pqueue_growth(n + cur_max, cur_max);305if (new_max == 0) {306ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);307return 0;308}309310h = OPENSSL_realloc(pq->heap, new_max * sizeof(*pq->heap));311if (h == NULL)312return 0;313pq->heap = h;314315e = OPENSSL_realloc(pq->elements, new_max * sizeof(*pq->elements));316if (e == NULL)317return 0;318pq->elements = e;319320pq->hmax = new_max;321pqueue_add_freelist(pq, cur_max);322return 1;323}324325OSSL_PQUEUE *ossl_pqueue_new(int (*compare)(const void *, const void *))326{327OSSL_PQUEUE *pq;328329if (compare == NULL)330return NULL;331332pq = OPENSSL_malloc(sizeof(*pq));333if (pq == NULL)334return NULL;335pq->compare = compare;336pq->hmax = min_nodes;337pq->htop = 0;338pq->freelist = 0;339pq->heap = OPENSSL_malloc(sizeof(*pq->heap) * min_nodes);340pq->elements = OPENSSL_malloc(sizeof(*pq->elements) * min_nodes);341if (pq->heap == NULL || pq->elements == NULL) {342ossl_pqueue_free(pq);343return NULL;344}345pqueue_add_freelist(pq, 0);346return pq;347}348349void ossl_pqueue_free(OSSL_PQUEUE *pq)350{351if (pq != NULL) {352OPENSSL_free(pq->heap);353OPENSSL_free(pq->elements);354OPENSSL_free(pq);355}356}357358void ossl_pqueue_pop_free(OSSL_PQUEUE *pq, void (*freefunc)(void *))359{360size_t i;361362if (pq != NULL) {363for (i = 0; i < pq->htop; i++)364(*freefunc)(pq->heap[i].data);365ossl_pqueue_free(pq);366}367}368369size_t ossl_pqueue_num(const OSSL_PQUEUE *pq)370{371return pq != NULL ? pq->htop : 0;372}373374375