• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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