• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ares_array_create(size_t member_size,ares_array_destructor_t destruct)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 
ares_array_len(const ares_array_t * arr)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 
ares_array_at(ares_array_t * arr,size_t idx)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 
ares_array_at_const(const ares_array_t * arr,size_t idx)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 
ares_array_sort(ares_array_t * arr,ares_array_cmp_t cmp)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 
ares_array_destroy(ares_array_t * arr)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 */
ares_array_move(ares_array_t * arr,size_t dest_idx,size_t src_idx)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 
ares_array_finish(ares_array_t * arr,size_t * num_members)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 
ares_array_set_size(ares_array_t * arr,size_t size)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 
ares_array_insert_at(void ** elem_ptr,ares_array_t * arr,size_t idx)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 
ares_array_insert_last(void ** elem_ptr,ares_array_t * arr)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 
ares_array_insert_first(void ** elem_ptr,ares_array_t * arr)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 
ares_array_insertdata_at(ares_array_t * arr,size_t idx,const void * data_ptr)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 
ares_array_insertdata_last(ares_array_t * arr,const void * data_ptr)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 
ares_array_insertdata_first(ares_array_t * arr,const void * data_ptr)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 
ares_array_first(ares_array_t * arr)306 void *ares_array_first(ares_array_t *arr)
307 {
308   return ares_array_at(arr, 0);
309 }
310 
ares_array_last(ares_array_t * arr)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 
ares_array_first_const(const ares_array_t * arr)320 const void *ares_array_first_const(const ares_array_t *arr)
321 {
322   return ares_array_at_const(arr, 0);
323 }
324 
ares_array_last_const(const ares_array_t * arr)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 
ares_array_claim_at(void * dest,size_t dest_size,ares_array_t * arr,size_t idx)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 
ares_array_remove_at(ares_array_t * arr,size_t idx)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 
ares_array_remove_first(ares_array_t * arr)382 ares_status_t ares_array_remove_first(ares_array_t *arr)
383 {
384   return ares_array_remove_at(arr, 0);
385 }
386 
ares_array_remove_last(ares_array_t * arr)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 }
395