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