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