1 /* MIT License
2 *
3 * Copyright (c) 2023 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_private.h"
27 #include "ares_slist.h"
28
29 /* SkipList implementation */
30
31 #define ARES__SLIST_START_LEVELS 4
32
33 struct ares_slist {
34 ares_rand_state *rand_state;
35 unsigned char rand_data[8];
36 size_t rand_bits;
37
38 ares_slist_node_t **head;
39 size_t levels;
40 ares_slist_node_t *tail;
41
42 ares_slist_cmp_t cmp;
43 ares_slist_destructor_t destruct;
44 size_t cnt;
45 };
46
47 struct ares_slist_node {
48 void *data;
49 ares_slist_node_t **prev;
50 ares_slist_node_t **next;
51 size_t levels;
52 ares_slist_t *parent;
53 };
54
ares_slist_create(ares_rand_state * rand_state,ares_slist_cmp_t cmp,ares_slist_destructor_t destruct)55 ares_slist_t *ares_slist_create(ares_rand_state *rand_state,
56 ares_slist_cmp_t cmp,
57 ares_slist_destructor_t destruct)
58 {
59 ares_slist_t *list;
60
61 if (rand_state == NULL || cmp == NULL) {
62 return NULL;
63 }
64
65 list = ares_malloc_zero(sizeof(*list));
66
67 if (list == NULL) {
68 return NULL;
69 }
70
71 list->rand_state = rand_state;
72 list->cmp = cmp;
73 list->destruct = destruct;
74
75 list->levels = ARES__SLIST_START_LEVELS;
76 list->head = ares_malloc_zero(sizeof(*list->head) * list->levels);
77 if (list->head == NULL) {
78 ares_free(list);
79 return NULL;
80 }
81
82 return list;
83 }
84
ares_slist_coin_flip(ares_slist_t * list)85 static ares_bool_t ares_slist_coin_flip(ares_slist_t *list)
86 {
87 size_t total_bits = sizeof(list->rand_data) * 8;
88 size_t bit;
89
90 /* Refill random data used for coin flips. We pull this in 8 byte chunks.
91 * ares_rand_bytes() has some built-in caching of its own so we don't need
92 * to be excessive in caching ourselves. Prefer to require less memory per
93 * skiplist */
94 if (list->rand_bits == 0) {
95 ares_rand_bytes(list->rand_state, list->rand_data, sizeof(list->rand_data));
96 list->rand_bits = total_bits;
97 }
98
99 bit = total_bits - list->rand_bits;
100 list->rand_bits--;
101
102 return (list->rand_data[bit / 8] & (1 << (bit % 8))) ? ARES_TRUE : ARES_FALSE;
103 }
104
ares_slist_replace_destructor(ares_slist_t * list,ares_slist_destructor_t destruct)105 void ares_slist_replace_destructor(ares_slist_t *list,
106 ares_slist_destructor_t destruct)
107 {
108 if (list == NULL) {
109 return;
110 }
111
112 list->destruct = destruct;
113 }
114
ares_slist_max_level(const ares_slist_t * list)115 static size_t ares_slist_max_level(const ares_slist_t *list)
116 {
117 size_t max_level = 0;
118
119 if (list->cnt + 1 <= (1 << ARES__SLIST_START_LEVELS)) {
120 max_level = ARES__SLIST_START_LEVELS;
121 } else {
122 max_level = ares_log2(ares_round_up_pow2(list->cnt + 1));
123 }
124
125 if (list->levels > max_level) {
126 max_level = list->levels;
127 }
128
129 return max_level;
130 }
131
ares_slist_calc_level(ares_slist_t * list)132 static size_t ares_slist_calc_level(ares_slist_t *list)
133 {
134 size_t max_level = ares_slist_max_level(list);
135 size_t level;
136
137 for (level = 1; ares_slist_coin_flip(list) && level < max_level; level++)
138 ;
139
140 return level;
141 }
142
ares_slist_node_push(ares_slist_t * list,ares_slist_node_t * node)143 static void ares_slist_node_push(ares_slist_t *list, ares_slist_node_t *node)
144 {
145 size_t i;
146 ares_slist_node_t *left = NULL;
147
148 /* Scan from highest level in the slist, even if we're not using that number
149 * of levels for this entry as this is what makes it O(log n) */
150 for (i = list->levels; i-- > 0;) {
151 /* set left if left is NULL and the current node value is greater than the
152 * head at this level */
153 if (left == NULL && list->head[i] != NULL &&
154 list->cmp(node->data, list->head[i]->data) > 0) {
155 left = list->head[i];
156 }
157
158 if (left != NULL) {
159 /* scan forward to find our insertion point */
160 while (left->next[i] != NULL &&
161 list->cmp(node->data, left->next[i]->data) > 0) {
162 left = left->next[i];
163 }
164 }
165
166 /* search only as we didn't randomly select this number of levels */
167 if (i >= node->levels) {
168 continue;
169 }
170
171 if (left == NULL) {
172 /* head insertion */
173 node->next[i] = list->head[i];
174 node->prev[i] = NULL;
175 list->head[i] = node;
176 } else {
177 /* Chain */
178 node->next[i] = left->next[i];
179 node->prev[i] = left;
180 left->next[i] = node;
181 }
182
183 if (node->next[i] != NULL) {
184 /* chain prev */
185 node->next[i]->prev[i] = node;
186 } else {
187 if (i == 0) {
188 /* update tail */
189 list->tail = node;
190 }
191 }
192 }
193 }
194
ares_slist_insert(ares_slist_t * list,void * val)195 ares_slist_node_t *ares_slist_insert(ares_slist_t *list, void *val)
196 {
197 ares_slist_node_t *node = NULL;
198
199 if (list == NULL || val == NULL) {
200 return NULL;
201 }
202
203 node = ares_malloc_zero(sizeof(*node));
204
205 if (node == NULL) {
206 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
207 }
208
209 node->data = val;
210 node->parent = list;
211
212 /* Randomly determine the number of levels we want to use */
213 node->levels = ares_slist_calc_level(list);
214
215 /* Allocate array of next and prev nodes for linking each level */
216 node->next = ares_malloc_zero(sizeof(*node->next) * node->levels);
217 if (node->next == NULL) {
218 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
219 }
220
221 node->prev = ares_malloc_zero(sizeof(*node->prev) * node->levels);
222 if (node->prev == NULL) {
223 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
224 }
225
226 /* If the number of levels is greater than we currently support in the slist,
227 * increase the count */
228 if (list->levels < node->levels) {
229 void *ptr =
230 ares_realloc_zero(list->head, sizeof(*list->head) * list->levels,
231 sizeof(*list->head) * node->levels);
232 if (ptr == NULL) {
233 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
234 }
235
236 list->head = ptr;
237 list->levels = node->levels;
238 }
239
240 ares_slist_node_push(list, node);
241
242 list->cnt++;
243
244 return node;
245
246 /* LCOV_EXCL_START: OutOfMemory */
247 fail:
248 if (node) {
249 ares_free(node->prev);
250 ares_free(node->next);
251 ares_free(node);
252 }
253 return NULL;
254 /* LCOV_EXCL_STOP */
255 }
256
ares_slist_node_pop(ares_slist_node_t * node)257 static void ares_slist_node_pop(ares_slist_node_t *node)
258 {
259 ares_slist_t *list = node->parent;
260 size_t i;
261
262 /* relink each node at each level */
263 for (i = node->levels; i-- > 0;) {
264 if (node->next[i] == NULL) {
265 if (i == 0) {
266 list->tail = node->prev[0];
267 }
268 } else {
269 node->next[i]->prev[i] = node->prev[i];
270 }
271
272 if (node->prev[i] == NULL) {
273 list->head[i] = node->next[i];
274 } else {
275 node->prev[i]->next[i] = node->next[i];
276 }
277 }
278
279 memset(node->next, 0, sizeof(*node->next) * node->levels);
280 memset(node->prev, 0, sizeof(*node->prev) * node->levels);
281 }
282
ares_slist_node_claim(ares_slist_node_t * node)283 void *ares_slist_node_claim(ares_slist_node_t *node)
284 {
285 ares_slist_t *list;
286 void *val;
287
288 if (node == NULL) {
289 return NULL;
290 }
291
292 list = node->parent;
293 val = node->data;
294
295 ares_slist_node_pop(node);
296
297 ares_free(node->next);
298 ares_free(node->prev);
299 ares_free(node);
300
301 list->cnt--;
302
303 return val;
304 }
305
ares_slist_node_reinsert(ares_slist_node_t * node)306 void ares_slist_node_reinsert(ares_slist_node_t *node)
307 {
308 ares_slist_t *list;
309
310 if (node == NULL) {
311 return;
312 }
313
314 list = node->parent;
315
316 ares_slist_node_pop(node);
317 ares_slist_node_push(list, node);
318 }
319
ares_slist_node_find(const ares_slist_t * list,const void * val)320 ares_slist_node_t *ares_slist_node_find(const ares_slist_t *list,
321 const void *val)
322 {
323 size_t i;
324 ares_slist_node_t *node = NULL;
325 int rv = -1;
326
327 if (list == NULL || val == NULL) {
328 return NULL;
329 }
330
331 /* Scan nodes starting at the highest level. For each level scan forward
332 * until the value is between the prior and next node, or if equal quit
333 * as we found a match */
334 for (i = list->levels; i-- > 0;) {
335 if (node == NULL) {
336 node = list->head[i];
337 }
338
339 if (node == NULL) {
340 continue;
341 }
342
343 do {
344 rv = list->cmp(val, node->data);
345
346 if (rv < 0) {
347 /* back off, our value is greater than current node reference */
348 node = node->prev[i];
349 } else if (rv > 0) {
350 /* move forward and try again. if it goes past, it will loop again and
351 * go to previous entry */
352 node = node->next[i];
353 }
354
355 /* rv == 0 will terminate loop */
356
357 } while (node != NULL && rv > 0);
358
359 /* Found a match, no need to continue */
360 if (rv == 0) {
361 break;
362 }
363 }
364
365 /* no match */
366 if (rv != 0) {
367 return NULL;
368 }
369
370 /* The list may have multiple entries that match. They're guaranteed to be
371 * in order, but we're not guaranteed to have selected the _first_ matching
372 * node. Lets scan backwards to find the first match */
373 while (node->prev[0] != NULL && list->cmp(node->prev[0]->data, val) == 0) {
374 node = node->prev[0];
375 }
376
377 return node;
378 }
379
ares_slist_node_first(const ares_slist_t * list)380 ares_slist_node_t *ares_slist_node_first(const ares_slist_t *list)
381 {
382 if (list == NULL) {
383 return NULL;
384 }
385
386 return list->head[0];
387 }
388
ares_slist_node_last(const ares_slist_t * list)389 ares_slist_node_t *ares_slist_node_last(const ares_slist_t *list)
390 {
391 if (list == NULL) {
392 return NULL;
393 }
394 return list->tail;
395 }
396
ares_slist_node_next(const ares_slist_node_t * node)397 ares_slist_node_t *ares_slist_node_next(const ares_slist_node_t *node)
398 {
399 if (node == NULL) {
400 return NULL;
401 }
402 return node->next[0];
403 }
404
ares_slist_node_prev(const ares_slist_node_t * node)405 ares_slist_node_t *ares_slist_node_prev(const ares_slist_node_t *node)
406 {
407 if (node == NULL) {
408 return NULL;
409 }
410 return node->prev[0];
411 }
412
ares_slist_node_val(ares_slist_node_t * node)413 void *ares_slist_node_val(ares_slist_node_t *node)
414 {
415 if (node == NULL) {
416 return NULL;
417 }
418
419 return node->data;
420 }
421
ares_slist_len(const ares_slist_t * list)422 size_t ares_slist_len(const ares_slist_t *list)
423 {
424 if (list == NULL) {
425 return 0;
426 }
427 return list->cnt;
428 }
429
ares_slist_node_parent(ares_slist_node_t * node)430 ares_slist_t *ares_slist_node_parent(ares_slist_node_t *node)
431 {
432 if (node == NULL) {
433 return NULL;
434 }
435 return node->parent;
436 }
437
ares_slist_first_val(const ares_slist_t * list)438 void *ares_slist_first_val(const ares_slist_t *list)
439 {
440 return ares_slist_node_val(ares_slist_node_first(list));
441 }
442
ares_slist_last_val(const ares_slist_t * list)443 void *ares_slist_last_val(const ares_slist_t *list)
444 {
445 return ares_slist_node_val(ares_slist_node_last(list));
446 }
447
ares_slist_node_destroy(ares_slist_node_t * node)448 void ares_slist_node_destroy(ares_slist_node_t *node)
449 {
450 ares_slist_destructor_t destruct;
451 void *val;
452
453 if (node == NULL) {
454 return;
455 }
456
457 destruct = node->parent->destruct;
458 val = ares_slist_node_claim(node);
459
460 if (val != NULL && destruct != NULL) {
461 destruct(val);
462 }
463 }
464
ares_slist_destroy(ares_slist_t * list)465 void ares_slist_destroy(ares_slist_t *list)
466 {
467 ares_slist_node_t *node;
468
469 if (list == NULL) {
470 return;
471 }
472
473 while ((node = ares_slist_node_first(list)) != NULL) {
474 ares_slist_node_destroy(node);
475 }
476
477 ares_free(list->head);
478 ares_free(list);
479 }
480