]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/src/vector.c
usr.bin/gh-bc, contrib/bc: update to version 5.0.0
[FreeBSD/FreeBSD.git] / contrib / bc / src / vector.c
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * * Redistributions of source code must retain the above copyright notice, this
12  *   list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright notice,
15  *   this list of conditions and the following disclaimer in the documentation
16  *   and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * *****************************************************************************
31  *
32  * Code to manipulate vectors (resizable arrays).
33  *
34  */
35
36 #include <assert.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdbool.h>
40
41 #include <vector.h>
42 #include <lang.h>
43 #include <vm.h>
44
45 void bc_vec_grow(BcVec *restrict v, size_t n) {
46
47         size_t cap, len;
48         sig_atomic_t lock;
49
50         cap = v->cap;
51         len = v->len + n;
52
53         // If this is true, we might overflow.
54         if (len > SIZE_MAX / 2) cap = len;
55         else {
56                 // Keep doubling until larger.
57                 while (cap < len) cap += cap;
58         }
59
60         BC_SIG_TRYLOCK(lock);
61
62         v->v = bc_vm_realloc(v->v, bc_vm_arraySize(cap, v->size));
63         v->cap = cap;
64
65         BC_SIG_TRYUNLOCK(lock);
66 }
67
68 void bc_vec_init(BcVec *restrict v, size_t esize, BcDtorType dtor) {
69
70         BC_SIG_ASSERT_LOCKED;
71
72         assert(v != NULL && esize);
73
74         v->v = bc_vm_malloc(bc_vm_arraySize(BC_VEC_START_CAP, esize));
75
76         v->size = (BcSize) esize;
77         v->cap = BC_VEC_START_CAP;
78         v->len = 0;
79         v->dtor = (BcSize) dtor;
80 }
81
82 void bc_vec_expand(BcVec *restrict v, size_t req) {
83
84         assert(v != NULL);
85
86         // Only expand if necessary.
87         if (v->cap < req) {
88
89                 sig_atomic_t lock;
90
91                 BC_SIG_TRYLOCK(lock);
92
93                 v->v = bc_vm_realloc(v->v, bc_vm_arraySize(req, v->size));
94                 v->cap = req;
95
96                 BC_SIG_TRYUNLOCK(lock);
97         }
98 }
99
100 void bc_vec_npop(BcVec *restrict v, size_t n) {
101
102         sig_atomic_t lock;
103
104         assert(v != NULL && n <= v->len);
105
106         BC_SIG_TRYLOCK(lock);
107
108         if (!v->dtor) v->len -= n;
109         else {
110
111                 const BcVecFree d = bc_vec_dtors[v->dtor];
112                 size_t esize = v->size;
113                 size_t len = v->len - n;
114
115                 // Loop through and manually destruct every element.
116                 while (v->len > len) d(v->v + (esize * --v->len));
117         }
118
119         BC_SIG_TRYUNLOCK(lock);
120 }
121
122 void bc_vec_npopAt(BcVec *restrict v, size_t n, size_t idx) {
123
124         char* ptr, *data;
125         sig_atomic_t lock;
126
127         assert(v != NULL);
128         assert(idx + n < v->len);
129
130         // Grab start and end pointers.
131         ptr = bc_vec_item(v, idx);
132         data = bc_vec_item(v, idx + n);
133
134         BC_SIG_TRYLOCK(lock);
135
136         if (v->dtor) {
137
138                 size_t i;
139                 const BcVecFree d = bc_vec_dtors[v->dtor];
140
141                 // Destroy every popped item.
142                 for (i = 0; i < n; ++i) d(bc_vec_item(v, idx + i));
143         }
144
145         v->len -= n;
146         memmove(ptr, data, (v->len - idx) * v->size);
147
148         BC_SIG_TRYUNLOCK(lock);
149 }
150
151 void bc_vec_npush(BcVec *restrict v, size_t n, const void *data) {
152
153         sig_atomic_t lock;
154         size_t esize;
155
156         assert(v != NULL && data != NULL);
157
158         BC_SIG_TRYLOCK(lock);
159
160         // Grow if necessary.
161         if (v->len + n > v->cap) bc_vec_grow(v, n);
162
163         esize = v->size;
164
165         // Copy the elements in.
166         memcpy(v->v + (esize * v->len), data, esize * n);
167         v->len += n;
168
169         BC_SIG_TRYUNLOCK(lock);
170 }
171
172 inline void bc_vec_push(BcVec *restrict v, const void *data) {
173         bc_vec_npush(v, 1, data);
174 }
175
176 void* bc_vec_pushEmpty(BcVec *restrict v) {
177
178         sig_atomic_t lock;
179         void *ptr;
180
181         assert(v != NULL);
182
183         BC_SIG_TRYLOCK(lock);
184
185         // Grow if necessary.
186         if (v->len + 1 > v->cap) bc_vec_grow(v, 1);
187
188         ptr = v->v + v->size * v->len;
189         v->len += 1;
190
191         BC_SIG_TRYUNLOCK(lock);
192
193         return ptr;
194 }
195
196 inline void bc_vec_pushByte(BcVec *restrict v, uchar data) {
197         assert(v != NULL && v->size == sizeof(uchar));
198         bc_vec_npush(v, 1, &data);
199 }
200
201 void bc_vec_pushIndex(BcVec *restrict v, size_t idx) {
202
203         uchar amt, nums[sizeof(size_t) + 1];
204
205         assert(v != NULL);
206         assert(v->size == sizeof(uchar));
207
208         // Encode the index.
209         for (amt = 0; idx; ++amt) {
210                 nums[amt + 1] = (uchar) idx;
211                 idx &= ((size_t) ~(UCHAR_MAX));
212                 idx >>= sizeof(uchar) * CHAR_BIT;
213         }
214
215         nums[0] = amt;
216
217         // Push the index onto the vector.
218         bc_vec_npush(v, amt + 1, nums);
219 }
220
221 void bc_vec_pushAt(BcVec *restrict v, const void *data, size_t idx) {
222
223         assert(v != NULL && data != NULL && idx <= v->len);
224
225         BC_SIG_ASSERT_LOCKED;
226
227         // Do the easy case.
228         if (idx == v->len) bc_vec_push(v, data);
229         else {
230
231                 char *ptr;
232                 size_t esize;
233
234                 // Grow if necessary.
235                 if (v->len == v->cap) bc_vec_grow(v, 1);
236
237                 esize = v->size;
238
239                 ptr = v->v + esize * idx;
240
241                 memmove(ptr + esize, ptr, esize * (v->len++ - idx));
242                 memcpy(ptr, data, esize);
243         }
244 }
245
246 void bc_vec_string(BcVec *restrict v, size_t len, const char *restrict str) {
247
248         sig_atomic_t lock;
249
250         assert(v != NULL && v->size == sizeof(char));
251         assert(!v->dtor);
252         assert(!v->len || !v->v[v->len - 1]);
253         assert(v->v != str);
254
255         BC_SIG_TRYLOCK(lock);
256
257         bc_vec_popAll(v);
258         bc_vec_expand(v, bc_vm_growSize(len, 1));
259         memcpy(v->v, str, len);
260         v->len = len;
261
262         bc_vec_pushByte(v, '\0');
263
264         BC_SIG_TRYUNLOCK(lock);
265 }
266
267 void bc_vec_concat(BcVec *restrict v, const char *restrict str) {
268
269         sig_atomic_t lock;
270
271         assert(v != NULL && v->size == sizeof(char));
272         assert(!v->dtor);
273         assert(!v->len || !v->v[v->len - 1]);
274         assert(v->v != str);
275
276         BC_SIG_TRYLOCK(lock);
277
278         // If there is already a string, erase its nul byte.
279         if (v->len) v->len -= 1;
280
281         bc_vec_npush(v, strlen(str) + 1, str);
282
283         BC_SIG_TRYUNLOCK(lock);
284 }
285
286 void bc_vec_empty(BcVec *restrict v) {
287
288         sig_atomic_t lock;
289
290         assert(v != NULL && v->size == sizeof(char));
291         assert(!v->dtor);
292
293         BC_SIG_TRYLOCK(lock);
294
295         bc_vec_popAll(v);
296         bc_vec_pushByte(v, '\0');
297
298         BC_SIG_TRYUNLOCK(lock);
299 }
300
301 #if BC_ENABLE_HISTORY
302 void bc_vec_replaceAt(BcVec *restrict v, size_t idx, const void *data) {
303
304         char *ptr;
305
306         BC_SIG_ASSERT_LOCKED;
307
308         assert(v != NULL);
309
310         ptr = bc_vec_item(v, idx);
311
312         if (v->dtor) bc_vec_dtors[v->dtor](ptr);
313
314         memcpy(ptr, data, v->size);
315 }
316 #endif // BC_ENABLE_HISTORY
317
318 inline void* bc_vec_item(const BcVec *restrict v, size_t idx) {
319         assert(v != NULL && v->len && idx < v->len);
320         return v->v + v->size * idx;
321 }
322
323 inline void* bc_vec_item_rev(const BcVec *restrict v, size_t idx) {
324         assert(v != NULL && v->len && idx < v->len);
325         return v->v + v->size * (v->len - idx - 1);
326 }
327
328 inline void bc_vec_clear(BcVec *restrict v) {
329         BC_SIG_ASSERT_LOCKED;
330         v->v = NULL;
331         v->len = 0;
332         v->dtor = BC_DTOR_NONE;
333 }
334
335 void bc_vec_free(void *vec) {
336         BcVec *v = (BcVec*) vec;
337         BC_SIG_ASSERT_LOCKED;
338         bc_vec_popAll(v);
339         free(v->v);
340 }
341
342 #if !BC_ENABLE_LIBRARY
343
344 /**
345  * Finds a name in a map by binary search. Returns the index where the item
346  * *would* be if it doesn't exist. Callers are responsible for checking that the
347  * item exists at the index.
348  * @param v     The map.
349  * @param name  The name to find.
350  * @return      The index of the item with @a name, or where the item would be
351  *              if it does not exist.
352  */
353 static size_t bc_map_find(const BcVec *restrict v, const char *name) {
354
355         size_t low = 0, high = v->len;
356
357         while (low < high) {
358
359                 size_t mid = (low + high) / 2;
360                 const BcId *id = bc_vec_item(v, mid);
361                 int result = strcmp(name, id->name);
362
363                 if (!result) return mid;
364                 else if (result < 0) high = mid;
365                 else low = mid + 1;
366         }
367
368         return low;
369 }
370
371 bool bc_map_insert(BcVec *restrict v, const char *name,
372                    size_t idx, size_t *restrict i)
373 {
374         BcId id;
375         BcVec *slabs;
376
377         BC_SIG_ASSERT_LOCKED;
378
379         assert(v != NULL && name != NULL && i != NULL);
380
381         *i = bc_map_find(v, name);
382
383         assert(*i <= v->len);
384
385         if (*i != v->len && !strcmp(name, ((BcId*) bc_vec_item(v, *i))->name))
386                 return false;
387
388 #if BC_ENABLED
389         slabs = BC_IS_DC ? &vm.main_slabs : &vm.other_slabs;
390 #else // BC_ENABLED
391         slabs = &vm.main_slabs;
392 #endif // BC_ENABLED
393
394         id.name = bc_slabvec_strdup(slabs, name);
395         id.idx = idx;
396
397         bc_vec_pushAt(v, &id, *i);
398
399         return true;
400 }
401
402 size_t bc_map_index(const BcVec *restrict v, const char *name) {
403
404         size_t i;
405
406         assert(v != NULL && name != NULL);
407
408         i = bc_map_find(v, name);
409
410         // If out of range, return invalid.
411         if (i >= v->len) return BC_VEC_INVALID_IDX;
412
413         // Make sure the item exists.
414         return strcmp(name, ((BcId*) bc_vec_item(v, i))->name) ?
415             BC_VEC_INVALID_IDX : i;
416 }
417
418 #if DC_ENABLED
419 const char* bc_map_name(const BcVec *restrict v, size_t idx) {
420
421         size_t i, len = v->len;
422
423         for (i = 0; i < len; ++i) {
424                 BcId* id = (BcId*) bc_vec_item(v, i);
425                 if (id->idx == idx) return id->name;
426         }
427
428         BC_UNREACHABLE
429
430         return "";
431 }
432 #endif // DC_ENABLED
433
434 /**
435  * Initializes a single slab.
436  * @param s  The slab to initialize.
437  */
438 static void bc_slab_init(BcSlab *s) {
439         s->s = bc_vm_malloc(BC_SLAB_SIZE);
440         s->len = 0;
441 }
442
443 /**
444  * Adds a string to a slab and returns a pointer to it, or NULL if it could not
445  * be added.
446  * @param s    The slab to add to.
447  * @param str  The string to add.
448  * @param len  The length of the string, including its nul byte.
449  * @return     A pointer to the new string in the slab, or NULL if it could not
450  *             be added.
451  */
452 static char* bc_slab_add(BcSlab *s, const char *str, size_t len) {
453
454         char *ptr;
455
456         assert(s != NULL);
457         assert(str != NULL);
458         assert(len == strlen(str) + 1);
459
460         if (s->len + len > BC_SLAB_SIZE) return NULL;
461
462         ptr = (char*) (s->s + s->len);
463
464         bc_strcpy(ptr, len, str);
465
466         s->len += len;
467
468         return ptr;
469 }
470
471 void bc_slab_free(void *slab) {
472         free(((BcSlab*) slab)->s);
473 }
474
475 void bc_slabvec_init(BcVec* v) {
476
477         BcSlab *slab;
478
479         assert(v != NULL);
480
481         bc_vec_init(v, sizeof(BcSlab), BC_DTOR_SLAB);
482
483         // We always want to have at least one slab.
484         slab = bc_vec_pushEmpty(v);
485         bc_slab_init(slab);
486 }
487
488 char* bc_slabvec_strdup(BcVec *v, const char *str) {
489
490         char *s;
491         size_t len;
492         BcSlab slab;
493         BcSlab *slab_ptr;
494
495         BC_SIG_ASSERT_LOCKED;
496
497         assert(v != NULL && v->len);
498
499         assert(str != NULL);
500
501         len = strlen(str) + 1;
502
503         // If the len is greater than 128, then just allocate it with malloc.
504         if (BC_UNLIKELY(len >= BC_SLAB_SIZE)) {
505
506                 // SIZE_MAX is a marker for these standalone allocations.
507                 slab.len = SIZE_MAX;
508                 slab.s = bc_vm_strdup(str);
509
510                 // Push the standalone slab.
511                 bc_vec_pushAt(v, &slab, v->len - 1);
512
513                 return slab.s;
514         }
515
516         // Add to a slab.
517         slab_ptr = bc_vec_top(v);
518         s = bc_slab_add(slab_ptr, str, len);
519
520         // If it couldn't be added, add a slab and try again.
521         if (BC_UNLIKELY(s == NULL)) {
522
523                 slab_ptr = bc_vec_pushEmpty(v);
524                 bc_slab_init(slab_ptr);
525
526                 s = bc_slab_add(slab_ptr, str, len);
527
528                 assert(s != NULL);
529         }
530
531         return s;
532 }
533
534 void bc_slabvec_clear(BcVec *v) {
535
536         BcSlab *s;
537         bool again;
538
539         // This complicated loop exists because of standalone allocations over 128
540         // bytes.
541         do {
542
543                 // Get the first slab.
544                 s = bc_vec_item(v, 0);
545
546                 // Either the slab must be valid (not standalone), or there must be
547                 // another slab.
548                 assert(s->len != SIZE_MAX || v->len > 1);
549
550                 // Do we have to loop again? We do if it's a standalone allocation.
551                 again = (s->len == SIZE_MAX);
552
553                 // Pop the standalone allocation, not the one after it.
554                 if (again) bc_vec_npopAt(v, 1, 0);
555
556         } while(again);
557
558         // If we get here, we know that the first slab is a valid slab. We want to
559         // pop all of the other slabs.
560         if (v->len > 1) bc_vec_npop(v, v->len - 1);
561
562         // Empty the first slab.
563         s->len = 0;
564 }
565 #endif // !BC_ENABLE_LIBRARY
566
567 #if BC_DEBUG_CODE
568
569 void bc_slabvec_print(BcVec *v, const char *func) {
570
571         size_t i;
572         BcSlab *s;
573
574         bc_file_printf(&vm.ferr, "%s\n", func);
575
576         for (i = 0; i < v->len; ++i) {
577                 s = bc_vec_item(v, i);
578                 bc_file_printf(&vm.ferr, "%zu { s = %zu, len = %zu }\n",
579                                i, (uintptr_t) s->s, s->len);
580         }
581
582         bc_file_puts(&vm.ferr, bc_flush_none, "\n");
583         bc_file_flush(&vm.ferr, bc_flush_none);
584 }
585
586 #endif // BC_DEBUG_CODE