]> git.ipfire.org Git - thirdparty/openssl.git/blame - ssl/quic/uint_set.c
Copyright year updates
[thirdparty/openssl.git] / ssl / quic / uint_set.c
CommitLineData
83022590 1/*
da1c088f 2 * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
83022590
HL
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 "internal/uint_set.h"
11#include "internal/common.h"
12#include <assert.h>
13
14/*
15 * uint64_t Integer Sets
16 * =====================
17 *
18 * This data structure supports the following operations:
19 *
20 * Insert Range: Adds an inclusive range of integers [start, end]
21 * to the set. Equivalent to Insert for each number
22 * in the range.
23 *
24 * Remove Range: Removes an inclusive range of integers [start, end]
25 * from the set. Not all of the range need already be in
26 * the set, but any part of the range in the set is removed.
27 *
28 * Query: Is an integer in the data structure?
29 *
30 * The data structure can be iterated.
31 *
32 * For greater efficiency in tracking large numbers of contiguous integers, we
33 * track integer ranges rather than individual integers. The data structure
34 * manages a list of integer ranges [[start, end]...]. Internally this is
35 * implemented as a doubly linked sorted list of range structures, which are
36 * automatically split and merged as necessary.
37 *
38 * This data structure requires O(n) traversal of the list for insertion,
39 * removal and query when we are not adding/removing ranges which are near the
40 * beginning or end of the set of ranges. For the applications for which this
41 * data structure is used (e.g. QUIC PN tracking for ACK generation), it is
42 * expected that the number of integer ranges needed at any given time will
43 * generally be small and that most operations will be close to the beginning or
44 * end of the range.
45 *
46 * Invariant: The data structure is always sorted in ascending order by value.
47 *
48 * Invariant: No two adjacent ranges ever 'border' one another (have no
49 * numerical gap between them) as the data structure always ensures
50 * such ranges are merged.
51 *
52 * Invariant: No two ranges ever overlap.
53 *
54 * Invariant: No range [a, b] ever has a > b.
55 *
56 * Invariant: Since ranges are represented using inclusive bounds, no range
57 * item inside the data structure can represent a span of zero
58 * integers.
59 */
60void ossl_uint_set_init(UINT_SET *s)
61{
c5ca7180 62 ossl_list_uint_set_init(s);
83022590
HL
63}
64
65void ossl_uint_set_destroy(UINT_SET *s)
66{
67 UINT_SET_ITEM *x, *xnext;
68
c5ca7180
P
69 for (x = ossl_list_uint_set_head(s); x != NULL; x = xnext) {
70 xnext = ossl_list_uint_set_next(x);
83022590
HL
71 OPENSSL_free(x);
72 }
73}
74
c5ca7180 75/* Possible merge of x, prev(x) */
83022590
HL
76static void uint_set_merge_adjacent(UINT_SET *s, UINT_SET_ITEM *x)
77{
c5ca7180 78 UINT_SET_ITEM *xprev = ossl_list_uint_set_prev(x);
83022590
HL
79
80 if (xprev == NULL)
81 return;
82
83 if (x->range.start - 1 != xprev->range.end)
84 return;
85
86 x->range.start = xprev->range.start;
c5ca7180 87 ossl_list_uint_set_remove(s, xprev);
83022590 88 OPENSSL_free(xprev);
83022590
HL
89}
90
91static uint64_t u64_min(uint64_t x, uint64_t y)
92{
93 return x < y ? x : y;
94}
95
96static uint64_t u64_max(uint64_t x, uint64_t y)
97{
98 return x > y ? x : y;
99}
100
101/*
102 * Returns 1 if there exists an integer x which falls within both ranges a and
103 * b.
104 */
105static int uint_range_overlaps(const UINT_RANGE *a,
106 const UINT_RANGE *b)
107{
108 return u64_min(a->end, b->end)
109 >= u64_max(a->start, b->start);
110}
111
c5ca7180
P
112static UINT_SET_ITEM *create_set_item(uint64_t start, uint64_t end)
113{
8761efb2
HL
114 UINT_SET_ITEM *x = OPENSSL_malloc(sizeof(UINT_SET_ITEM));
115
116 if (x == NULL)
117 return NULL;
118
119 ossl_list_uint_set_init_elem(x);
120 x->range.start = start;
121 x->range.end = end;
122 return x;
c5ca7180
P
123}
124
83022590
HL
125int ossl_uint_set_insert(UINT_SET *s, const UINT_RANGE *range)
126{
c5ca7180 127 UINT_SET_ITEM *x, *xnext, *z, *zprev, *f;
83022590
HL
128 uint64_t start = range->start, end = range->end;
129
130 if (!ossl_assert(start <= end))
131 return 0;
132
c5ca7180 133 if (ossl_list_uint_set_is_empty(s)) {
83022590 134 /* Nothing in the set yet, so just add this range. */
c5ca7180 135 x = create_set_item(start, end);
83022590
HL
136 if (x == NULL)
137 return 0;
c5ca7180 138 ossl_list_uint_set_insert_head(s, x);
83022590
HL
139 return 1;
140 }
141
c5ca7180
P
142 z = ossl_list_uint_set_tail(s);
143 if (start > z->range.end) {
83022590
HL
144 /*
145 * Range is after the latest range in the set, so append.
146 *
147 * Note: The case where the range is before the earliest range in the
148 * set is handled as a degenerate case of the final case below. See
149 * optimization note (*) below.
150 */
c5ca7180
P
151 if (z->range.end + 1 == start) {
152 z->range.end = end;
83022590
HL
153 return 1;
154 }
155
c5ca7180 156 x = create_set_item(start, end);
83022590
HL
157 if (x == NULL)
158 return 0;
c5ca7180 159 ossl_list_uint_set_insert_tail(s, x);
83022590
HL
160 return 1;
161 }
162
c5ca7180
P
163 f = ossl_list_uint_set_head(s);
164 if (start <= f->range.start && end >= z->range.end) {
83022590
HL
165 /*
166 * New range dwarfs all ranges in our set.
167 *
168 * Free everything except the first range in the set, which we scavenge
169 * and reuse.
170 */
c5ca7180
P
171 x = ossl_list_uint_set_head(s);
172 x->range.start = start;
173 x->range.end = end;
174 for (x = ossl_list_uint_set_next(x); x != NULL; x = xnext) {
175 xnext = ossl_list_uint_set_next(x);
176 ossl_list_uint_set_remove(s, x);
83022590 177 }
83022590
HL
178 return 1;
179 }
180
181 /*
182 * Walk backwards since we will most often be inserting at the end. As an
183 * optimization, test the head node first and skip iterating over the
184 * entire list if we are inserting at the start. The assumption is that
185 * insertion at the start and end of the space will be the most common
186 * operations. (*)
187 */
c5ca7180
P
188 z = end < f->range.start ? f : z;
189
190 for (; z != NULL; z = zprev) {
191 zprev = ossl_list_uint_set_prev(z);
83022590 192
83022590
HL
193 /* An existing range dwarfs our new range (optimisation). */
194 if (z->range.start <= start && z->range.end >= end)
195 return 1;
196
197 if (uint_range_overlaps(&z->range, range)) {
198 /*
199 * Our new range overlaps an existing range, or possibly several
200 * existing ranges.
201 */
202 UINT_SET_ITEM *ovend = z;
83022590 203
c5ca7180 204 ovend->range.end = u64_max(end, z->range.end);
83022590
HL
205
206 /* Get earliest overlapping range. */
c5ca7180
P
207 while (zprev != NULL && uint_range_overlaps(&zprev->range, range)) {
208 z = zprev;
209 zprev = ossl_list_uint_set_prev(z);
83022590
HL
210 }
211
c5ca7180
P
212 ovend->range.start = u64_min(start, z->range.start);
213
214 /* Replace sequence of nodes z..ovend with updated ovend only. */
215 while (z != ovend) {
216 z = ossl_list_uint_set_next(x = z);
217 ossl_list_uint_set_remove(s, x);
218 OPENSSL_free(x);
219 }
83022590
HL
220 break;
221 } else if (end < z->range.start
c5ca7180 222 && (zprev == NULL || start > zprev->range.end)) {
83022590
HL
223 if (z->range.start == end + 1) {
224 /* We can extend the following range backwards. */
225 z->range.start = start;
226
227 /*
228 * If this closes a gap we now need to merge
229 * consecutive nodes.
230 */
231 uint_set_merge_adjacent(s, z);
c5ca7180 232 } else if (zprev != NULL && zprev->range.end + 1 == start) {
83022590 233 /* We can extend the preceding range forwards. */
c5ca7180 234 zprev->range.end = end;
83022590
HL
235
236 /*
237 * If this closes a gap we now need to merge
238 * consecutive nodes.
239 */
240 uint_set_merge_adjacent(s, z);
241 } else {
242 /*
243 * The new interval is between intervals without overlapping or
244 * touching them, so insert between, preserving sort.
245 */
c5ca7180 246 x = create_set_item(start, end);
83022590
HL
247 if (x == NULL)
248 return 0;
c5ca7180 249 ossl_list_uint_set_insert_before(s, z, x);
83022590
HL
250 }
251 break;
252 }
253 }
254
255 return 1;
256}
257
258int ossl_uint_set_remove(UINT_SET *s, const UINT_RANGE *range)
259{
260 UINT_SET_ITEM *z, *zprev, *y;
261 uint64_t start = range->start, end = range->end;
262
263 if (!ossl_assert(start <= end))
264 return 0;
265
266 /* Walk backwards since we will most often be removing at the end. */
c5ca7180
P
267 for (z = ossl_list_uint_set_tail(s); z != NULL; z = zprev) {
268 zprev = ossl_list_uint_set_prev(z);
83022590
HL
269
270 if (start > z->range.end)
271 /* No overlapping ranges can exist beyond this point, so stop. */
272 break;
273
274 if (start <= z->range.start && end >= z->range.end) {
275 /*
276 * The range being removed dwarfs this range, so it should be
277 * removed.
278 */
c5ca7180 279 ossl_list_uint_set_remove(s, z);
83022590 280 OPENSSL_free(z);
dc5e5c51 281 } else if (start <= z->range.start && end >= z->range.start) {
83022590
HL
282 /*
283 * The range being removed includes start of this range, but does
284 * not cover the entire range (as this would be caught by the case
285 * above). Shorten the range.
286 */
287 assert(end < z->range.end);
288 z->range.start = end + 1;
289 } else if (end >= z->range.end) {
290 /*
291 * The range being removed includes the end of this range, but does
292 * not cover the entire range (as this would be caught by the case
293 * above). Shorten the range. We can also stop iterating.
294 */
295 assert(start > z->range.start);
296 assert(start > 0);
297 z->range.end = start - 1;
298 break;
299 } else if (start > z->range.start && end < z->range.end) {
300 /*
301 * The range being removed falls entirely in this range, so cut it
302 * into two. Cases where a zero-length range would be created are
303 * handled by the above cases.
304 */
c5ca7180
P
305 y = create_set_item(end + 1, z->range.end);
306 ossl_list_uint_set_insert_after(s, z, y);
dc5e5c51 307 z->range.end = start - 1;
83022590
HL
308 break;
309 } else {
310 /* Assert no partial overlap; all cases should be covered above. */
311 assert(!uint_range_overlaps(&z->range, range));
312 }
313 }
314
dc5e5c51 315 return 1;
83022590
HL
316}
317
318int ossl_uint_set_query(const UINT_SET *s, uint64_t v)
319{
320 UINT_SET_ITEM *x;
321
c5ca7180 322 if (ossl_list_uint_set_is_empty(s))
83022590
HL
323 return 0;
324
c5ca7180 325 for (x = ossl_list_uint_set_tail(s); x != NULL; x = ossl_list_uint_set_prev(x))
83022590
HL
326 if (x->range.start <= v && x->range.end >= v)
327 return 1;
328 else if (x->range.end < v)
329 return 0;
330
331 return 0;
332}