Coverage Report

Created: 2025-12-31 06:58

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