Coverage Report

Created: 2025-11-16 06:40

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