2 * *****************************************************************************
4 * SPDX-License-Identifier: BSD-2-Clause
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
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.
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.
30 * *****************************************************************************
32 * Code to manipulate vectors (resizable arrays).
45 void bc_vec_grow(BcVec *restrict v, size_t n) {
53 // If this is true, we might overflow.
54 if (len > SIZE_MAX / 2) cap = len;
56 // Keep doubling until larger.
57 while (cap < len) cap += cap;
62 v->v = bc_vm_realloc(v->v, bc_vm_arraySize(cap, v->size));
65 BC_SIG_TRYUNLOCK(lock);
68 void bc_vec_init(BcVec *restrict v, size_t esize, BcDtorType dtor) {
72 assert(v != NULL && esize);
74 v->v = bc_vm_malloc(bc_vm_arraySize(BC_VEC_START_CAP, esize));
76 v->size = (BcSize) esize;
77 v->cap = BC_VEC_START_CAP;
79 v->dtor = (BcSize) dtor;
82 void bc_vec_expand(BcVec *restrict v, size_t req) {
86 // Only expand if necessary.
93 v->v = bc_vm_realloc(v->v, bc_vm_arraySize(req, v->size));
96 BC_SIG_TRYUNLOCK(lock);
100 void bc_vec_npop(BcVec *restrict v, size_t n) {
104 assert(v != NULL && n <= v->len);
106 BC_SIG_TRYLOCK(lock);
108 if (!v->dtor) v->len -= n;
111 const BcVecFree d = bc_vec_dtors[v->dtor];
112 size_t esize = v->size;
113 size_t len = v->len - n;
115 // Loop through and manually destruct every element.
116 while (v->len > len) d(v->v + (esize * --v->len));
119 BC_SIG_TRYUNLOCK(lock);
122 void bc_vec_npopAt(BcVec *restrict v, size_t n, size_t idx) {
128 assert(idx + n < v->len);
130 // Grab start and end pointers.
131 ptr = bc_vec_item(v, idx);
132 data = bc_vec_item(v, idx + n);
134 BC_SIG_TRYLOCK(lock);
139 const BcVecFree d = bc_vec_dtors[v->dtor];
141 // Destroy every popped item.
142 for (i = 0; i < n; ++i) d(bc_vec_item(v, idx + i));
146 memmove(ptr, data, (v->len - idx) * v->size);
148 BC_SIG_TRYUNLOCK(lock);
151 void bc_vec_npush(BcVec *restrict v, size_t n, const void *data) {
156 assert(v != NULL && data != NULL);
158 BC_SIG_TRYLOCK(lock);
160 // Grow if necessary.
161 if (v->len + n > v->cap) bc_vec_grow(v, n);
165 // Copy the elements in.
166 memcpy(v->v + (esize * v->len), data, esize * n);
169 BC_SIG_TRYUNLOCK(lock);
172 inline void bc_vec_push(BcVec *restrict v, const void *data) {
173 bc_vec_npush(v, 1, data);
176 void* bc_vec_pushEmpty(BcVec *restrict v) {
183 BC_SIG_TRYLOCK(lock);
185 // Grow if necessary.
186 if (v->len + 1 > v->cap) bc_vec_grow(v, 1);
188 ptr = v->v + v->size * v->len;
191 BC_SIG_TRYUNLOCK(lock);
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);
201 void bc_vec_pushIndex(BcVec *restrict v, size_t idx) {
203 uchar amt, nums[sizeof(size_t) + 1];
206 assert(v->size == sizeof(uchar));
209 for (amt = 0; idx; ++amt) {
210 nums[amt + 1] = (uchar) idx;
211 idx &= ((size_t) ~(UCHAR_MAX));
212 idx >>= sizeof(uchar) * CHAR_BIT;
217 // Push the index onto the vector.
218 bc_vec_npush(v, amt + 1, nums);
221 void bc_vec_pushAt(BcVec *restrict v, const void *data, size_t idx) {
223 assert(v != NULL && data != NULL && idx <= v->len);
225 BC_SIG_ASSERT_LOCKED;
228 if (idx == v->len) bc_vec_push(v, data);
234 // Grow if necessary.
235 if (v->len == v->cap) bc_vec_grow(v, 1);
239 ptr = v->v + esize * idx;
241 memmove(ptr + esize, ptr, esize * (v->len++ - idx));
242 memcpy(ptr, data, esize);
246 void bc_vec_string(BcVec *restrict v, size_t len, const char *restrict str) {
250 assert(v != NULL && v->size == sizeof(char));
252 assert(!v->len || !v->v[v->len - 1]);
255 BC_SIG_TRYLOCK(lock);
258 bc_vec_expand(v, bc_vm_growSize(len, 1));
259 memcpy(v->v, str, len);
262 bc_vec_pushByte(v, '\0');
264 BC_SIG_TRYUNLOCK(lock);
267 void bc_vec_concat(BcVec *restrict v, const char *restrict str) {
271 assert(v != NULL && v->size == sizeof(char));
273 assert(!v->len || !v->v[v->len - 1]);
276 BC_SIG_TRYLOCK(lock);
278 // If there is already a string, erase its nul byte.
279 if (v->len) v->len -= 1;
281 bc_vec_npush(v, strlen(str) + 1, str);
283 BC_SIG_TRYUNLOCK(lock);
286 void bc_vec_empty(BcVec *restrict v) {
290 assert(v != NULL && v->size == sizeof(char));
293 BC_SIG_TRYLOCK(lock);
296 bc_vec_pushByte(v, '\0');
298 BC_SIG_TRYUNLOCK(lock);
301 #if BC_ENABLE_HISTORY
302 void bc_vec_replaceAt(BcVec *restrict v, size_t idx, const void *data) {
306 BC_SIG_ASSERT_LOCKED;
310 ptr = bc_vec_item(v, idx);
312 if (v->dtor) bc_vec_dtors[v->dtor](ptr);
314 memcpy(ptr, data, v->size);
316 #endif // BC_ENABLE_HISTORY
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;
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);
328 inline void bc_vec_clear(BcVec *restrict v) {
329 BC_SIG_ASSERT_LOCKED;
332 v->dtor = BC_DTOR_NONE;
335 void bc_vec_free(void *vec) {
336 BcVec *v = (BcVec*) vec;
337 BC_SIG_ASSERT_LOCKED;
342 #if !BC_ENABLE_LIBRARY
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.
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.
353 static size_t bc_map_find(const BcVec *restrict v, const char *name) {
355 size_t low = 0, high = v->len;
359 size_t mid = (low + high) / 2;
360 const BcId *id = bc_vec_item(v, mid);
361 int result = strcmp(name, id->name);
363 if (!result) return mid;
364 else if (result < 0) high = mid;
371 bool bc_map_insert(BcVec *restrict v, const char *name,
372 size_t idx, size_t *restrict i)
377 BC_SIG_ASSERT_LOCKED;
379 assert(v != NULL && name != NULL && i != NULL);
381 *i = bc_map_find(v, name);
383 assert(*i <= v->len);
385 if (*i != v->len && !strcmp(name, ((BcId*) bc_vec_item(v, *i))->name))
389 slabs = BC_IS_DC ? &vm.main_slabs : &vm.other_slabs;
391 slabs = &vm.main_slabs;
394 id.name = bc_slabvec_strdup(slabs, name);
397 bc_vec_pushAt(v, &id, *i);
402 size_t bc_map_index(const BcVec *restrict v, const char *name) {
406 assert(v != NULL && name != NULL);
408 i = bc_map_find(v, name);
410 // If out of range, return invalid.
411 if (i >= v->len) return BC_VEC_INVALID_IDX;
413 // Make sure the item exists.
414 return strcmp(name, ((BcId*) bc_vec_item(v, i))->name) ?
415 BC_VEC_INVALID_IDX : i;
419 const char* bc_map_name(const BcVec *restrict v, size_t idx) {
421 size_t i, len = v->len;
423 for (i = 0; i < len; ++i) {
424 BcId* id = (BcId*) bc_vec_item(v, i);
425 if (id->idx == idx) return id->name;
435 * Initializes a single slab.
436 * @param s The slab to initialize.
438 static void bc_slab_init(BcSlab *s) {
439 s->s = bc_vm_malloc(BC_SLAB_SIZE);
444 * Adds a string to a slab and returns a pointer to it, or NULL if it could not
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
452 static char* bc_slab_add(BcSlab *s, const char *str, size_t len) {
458 assert(len == strlen(str) + 1);
460 if (s->len + len > BC_SLAB_SIZE) return NULL;
462 ptr = (char*) (s->s + s->len);
464 bc_strcpy(ptr, len, str);
471 void bc_slab_free(void *slab) {
472 free(((BcSlab*) slab)->s);
475 void bc_slabvec_init(BcVec* v) {
481 bc_vec_init(v, sizeof(BcSlab), BC_DTOR_SLAB);
483 // We always want to have at least one slab.
484 slab = bc_vec_pushEmpty(v);
488 char* bc_slabvec_strdup(BcVec *v, const char *str) {
495 BC_SIG_ASSERT_LOCKED;
497 assert(v != NULL && v->len);
501 len = strlen(str) + 1;
503 // If the len is greater than 128, then just allocate it with malloc.
504 if (BC_UNLIKELY(len >= BC_SLAB_SIZE)) {
506 // SIZE_MAX is a marker for these standalone allocations.
508 slab.s = bc_vm_strdup(str);
510 // Push the standalone slab.
511 bc_vec_pushAt(v, &slab, v->len - 1);
517 slab_ptr = bc_vec_top(v);
518 s = bc_slab_add(slab_ptr, str, len);
520 // If it couldn't be added, add a slab and try again.
521 if (BC_UNLIKELY(s == NULL)) {
523 slab_ptr = bc_vec_pushEmpty(v);
524 bc_slab_init(slab_ptr);
526 s = bc_slab_add(slab_ptr, str, len);
534 void bc_slabvec_clear(BcVec *v) {
539 // This complicated loop exists because of standalone allocations over 128
543 // Get the first slab.
544 s = bc_vec_item(v, 0);
546 // Either the slab must be valid (not standalone), or there must be
548 assert(s->len != SIZE_MAX || v->len > 1);
550 // Do we have to loop again? We do if it's a standalone allocation.
551 again = (s->len == SIZE_MAX);
553 // Pop the standalone allocation, not the one after it.
554 if (again) bc_vec_npopAt(v, 1, 0);
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);
562 // Empty the first slab.
565 #endif // !BC_ENABLE_LIBRARY
569 void bc_slabvec_print(BcVec *v, const char *func) {
574 bc_file_printf(&vm.ferr, "%s\n", func);
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);
582 bc_file_puts(&vm.ferr, bc_flush_none, "\n");
583 bc_file_flush(&vm.ferr, bc_flush_none);
586 #endif // BC_DEBUG_CODE