quickjs-tart

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

ares_array.c (9741B)


      1 /* MIT License
      2  *
      3  * Copyright (c) 2024 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_array.h"
     28 
     29 #define ARES__ARRAY_MIN 4
     30 
     31 struct ares_array {
     32   ares_array_destructor_t destruct;
     33   void                   *arr;
     34   size_t                  member_size;
     35   size_t                  cnt;
     36   size_t                  offset;
     37   size_t                  alloc_cnt;
     38 };
     39 
     40 ares_array_t *ares_array_create(size_t                  member_size,
     41                                 ares_array_destructor_t destruct)
     42 {
     43   ares_array_t *arr;
     44 
     45   if (member_size == 0) {
     46     return NULL;
     47   }
     48 
     49   arr = ares_malloc_zero(sizeof(*arr));
     50   if (arr == NULL) {
     51     return NULL;
     52   }
     53 
     54   arr->member_size = member_size;
     55   arr->destruct    = destruct;
     56   return arr;
     57 }
     58 
     59 size_t ares_array_len(const ares_array_t *arr)
     60 {
     61   if (arr == NULL) {
     62     return 0;
     63   }
     64   return arr->cnt;
     65 }
     66 
     67 void *ares_array_at(ares_array_t *arr, size_t idx)
     68 {
     69   if (arr == NULL || idx >= arr->cnt) {
     70     return NULL;
     71   }
     72   return (unsigned char *)arr->arr + ((idx + arr->offset) * arr->member_size);
     73 }
     74 
     75 const void *ares_array_at_const(const ares_array_t *arr, size_t idx)
     76 {
     77   if (arr == NULL || idx >= arr->cnt) {
     78     return NULL;
     79   }
     80   return (unsigned char *)arr->arr + ((idx + arr->offset) * arr->member_size);
     81 }
     82 
     83 ares_status_t ares_array_sort(ares_array_t *arr, ares_array_cmp_t cmp)
     84 {
     85   if (arr == NULL || cmp == NULL) {
     86     return ARES_EFORMERR;
     87   }
     88 
     89   /* Nothing to sort */
     90   if (arr->cnt < 2) {
     91     return ARES_SUCCESS;
     92   }
     93 
     94   qsort((unsigned char *)arr->arr + (arr->offset * arr->member_size), arr->cnt,
     95         arr->member_size, cmp);
     96   return ARES_SUCCESS;
     97 }
     98 
     99 void ares_array_destroy(ares_array_t *arr)
    100 {
    101   size_t i;
    102 
    103   if (arr == NULL) {
    104     return;
    105   }
    106 
    107   if (arr->destruct != NULL) {
    108     for (i = 0; i < arr->cnt; i++) {
    109       arr->destruct(ares_array_at(arr, i));
    110     }
    111   }
    112 
    113   ares_free(arr->arr);
    114   ares_free(arr);
    115 }
    116 
    117 /* NOTE: this function operates on actual indexes, NOT indexes using the
    118  *       arr->offset */
    119 static ares_status_t ares_array_move(ares_array_t *arr, size_t dest_idx,
    120                                      size_t src_idx)
    121 {
    122   void       *dest_ptr;
    123   const void *src_ptr;
    124   size_t      nmembers;
    125 
    126   if (arr == NULL || dest_idx >= arr->alloc_cnt || src_idx >= arr->alloc_cnt) {
    127     return ARES_EFORMERR;
    128   }
    129 
    130   /* Nothing to do */
    131   if (dest_idx == src_idx) {
    132     return ARES_SUCCESS;
    133   }
    134 
    135   dest_ptr = (unsigned char *)arr->arr + (dest_idx * arr->member_size);
    136   src_ptr  = (unsigned char *)arr->arr + (src_idx * arr->member_size);
    137 
    138   /* Check to make sure shifting to the right won't overflow our allocation
    139    * boundary */
    140   if (dest_idx > src_idx && arr->cnt + (dest_idx - src_idx) > arr->alloc_cnt) {
    141     return ARES_EFORMERR;
    142   }
    143 
    144   nmembers = arr->cnt - (src_idx - arr->offset);
    145   memmove(dest_ptr, src_ptr, nmembers * arr->member_size);
    146 
    147   return ARES_SUCCESS;
    148 }
    149 
    150 void *ares_array_finish(ares_array_t *arr, size_t *num_members)
    151 {
    152   void *ptr;
    153 
    154   if (arr == NULL || num_members == NULL) {
    155     return NULL;
    156   }
    157 
    158   /* Make sure we move data to beginning of allocation */
    159   if (arr->offset != 0) {
    160     if (ares_array_move(arr, 0, arr->offset) != ARES_SUCCESS) {
    161       return NULL;
    162     }
    163     arr->offset = 0;
    164   }
    165 
    166   ptr          = arr->arr;
    167   *num_members = arr->cnt;
    168   ares_free(arr);
    169   return ptr;
    170 }
    171 
    172 ares_status_t ares_array_set_size(ares_array_t *arr, size_t size)
    173 {
    174   void *temp;
    175 
    176   if (arr == NULL || size == 0 || size < arr->cnt) {
    177     return ARES_EFORMERR;
    178   }
    179 
    180   /* Always operate on powers of 2 */
    181   size = ares_round_up_pow2(size);
    182 
    183   if (size < ARES__ARRAY_MIN) {
    184     size = ARES__ARRAY_MIN;
    185   }
    186 
    187   /* If our allocation size is already large enough, skip */
    188   if (size <= arr->alloc_cnt) {
    189     return ARES_SUCCESS;
    190   }
    191 
    192   temp = ares_realloc_zero(arr->arr, arr->alloc_cnt * arr->member_size,
    193                            size * arr->member_size);
    194   if (temp == NULL) {
    195     return ARES_ENOMEM;
    196   }
    197   arr->alloc_cnt = size;
    198   arr->arr       = temp;
    199   return ARES_SUCCESS;
    200 }
    201 
    202 ares_status_t ares_array_insert_at(void **elem_ptr, ares_array_t *arr,
    203                                    size_t idx)
    204 {
    205   void         *ptr;
    206   ares_status_t status;
    207 
    208   if (arr == NULL) {
    209     return ARES_EFORMERR;
    210   }
    211 
    212   /* Not >= since we are allowed to append to the end */
    213   if (idx > arr->cnt) {
    214     return ARES_EFORMERR;
    215   }
    216 
    217   /* Allocate more if needed */
    218   status = ares_array_set_size(arr, arr->cnt + 1);
    219   if (status != ARES_SUCCESS) {
    220     return status;
    221   }
    222 
    223   /* Shift if we have memory but not enough room at the end */
    224   if (arr->cnt + 1 + arr->offset > arr->alloc_cnt) {
    225     status = ares_array_move(arr, 0, arr->offset);
    226     if (status != ARES_SUCCESS) {
    227       return status;
    228     }
    229     arr->offset = 0;
    230   }
    231 
    232   /* If we're inserting anywhere other than the end, we need to move some
    233    * elements out of the way */
    234   if (idx != arr->cnt) {
    235     status = ares_array_move(arr, idx + arr->offset + 1, idx + arr->offset);
    236     if (status != ARES_SUCCESS) {
    237       return status;
    238     }
    239   }
    240 
    241   /* Ok, we're guaranteed to have a gap where we need it, lets zero it out,
    242    * and return it */
    243   ptr = (unsigned char *)arr->arr + ((idx + arr->offset) * arr->member_size);
    244   memset(ptr, 0, arr->member_size);
    245   arr->cnt++;
    246 
    247   if (elem_ptr) {
    248     *elem_ptr = ptr;
    249   }
    250 
    251   return ARES_SUCCESS;
    252 }
    253 
    254 ares_status_t ares_array_insert_last(void **elem_ptr, ares_array_t *arr)
    255 {
    256   return ares_array_insert_at(elem_ptr, arr, ares_array_len(arr));
    257 }
    258 
    259 ares_status_t ares_array_insert_first(void **elem_ptr, ares_array_t *arr)
    260 {
    261   return ares_array_insert_at(elem_ptr, arr, 0);
    262 }
    263 
    264 ares_status_t ares_array_insertdata_at(ares_array_t *arr, size_t idx,
    265                                        const void *data_ptr)
    266 {
    267   ares_status_t status;
    268   void         *ptr = NULL;
    269 
    270   status = ares_array_insert_at(&ptr, arr, idx);
    271   if (status != ARES_SUCCESS) {
    272     return status;
    273   }
    274   memcpy(ptr, data_ptr, arr->member_size);
    275   return ARES_SUCCESS;
    276 }
    277 
    278 ares_status_t ares_array_insertdata_last(ares_array_t *arr,
    279                                          const void   *data_ptr)
    280 {
    281   ares_status_t status;
    282   void         *ptr = NULL;
    283 
    284   status = ares_array_insert_last(&ptr, arr);
    285   if (status != ARES_SUCCESS) {
    286     return status;
    287   }
    288   memcpy(ptr, data_ptr, arr->member_size);
    289   return ARES_SUCCESS;
    290 }
    291 
    292 ares_status_t ares_array_insertdata_first(ares_array_t *arr,
    293                                           const void   *data_ptr)
    294 {
    295   ares_status_t status;
    296   void         *ptr = NULL;
    297 
    298   status = ares_array_insert_last(&ptr, arr);
    299   if (status != ARES_SUCCESS) {
    300     return status;
    301   }
    302   memcpy(ptr, data_ptr, arr->member_size);
    303   return ARES_SUCCESS;
    304 }
    305 
    306 void *ares_array_first(ares_array_t *arr)
    307 {
    308   return ares_array_at(arr, 0);
    309 }
    310 
    311 void *ares_array_last(ares_array_t *arr)
    312 {
    313   size_t cnt = ares_array_len(arr);
    314   if (cnt == 0) {
    315     return NULL;
    316   }
    317   return ares_array_at(arr, cnt - 1);
    318 }
    319 
    320 const void *ares_array_first_const(const ares_array_t *arr)
    321 {
    322   return ares_array_at_const(arr, 0);
    323 }
    324 
    325 const void *ares_array_last_const(const ares_array_t *arr)
    326 {
    327   size_t cnt = ares_array_len(arr);
    328   if (cnt == 0) {
    329     return NULL;
    330   }
    331   return ares_array_at_const(arr, cnt - 1);
    332 }
    333 
    334 ares_status_t ares_array_claim_at(void *dest, size_t dest_size,
    335                                   ares_array_t *arr, size_t idx)
    336 {
    337   ares_status_t status;
    338 
    339   if (arr == NULL || idx >= arr->cnt) {
    340     return ARES_EFORMERR;
    341   }
    342 
    343   if (dest != NULL && dest_size < arr->member_size) {
    344     return ARES_EFORMERR;
    345   }
    346 
    347   if (dest) {
    348     memcpy(dest, ares_array_at(arr, idx), arr->member_size);
    349   }
    350 
    351   if (idx == 0) {
    352     /* Optimization, if first element, just increment offset, makes removing a
    353      * lot from the start quick */
    354     arr->offset++;
    355   } else if (idx != arr->cnt - 1) {
    356     /* Must shift entire array if removing an element from the middle. Does
    357      * nothing if removing last element other than decrement count. */
    358     status = ares_array_move(arr, idx + arr->offset, idx + arr->offset + 1);
    359     if (status != ARES_SUCCESS) {
    360       return status;
    361     }
    362   }
    363 
    364   arr->cnt--;
    365   return ARES_SUCCESS;
    366 }
    367 
    368 ares_status_t ares_array_remove_at(ares_array_t *arr, size_t idx)
    369 {
    370   void *ptr = ares_array_at(arr, idx);
    371   if (arr == NULL || ptr == NULL) {
    372     return ARES_EFORMERR;
    373   }
    374 
    375   if (arr->destruct != NULL) {
    376     arr->destruct(ptr);
    377   }
    378 
    379   return ares_array_claim_at(NULL, 0, arr, idx);
    380 }
    381 
    382 ares_status_t ares_array_remove_first(ares_array_t *arr)
    383 {
    384   return ares_array_remove_at(arr, 0);
    385 }
    386 
    387 ares_status_t ares_array_remove_last(ares_array_t *arr)
    388 {
    389   size_t cnt = ares_array_len(arr);
    390   if (cnt == 0) {
    391     return ARES_EFORMERR;
    392   }
    393   return ares_array_remove_at(arr, cnt - 1);
    394 }