1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2023 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 bc_vec_grow(BcVec* restrict v, size_t n)
47 {
48 size_t cap, len;
49 #if !BC_ENABLE_LIBRARY
50 sig_atomic_t lock;
51 #endif // !BC_ENABLE_LIBRARY
52
53 cap = v->cap;
54 len = v->len + n;
55
56 // If this is true, we might overflow.
57 if (len > SIZE_MAX / 2) cap = len;
58 else
59 {
60 // Keep doubling until larger.
61 while (cap < len)
62 {
63 cap += cap;
64 }
65 }
66
67 BC_SIG_TRYLOCK(lock);
68
69 v->v = bc_vm_realloc(v->v, bc_vm_arraySize(cap, v->size));
70 v->cap = cap;
71
72 BC_SIG_TRYUNLOCK(lock);
73 }
74
75 void
bc_vec_init(BcVec * restrict v,size_t esize,BcDtorType dtor)76 bc_vec_init(BcVec* restrict v, size_t esize, BcDtorType dtor)
77 {
78 BC_SIG_ASSERT_LOCKED;
79
80 assert(v != NULL && esize);
81
82 v->v = bc_vm_malloc(bc_vm_arraySize(BC_VEC_START_CAP, esize));
83
84 v->size = (BcSize) esize;
85 v->cap = BC_VEC_START_CAP;
86 v->len = 0;
87 v->dtor = (BcSize) dtor;
88 }
89
90 void
bc_vec_expand(BcVec * restrict v,size_t req)91 bc_vec_expand(BcVec* restrict v, size_t req)
92 {
93 assert(v != NULL);
94
95 // Only expand if necessary.
96 if (v->cap < req)
97 {
98 #if !BC_ENABLE_LIBRARY
99 sig_atomic_t lock;
100 #endif // !BC_ENABLE_LIBRARY
101
102 BC_SIG_TRYLOCK(lock);
103
104 v->v = bc_vm_realloc(v->v, bc_vm_arraySize(req, v->size));
105 v->cap = req;
106
107 BC_SIG_TRYUNLOCK(lock);
108 }
109 }
110
111 void
bc_vec_npop(BcVec * restrict v,size_t n)112 bc_vec_npop(BcVec* restrict v, size_t n)
113 {
114 #if !BC_ENABLE_LIBRARY
115 sig_atomic_t lock;
116 #endif // !BC_ENABLE_LIBRARY
117
118 assert(v != NULL && n <= v->len);
119
120 BC_SIG_TRYLOCK(lock);
121
122 if (!v->dtor) v->len -= n;
123 else
124 {
125 const BcVecFree d = bc_vec_dtors[v->dtor];
126 size_t esize = v->size;
127 size_t len = v->len - n;
128
129 // Loop through and manually destruct every element.
130 while (v->len > len)
131 {
132 d(v->v + (esize * --v->len));
133 }
134 }
135
136 BC_SIG_TRYUNLOCK(lock);
137 }
138
139 void
bc_vec_npopAt(BcVec * restrict v,size_t n,size_t idx)140 bc_vec_npopAt(BcVec* restrict v, size_t n, size_t idx)
141 {
142 char* ptr;
143 char* data;
144 #if !BC_ENABLE_LIBRARY
145 sig_atomic_t lock;
146 #endif // !BC_ENABLE_LIBRARY
147
148 assert(v != NULL);
149 assert(idx + n < v->len);
150
151 // Grab start and end pointers.
152 ptr = bc_vec_item(v, idx);
153 data = bc_vec_item(v, idx + n);
154
155 BC_SIG_TRYLOCK(lock);
156
157 if (v->dtor)
158 {
159 size_t i;
160 const BcVecFree d = bc_vec_dtors[v->dtor];
161
162 // Destroy every popped item.
163 for (i = 0; i < n; ++i)
164 {
165 d(bc_vec_item(v, idx + i));
166 }
167 }
168
169 v->len -= n;
170 // NOLINTNEXTLINE
171 memmove(ptr, data, (v->len - idx) * v->size);
172
173 BC_SIG_TRYUNLOCK(lock);
174 }
175
176 void
bc_vec_npush(BcVec * restrict v,size_t n,const void * data)177 bc_vec_npush(BcVec* restrict v, size_t n, const void* data)
178 {
179 #if !BC_ENABLE_LIBRARY
180 sig_atomic_t lock;
181 #endif // !BC_ENABLE_LIBRARY
182 size_t esize;
183
184 assert(v != NULL && data != NULL);
185
186 BC_SIG_TRYLOCK(lock);
187
188 // Grow if necessary.
189 if (v->len + n > v->cap) bc_vec_grow(v, n);
190
191 esize = v->size;
192
193 // Copy the elements in.
194 // NOLINTNEXTLINE
195 memcpy(v->v + (esize * v->len), data, esize * n);
196 v->len += n;
197
198 BC_SIG_TRYUNLOCK(lock);
199 }
200
201 inline void
bc_vec_push(BcVec * restrict v,const void * data)202 bc_vec_push(BcVec* restrict v, const void* data)
203 {
204 bc_vec_npush(v, 1, data);
205 }
206
207 void*
bc_vec_pushEmpty(BcVec * restrict v)208 bc_vec_pushEmpty(BcVec* restrict v)
209 {
210 #if !BC_ENABLE_LIBRARY
211 sig_atomic_t lock;
212 #endif // !BC_ENABLE_LIBRARY
213 void* ptr;
214
215 assert(v != NULL);
216
217 BC_SIG_TRYLOCK(lock);
218
219 // Grow if necessary.
220 if (v->len + 1 > v->cap) bc_vec_grow(v, 1);
221
222 ptr = v->v + v->size * v->len;
223 v->len += 1;
224
225 BC_SIG_TRYUNLOCK(lock);
226
227 return ptr;
228 }
229
230 inline void
bc_vec_pushByte(BcVec * restrict v,uchar data)231 bc_vec_pushByte(BcVec* restrict v, uchar data)
232 {
233 assert(v != NULL && v->size == sizeof(uchar));
234 bc_vec_npush(v, 1, &data);
235 }
236
237 void
bc_vec_pushIndex(BcVec * restrict v,size_t idx)238 bc_vec_pushIndex(BcVec* restrict v, size_t idx)
239 {
240 uchar amt, nums[sizeof(size_t) + 1];
241
242 assert(v != NULL);
243 assert(v->size == sizeof(uchar));
244
245 // Encode the index.
246 for (amt = 0; idx; ++amt)
247 {
248 nums[amt + 1] = (uchar) idx;
249 idx &= ((size_t) ~(UCHAR_MAX));
250 idx >>= sizeof(uchar) * CHAR_BIT;
251 }
252
253 nums[0] = amt;
254
255 // Push the index onto the vector.
256 bc_vec_npush(v, amt + 1, nums);
257 }
258
259 void
bc_vec_pushAt(BcVec * restrict v,const void * data,size_t idx)260 bc_vec_pushAt(BcVec* restrict v, const void* data, size_t idx)
261 {
262 assert(v != NULL && data != NULL && idx <= v->len);
263
264 BC_SIG_ASSERT_LOCKED;
265
266 // Do the easy case.
267 if (idx == v->len) bc_vec_push(v, data);
268 else
269 {
270 char* ptr;
271 size_t esize;
272
273 // Grow if necessary.
274 if (v->len == v->cap) bc_vec_grow(v, 1);
275
276 esize = v->size;
277
278 ptr = v->v + esize * idx;
279
280 // NOLINTNEXTLINE
281 memmove(ptr + esize, ptr, esize * (v->len++ - idx));
282 // NOLINTNEXTLINE
283 memcpy(ptr, data, esize);
284 }
285 }
286
287 void
bc_vec_string(BcVec * restrict v,size_t len,const char * restrict str)288 bc_vec_string(BcVec* restrict v, size_t len, const char* restrict str)
289 {
290 #if !BC_ENABLE_LIBRARY
291 sig_atomic_t lock;
292 #endif // !BC_ENABLE_LIBRARY
293
294 assert(v != NULL && v->size == sizeof(char));
295 assert(!v->dtor);
296 assert(!v->len || !v->v[v->len - 1]);
297 assert(v->v != str);
298
299 BC_SIG_TRYLOCK(lock);
300
301 bc_vec_popAll(v);
302 bc_vec_expand(v, bc_vm_growSize(len, 1));
303 // NOLINTNEXTLINE
304 memcpy(v->v, str, len);
305 v->len = len;
306
307 bc_vec_pushByte(v, '\0');
308
309 BC_SIG_TRYUNLOCK(lock);
310 }
311
312 void
bc_vec_concat(BcVec * restrict v,const char * restrict str)313 bc_vec_concat(BcVec* restrict v, const char* restrict str)
314 {
315 #if !BC_ENABLE_LIBRARY
316 sig_atomic_t lock;
317 #endif // !BC_ENABLE_LIBRARY
318
319 assert(v != NULL && v->size == sizeof(char));
320 assert(!v->dtor);
321 assert(!v->len || !v->v[v->len - 1]);
322 assert(v->v != str);
323
324 BC_SIG_TRYLOCK(lock);
325
326 // If there is already a string, erase its nul byte.
327 if (v->len) v->len -= 1;
328
329 bc_vec_npush(v, strlen(str) + 1, str);
330
331 BC_SIG_TRYUNLOCK(lock);
332 }
333
334 void
bc_vec_empty(BcVec * restrict v)335 bc_vec_empty(BcVec* restrict v)
336 {
337 #if !BC_ENABLE_LIBRARY
338 sig_atomic_t lock;
339 #endif // !BC_ENABLE_LIBRARY
340
341 assert(v != NULL && v->size == sizeof(char));
342 assert(!v->dtor);
343
344 BC_SIG_TRYLOCK(lock);
345
346 bc_vec_popAll(v);
347 bc_vec_pushByte(v, '\0');
348
349 BC_SIG_TRYUNLOCK(lock);
350 }
351
352 #if BC_ENABLE_HISTORY
353 void
bc_vec_replaceAt(BcVec * restrict v,size_t idx,const void * data)354 bc_vec_replaceAt(BcVec* restrict v, size_t idx, const void* data)
355 {
356 char* ptr;
357
358 BC_SIG_ASSERT_LOCKED;
359
360 assert(v != NULL);
361
362 ptr = bc_vec_item(v, idx);
363
364 if (v->dtor) bc_vec_dtors[v->dtor](ptr);
365
366 // NOLINTNEXTLINE
367 memcpy(ptr, data, v->size);
368 }
369 #endif // BC_ENABLE_HISTORY
370
371 inline void*
bc_vec_item(const BcVec * restrict v,size_t idx)372 bc_vec_item(const BcVec* restrict v, size_t idx)
373 {
374 assert(v != NULL && v->len && idx < v->len);
375 return v->v + v->size * idx;
376 }
377
378 inline void*
bc_vec_item_rev(const BcVec * restrict v,size_t idx)379 bc_vec_item_rev(const BcVec* restrict v, size_t idx)
380 {
381 assert(v != NULL && v->len && idx < v->len);
382 return v->v + v->size * (v->len - idx - 1);
383 }
384
385 inline void
bc_vec_clear(BcVec * restrict v)386 bc_vec_clear(BcVec* restrict v)
387 {
388 BC_SIG_ASSERT_LOCKED;
389 v->v = NULL;
390 v->len = 0;
391 v->dtor = BC_DTOR_NONE;
392 }
393
394 void
bc_vec_free(void * vec)395 bc_vec_free(void* vec)
396 {
397 BcVec* v = (BcVec*) vec;
398 BC_SIG_ASSERT_LOCKED;
399 bc_vec_popAll(v);
400 free(v->v);
401 }
402
403 #if !BC_ENABLE_LIBRARY
404
405 /**
406 * Finds a name in a map by binary search. Returns the index where the item
407 * *would* be if it doesn't exist. Callers are responsible for checking that the
408 * item exists at the index.
409 * @param v The map.
410 * @param name The name to find.
411 * @return The index of the item with @a name, or where the item would be
412 * if it does not exist.
413 */
414 static size_t
bc_map_find(const BcVec * restrict v,const char * name)415 bc_map_find(const BcVec* restrict v, const char* name)
416 {
417 size_t low = 0, high = v->len;
418
419 while (low < high)
420 {
421 size_t mid = low + (high - low) / 2;
422 const BcId* id = bc_vec_item(v, mid);
423 int result = strcmp(name, id->name);
424
425 if (!result) return mid;
426 else if (result < 0) high = mid;
427 else low = mid + 1;
428 }
429
430 return low;
431 }
432
433 bool
bc_map_insert(BcVec * restrict v,const char * name,size_t idx,size_t * restrict i)434 bc_map_insert(BcVec* restrict v, const char* name, size_t idx,
435 size_t* restrict i)
436 {
437 BcId id;
438
439 BC_SIG_ASSERT_LOCKED;
440
441 assert(v != NULL && name != NULL && i != NULL);
442
443 *i = bc_map_find(v, name);
444
445 assert(*i <= v->len);
446
447 if (*i != v->len && !strcmp(name, ((BcId*) bc_vec_item(v, *i))->name))
448 {
449 return false;
450 }
451
452 id.name = bc_slabvec_strdup(&vm->slabs, name);
453 id.idx = idx;
454
455 bc_vec_pushAt(v, &id, *i);
456
457 return true;
458 }
459
460 size_t
bc_map_index(const BcVec * restrict v,const char * name)461 bc_map_index(const BcVec* restrict v, const char* name)
462 {
463 size_t i;
464 BcId* id;
465
466 assert(v != NULL && name != NULL);
467
468 i = bc_map_find(v, name);
469
470 // If out of range, return invalid.
471 if (i >= v->len) return BC_VEC_INVALID_IDX;
472
473 id = (BcId*) bc_vec_item(v, i);
474
475 // Make sure the item exists and return appropriately.
476 return strcmp(name, id->name) ? BC_VEC_INVALID_IDX : i;
477 }
478
479 #if DC_ENABLED
480 const char*
bc_map_name(const BcVec * restrict v,size_t idx)481 bc_map_name(const BcVec* restrict v, size_t idx)
482 {
483 size_t i, len = v->len;
484
485 for (i = 0; i < len; ++i)
486 {
487 BcId* id = (BcId*) bc_vec_item(v, i);
488 if (id->idx == idx) return id->name;
489 }
490
491 BC_UNREACHABLE
492
493 #if !BC_CLANG
494 return "";
495 #endif // !BC_CLANG
496 }
497 #endif // DC_ENABLED
498
499 /**
500 * Initializes a single slab.
501 * @param s The slab to initialize.
502 */
503 static void
bc_slab_init(BcSlab * s)504 bc_slab_init(BcSlab* s)
505 {
506 s->s = bc_vm_malloc(BC_SLAB_SIZE);
507 s->len = 0;
508 }
509
510 /**
511 * Adds a string to a slab and returns a pointer to it, or NULL if it could not
512 * be added.
513 * @param s The slab to add to.
514 * @param str The string to add.
515 * @param len The length of the string, including its nul byte.
516 * @return A pointer to the new string in the slab, or NULL if it could not
517 * be added.
518 */
519 static char*
bc_slab_add(BcSlab * s,const char * str,size_t len)520 bc_slab_add(BcSlab* s, const char* str, size_t len)
521 {
522 char* ptr;
523
524 assert(s != NULL);
525 assert(str != NULL);
526 assert(len == strlen(str) + 1);
527
528 if (s->len + len > BC_SLAB_SIZE) return NULL;
529
530 ptr = (char*) (s->s + s->len);
531
532 // NOLINTNEXTLINE
533 bc_strcpy(ptr, len, str);
534
535 s->len += len;
536
537 return ptr;
538 }
539
540 void
bc_slab_free(void * slab)541 bc_slab_free(void* slab)
542 {
543 free(((BcSlab*) slab)->s);
544 }
545
546 void
bc_slabvec_init(BcVec * v)547 bc_slabvec_init(BcVec* v)
548 {
549 BcSlab* slab;
550
551 assert(v != NULL);
552
553 bc_vec_init(v, sizeof(BcSlab), BC_DTOR_SLAB);
554
555 // We always want to have at least one slab.
556 slab = bc_vec_pushEmpty(v);
557 bc_slab_init(slab);
558 }
559
560 char*
bc_slabvec_strdup(BcVec * v,const char * str)561 bc_slabvec_strdup(BcVec* v, const char* str)
562 {
563 char* s;
564 size_t len;
565 BcSlab slab;
566 BcSlab* slab_ptr;
567
568 BC_SIG_ASSERT_LOCKED;
569
570 assert(v != NULL && v->len);
571
572 assert(str != NULL);
573
574 len = strlen(str) + 1;
575
576 // If the len is greater than 128, then just allocate it with malloc.
577 if (BC_UNLIKELY(len >= BC_SLAB_SIZE))
578 {
579 // SIZE_MAX is a marker for these standalone allocations.
580 slab.len = SIZE_MAX;
581 slab.s = bc_vm_strdup(str);
582
583 // Push the standalone slab.
584 bc_vec_pushAt(v, &slab, v->len - 1);
585
586 return slab.s;
587 }
588
589 // Add to a slab.
590 slab_ptr = bc_vec_top(v);
591 s = bc_slab_add(slab_ptr, str, len);
592
593 // If it couldn't be added, add a slab and try again.
594 if (BC_UNLIKELY(s == NULL))
595 {
596 slab_ptr = bc_vec_pushEmpty(v);
597 bc_slab_init(slab_ptr);
598
599 s = bc_slab_add(slab_ptr, str, len);
600
601 assert(s != NULL);
602 }
603
604 return s;
605 }
606
607 void
bc_slabvec_clear(BcVec * v)608 bc_slabvec_clear(BcVec* v)
609 {
610 BcSlab* s;
611 bool again;
612
613 // This complicated loop exists because of standalone allocations over 128
614 // bytes.
615 do
616 {
617 // Get the first slab.
618 s = bc_vec_item(v, 0);
619
620 // Either the slab must be valid (not standalone), or there must be
621 // another slab.
622 assert(s->len != SIZE_MAX || v->len > 1);
623
624 // Do we have to loop again? We do if it's a standalone allocation.
625 again = (s->len == SIZE_MAX);
626
627 // Pop the standalone allocation, not the one after it.
628 if (again) bc_vec_npopAt(v, 1, 0);
629 }
630 while (again);
631
632 // If we get here, we know that the first slab is a valid slab. We want to
633 // pop all of the other slabs.
634 if (v->len > 1) bc_vec_npop(v, v->len - 1);
635
636 // Empty the first slab.
637 s->len = 0;
638 }
639 #endif // !BC_ENABLE_LIBRARY
640
641 #if BC_DEBUG_CODE
642
643 void
bc_slabvec_print(BcVec * v,const char * func)644 bc_slabvec_print(BcVec* v, const char* func)
645 {
646 size_t i;
647 BcSlab* s;
648
649 bc_file_printf(&vm->ferr, "%s\n", func);
650
651 for (i = 0; i < v->len; ++i)
652 {
653 s = bc_vec_item(v, i);
654 bc_file_printf(&vm->ferr, "%zu { s = %zu, len = %zu }\n", i,
655 (uintptr_t) s->s, s->len);
656 }
657
658 bc_file_puts(&vm->ferr, bc_flush_none, "\n");
659 bc_file_flush(&vm->ferr, bc_flush_none);
660 }
661
662 #endif // BC_DEBUG_CODE
663