quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_slist.c (11239B)


      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 }