Coverage Report

Created: 2025-08-11 07:04

/src/openssl34/ssl/priority_queue.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2022-2024 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
    struct pq_heap_st *heap;
51
    struct pq_elem_st *elements;
52
    int (*compare)(const void *, const void *);
53
    size_t htop;        /* Highest used heap element */
54
    size_t hmax;        /* Allocated heap & element space */
55
    size_t freelist;    /* Index into elements[], start of free element list */
56
};
57
58
/*
59
 * The initial and maximum number of elements in the heap.
60
 */
61
static const size_t min_nodes = 8;
62
static const size_t max_nodes =
63
        SIZE_MAX / (sizeof(struct pq_heap_st) > sizeof(struct pq_elem_st)
64
                    ? sizeof(struct pq_heap_st) : sizeof(struct pq_elem_st));
65
66
#ifndef NDEBUG
67
/* Some basic sanity checking of the data structure */
68
# define ASSERT_USED(pq, idx)                                               \
69
84.6M
    assert(pq->elements[pq->heap[idx].index].used);                         \
70
82.0M
    assert(pq->elements[pq->heap[idx].index].posn == idx)
71
# define ASSERT_ELEM_USED(pq, elem)                                         \
72
2.26M
    assert(pq->elements[elem].used)
73
#else
74
# define ASSERT_USED(pq, idx)
75
# define ASSERT_ELEM_USED(pq, elem)
76
#endif
77
78
/*
79
 * Calculate the array growth based on the target size.
80
 *
81
 * The growth factor is a rational number and is defined by a numerator
82
 * and a denominator.  According to Andrew Koenig in his paper "Why Are
83
 * Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less
84
 * than the golden ratio (1.618...).
85
 *
86
 * We use an expansion factor of 8 / 5 = 1.6
87
 */
88
static ossl_inline size_t compute_pqueue_growth(size_t target, size_t current)
89
46.6k
{
90
46.6k
    int err = 0;
91
92
93.3k
    while (current < target) {
93
46.6k
        if (current >= max_nodes)
94
0
            return 0;
95
96
46.6k
        current = safe_muldiv_size_t(current, 8, 5, &err);
97
46.6k
        if (err)
98
0
            return 0;
99
46.6k
        if (current >= max_nodes)
100
0
            current = max_nodes;
101
46.6k
    }
102
46.6k
    return current;
103
46.6k
}
104
105
static ossl_inline void pqueue_swap_elem(OSSL_PQUEUE *pq, size_t i, size_t j)
106
14.7M
{
107
14.7M
    struct pq_heap_st *h = pq->heap, t_h;
108
14.7M
    struct pq_elem_st *e = pq->elements;
109
110
14.7M
    ASSERT_USED(pq, i);
111
29.5M
    ASSERT_USED(pq, j);
112
113
14.7M
    t_h = h[i];
114
14.7M
    h[i] = h[j];
115
14.7M
    h[j] = t_h;
116
117
14.7M
    e[h[i].index].posn = i;
118
14.7M
    e[h[j].index].posn = j;
119
14.7M
}
120
121
static ossl_inline void pqueue_move_elem(OSSL_PQUEUE *pq, size_t from, size_t to)
122
2.91M
{
123
2.91M
    struct pq_heap_st *h = pq->heap;
124
2.91M
    struct pq_elem_st *e = pq->elements;
125
126
2.91M
    ASSERT_USED(pq, from);
127
128
2.91M
    h[to] = h[from];
129
2.91M
    e[h[to].index].posn = to;
130
2.91M
}
131
132
/*
133
 * Force the specified element to the front of the heap.  This breaks
134
 * the heap partial ordering pre-condition.
135
 */
136
static ossl_inline void pqueue_force_bottom(OSSL_PQUEUE *pq, size_t n)
137
0
{
138
0
    ASSERT_USED(pq, n);
139
0
    while (n > 0) {
140
0
        const size_t p = (n - 1) / 2;
141
142
0
        ASSERT_USED(pq, p);
143
0
        pqueue_swap_elem(pq, n, p);
144
0
        n = p;
145
0
    }
146
0
}
147
148
/*
149
 * Move an element down to its correct position to restore the partial
150
 * order pre-condition.
151
 */
152
static ossl_inline void pqueue_move_down(OSSL_PQUEUE *pq, size_t n)
153
3.45M
{
154
3.45M
    struct pq_heap_st *h = pq->heap;
155
156
3.45M
    ASSERT_USED(pq, n);
157
4.73M
    while (n > 0) {
158
4.14M
        const size_t p = (n - 1) / 2;
159
160
4.14M
        ASSERT_USED(pq, p);
161
4.14M
        if (pq->compare(h[n].data, h[p].data) >= 0)
162
2.86M
            break;
163
1.27M
        pqueue_swap_elem(pq, n, p);
164
1.27M
        n = p;
165
1.27M
    }
166
3.45M
}
167
168
/*
169
 * Move an element up to its correct position to restore the partial
170
 * order pre-condition.
171
 */
172
static ossl_inline void pqueue_move_up(OSSL_PQUEUE *pq, size_t n)
173
2.91M
{
174
2.91M
    struct pq_heap_st *h = pq->heap;
175
2.91M
    size_t p = n * 2 + 1;
176
177
2.91M
    ASSERT_USED(pq, n);
178
2.91M
    if (pq->htop > p + 1) {
179
2.76M
        ASSERT_USED(pq, p);
180
5.52M
        ASSERT_USED(pq, p + 1);
181
2.76M
        if (pq->compare(h[p].data, h[p + 1].data) > 0)
182
562k
            p++;
183
2.76M
    }
184
16.3M
    while (pq->htop > p && pq->compare(h[p].data, h[n].data) < 0) {
185
13.4M
        ASSERT_USED(pq, p);
186
13.4M
        pqueue_swap_elem(pq, n, p);
187
13.4M
        n = p;
188
13.4M
        p = n * 2 + 1;
189
13.4M
        if (pq->htop > p + 1) {
190
12.4M
            ASSERT_USED(pq, p + 1);
191
12.4M
            if (pq->compare(h[p].data, h[p + 1].data) > 0)
192
5.77M
                p++;
193
12.4M
        }
194
13.4M
    }
195
2.91M
}
196
197
int ossl_pqueue_push(OSSL_PQUEUE *pq, void *data, size_t *elem)
198
3.45M
{
199
3.45M
    size_t n, m;
200
201
3.45M
    if (!ossl_pqueue_reserve(pq, 1))
202
0
        return 0;
203
204
3.45M
    n = pq->htop++;
205
3.45M
    m = pq->freelist;
206
3.45M
    pq->freelist = pq->elements[m].posn;
207
208
3.45M
    pq->heap[n].data = data;
209
3.45M
    pq->heap[n].index = m;
210
211
3.45M
    pq->elements[m].posn = n;
212
3.45M
#ifndef NDEBUG
213
3.45M
    pq->elements[m].used = 1;
214
3.45M
#endif
215
3.45M
    pqueue_move_down(pq, n);
216
3.45M
    if (elem != NULL)
217
3.45M
        *elem = m;
218
3.45M
    return 1;
219
3.45M
}
220
221
void *ossl_pqueue_peek(const OSSL_PQUEUE *pq)
222
10.1M
{
223
10.1M
    if (pq->htop > 0) {
224
2.48M
        ASSERT_USED(pq, 0);
225
2.48M
        return pq->heap->data;
226
2.48M
    }
227
7.61M
    return NULL;
228
10.1M
}
229
230
void *ossl_pqueue_pop(OSSL_PQUEUE *pq)
231
5.51M
{
232
5.51M
    void *res;
233
5.51M
    size_t elem;
234
235
5.51M
    if (pq == NULL || pq->htop == 0)
236
2.55M
        return NULL;
237
238
5.91M
    ASSERT_USED(pq, 0);
239
2.95M
    res = pq->heap->data;
240
2.95M
    elem = pq->heap->index;
241
242
2.95M
    if (--pq->htop != 0) {
243
2.91M
        pqueue_move_elem(pq, pq->htop, 0);
244
2.91M
        pqueue_move_up(pq, 0);
245
2.91M
    }
246
247
2.95M
    pq->elements[elem].posn = pq->freelist;
248
2.95M
    pq->freelist = elem;
249
2.95M
#ifndef NDEBUG
250
2.95M
    pq->elements[elem].used = 0;
251
2.95M
#endif
252
2.95M
    return res;
253
5.91M
}
254
255
void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem)
256
2.26M
{
257
2.26M
    size_t n;
258
259
2.26M
    if (pq == NULL || elem >= pq->hmax || pq->htop == 0)
260
0
        return 0;
261
262
2.26M
    ASSERT_ELEM_USED(pq, elem);
263
2.26M
    n = pq->elements[elem].posn;
264
265
2.26M
    ASSERT_USED(pq, n);
266
267
2.26M
    if (n == pq->htop - 1) {
268
496k
        pq->elements[elem].posn = pq->freelist;
269
496k
        pq->freelist = elem;
270
496k
#ifndef NDEBUG
271
496k
        pq->elements[elem].used = 0;
272
496k
#endif
273
496k
        return pq->heap[--pq->htop].data;
274
496k
    }
275
1.76M
    if (n > 0)
276
0
        pqueue_force_bottom(pq, n);
277
1.76M
    return ossl_pqueue_pop(pq);
278
2.26M
}
279
280
static void pqueue_add_freelist(OSSL_PQUEUE *pq, size_t from)
281
2.60M
{
282
2.60M
    struct pq_elem_st *e = pq->elements;
283
2.60M
    size_t i;
284
285
2.60M
#ifndef NDEBUG
286
26.0M
    for (i = from; i < pq->hmax; i++)
287
23.4M
        e[i].used = 0;
288
2.60M
#endif
289
2.60M
    e[from].posn = pq->freelist;
290
23.4M
    for (i = from + 1; i < pq->hmax; i++)
291
20.8M
        e[i].posn = i - 1;
292
2.60M
    pq->freelist = pq->hmax - 1;
293
2.60M
}
294
295
int ossl_pqueue_reserve(OSSL_PQUEUE *pq, size_t n)
296
3.45M
{
297
3.45M
    size_t new_max, cur_max;
298
3.45M
    struct pq_heap_st *h;
299
3.45M
    struct pq_elem_st *e;
300
301
3.45M
    if (pq == NULL)
302
0
        return 0;
303
3.45M
    cur_max = pq->hmax;
304
3.45M
    if (pq->htop + n < cur_max)
305
3.40M
        return 1;
306
307
46.6k
    new_max = compute_pqueue_growth(n + cur_max, cur_max);
308
46.6k
    if (new_max == 0) {
309
0
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
310
0
        return 0;
311
0
    }
312
313
46.6k
    h = OPENSSL_realloc(pq->heap, new_max * sizeof(*pq->heap));
314
46.6k
    if (h == NULL)
315
0
        return 0;
316
46.6k
    pq->heap = h;
317
318
46.6k
    e = OPENSSL_realloc(pq->elements, new_max * sizeof(*pq->elements));
319
46.6k
    if (e == NULL)
320
0
        return 0;
321
46.6k
    pq->elements = e;
322
323
46.6k
    pq->hmax = new_max;
324
46.6k
    pqueue_add_freelist(pq, cur_max);
325
46.6k
    return 1;
326
46.6k
}
327
328
OSSL_PQUEUE *ossl_pqueue_new(int (*compare)(const void *, const void *))
329
2.55M
{
330
2.55M
    OSSL_PQUEUE *pq;
331
332
2.55M
    if (compare == NULL)
333
0
        return NULL;
334
335
2.55M
    pq = OPENSSL_malloc(sizeof(*pq));
336
2.55M
    if (pq == NULL)
337
0
        return NULL;
338
2.55M
    pq->compare = compare;
339
2.55M
    pq->hmax = min_nodes;
340
2.55M
    pq->htop = 0;
341
2.55M
    pq->freelist = 0;
342
2.55M
    pq->heap = OPENSSL_malloc(sizeof(*pq->heap) * min_nodes);
343
2.55M
    pq->elements = OPENSSL_malloc(sizeof(*pq->elements) * min_nodes);
344
2.55M
    if (pq->heap == NULL || pq->elements == NULL) {
345
0
        ossl_pqueue_free(pq);
346
0
        return NULL;
347
0
    }
348
2.55M
    pqueue_add_freelist(pq, 0);
349
2.55M
    return pq;
350
2.55M
}
351
352
void ossl_pqueue_free(OSSL_PQUEUE *pq)
353
2.55M
{
354
2.55M
    if (pq != NULL) {
355
2.55M
        OPENSSL_free(pq->heap);
356
2.55M
        OPENSSL_free(pq->elements);
357
2.55M
        OPENSSL_free(pq);
358
2.55M
    }
359
2.55M
}
360
361
void ossl_pqueue_pop_free(OSSL_PQUEUE *pq, void (*freefunc)(void *))
362
0
{
363
0
    size_t i;
364
365
0
    if (pq != NULL) {
366
0
        for (i = 0; i < pq->htop; i++)
367
0
            (*freefunc)(pq->heap[i].data);
368
0
        ossl_pqueue_free(pq);
369
0
    }
370
0
}
371
372
size_t ossl_pqueue_num(const OSSL_PQUEUE *pq)
373
4.68M
{
374
4.68M
    return pq != NULL ? pq->htop : 0;
375
4.68M
}