├── .github └── workflows │ └── c-cpp.yml ├── LICENSE ├── Makefile ├── README.md ├── lib └── .gitignore ├── src ├── algebraic.h ├── array.h ├── core.c ├── core.h ├── cppmap.h ├── list.c ├── list.h ├── maybe.h ├── mdarray.h ├── mem.h ├── namespace.h ├── nat.c ├── nat.h ├── rb3ptr.c ├── rb3ptr.h ├── rbtree.h ├── span.c ├── span.h ├── string.c ├── string.h ├── using.h ├── variadic.h ├── vec.c └── vec.h ├── test_mdarray.c └── tests ├── list.c ├── maybe.c ├── span.c ├── string.c ├── variadic.c └── vec.c /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: update 17 | run: sudo apt-get install gcc make 18 | - name: make 19 | run: make 20 | - name: make test 21 | run: make test 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2025. Martin Uecker. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2023. Martin Uecker 2 | # All rights reserved. Use of this source code is governed by 3 | # a BSD-style license which can be found in the LICENSE file. 4 | 5 | CC ?= gcc 6 | CFLAGS = -O2 -g -std=gnu17 -Wall -Wextra -D_GNU_SOURCE 7 | LDFLAGS = 8 | 9 | ifeq ($(findstring clang,$(CC)),clang) 10 | CFLAGS += -fsanitize=vla-bound,bounds,null,signed-integer-overflow -fsanitize-undefined-trap-on-error -fblocks 11 | LDFLAGS+= -lBlocksRuntime 12 | else 13 | CFLAGS += -fsanitize=vla-bound,bounds-strict,null,signed-integer-overflow -fsanitize-undefined-trap-on-error 14 | endif 15 | 16 | CPPFLAGS= -iquote ./src/ 17 | 18 | SRCS = $(wildcard src/*.c) 19 | 20 | .INTERMEDIATE: $(SRCS:.c=.o) 21 | 22 | (%): % 23 | $(AR) $(ARFLAGS) $@ $% 24 | 25 | lib/libnoplate.a: lib/libnoplate.a($(SRCS:.c=.o)) 26 | 27 | test: tests/list tests/maybe tests/span tests/string tests/vec tests/variadic 28 | # execstack -c test 29 | 30 | tests/list: tests/list.c lib/libnoplate.a 31 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ 32 | tests/list 33 | 34 | tests/maybe: tests/maybe.c lib/libnoplate.a 35 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ 36 | tests/maybe 37 | 38 | tests/span: tests/span.c lib/libnoplate.a 39 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ 40 | tests/span 41 | 42 | tests/string: tests/string.c lib/libnoplate.a 43 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ 44 | tests/string 45 | 46 | tests/vec: tests/vec.c lib/libnoplate.a 47 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ 48 | tests/vec 49 | 50 | tests/variadic: tests/variadic.c lib/libnoplate.a 51 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ 52 | tests/variadic 53 | 54 | test_mdarray: test_mdarray.c lib/libnoplate.a 55 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -Wno-missing-braces -o $@ $^ 56 | # execstack -c test 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | noplate library 3 | =============== 4 | 5 | *experimental* generic type-safe container data structures for C 6 | 7 | 8 | Generic types provided are: vec, span, list, product, sum, and 9 | maybe. There is also a string type build on top of it, facilities 10 | for safe slicing of arrays, and for storing the byte representation 11 | of types in a buffer. The generic types are based on structures defined 12 | in macros. They take typedef names as arguments and define a generic 13 | container with this element type. For example: 14 | 15 | vec(int)* vi; // pointer to a vector of ints 16 | 17 | 18 | Note that the type argument must be a single identifier, i.e. to 19 | use it with more complicated types, a typedef name must be defined 20 | first. 21 | 22 | There is also support for arrays and strings. Arrays are just 23 | regular C arrays but we provide functions such as array_slice 24 | that perform bounds checking. A string is a vector of char 25 | which also has null-termination. 26 | 27 | 28 | All container types preserve information about the type of 29 | the elements and also have bounds checking, i.e. out-of-bounds 30 | accesses trap at run-time (with UBSan). (NOTE: EXPERIMENTAL, 31 | this is certainly not safe at this point!) 32 | 33 | 34 | Interoperability works by providing access to the underlying 35 | elements. For example, vec_access provides access to an 36 | lvalue for the underlying element of a vector and vec2array 37 | provides access to an array representing the complete vector. 38 | See example 9 how where a span is created from a vector and 39 | a pointer to the underlying array is passed to a C function. 40 | 41 | Array views constructed by vec2array or span2array are VLAs, 42 | so accesses are bounds-checked when using UBSan. A string 43 | type is defined as vector of char with null termination. The 44 | underlying array can be passed as an argument to C functions, 45 | because the array decays to a pointer. 46 | 47 | Before C23, one has to add forward declarations of the types, 48 | e.g. vec_decl(int). 49 | 50 | 51 | Example 1 (vector of integers) 52 | ------------------------------ 53 | 54 | https://godbolt.org/z/4n7vE3jTh 55 | 56 | vec_decl(int); 57 | vec(int)* v = vec_alloc(int); 58 | 59 | vec_push(int, &v, 1); 60 | vec_push(int, &v, 3); 61 | 62 | vec_access(int, v, 1)++; 63 | 64 | vec_access(int, v, 10) = 1; // run-time error! 65 | 66 | free(v); 67 | 68 | 69 | Example 2 (span) 70 | ---------------- 71 | 72 | https://godbolt.org/z/TvxseshGc 73 | 74 | void sum(span(int) v) 75 | { 76 | int sum = 0; 77 | for (int i = 0; i < span_length(&v); i++) 78 | sum += span_access(int, &v, i); 79 | return sum; 80 | } 81 | 82 | vec(int)* v = vec_alloc(int); 83 | 84 | int s = sum(vec2span(int, v)); 85 | 86 | 87 | Example 3 (strings) 88 | ------------------- 89 | 90 | https://godbolt.org/z/MMKEdG3or 91 | 92 | string *a = string_init("abc"); 93 | string *b = string_printf("%d", 3); 94 | string *c = string_concat(a, b); 95 | 96 | printf("%s\n", string_cstr(c)); 97 | 98 | assert(string_length(c) + 1 == sizeof(string_cstr(c))); 99 | 100 | 101 | Example 4 (strview) 102 | ------------------- 103 | 104 | string s = ... 105 | strview v = string_view(s); 106 | 107 | printf("%s\n", string_cstr(&v)); 108 | 109 | 110 | Example 5 (maybe) 111 | ----------------- 112 | 113 | https://godbolt.org/z/3WsWzfcKK 114 | 115 | maybe(int) divide(int a, int b) 116 | { 117 | return (b != 0) ? maybe_ok(int, a / b) : maybe_fail(int); 118 | } 119 | 120 | 121 | Example 6 (product and sum) 122 | ------------------------------ 123 | 124 | https://godbolt.org/z/9bh6sPc3K 125 | 126 | typedef product(int, float) product_name(int, float); 127 | 128 | auto ifl = product_init(int, float, (3, 0.1)); 129 | auto si = sum_init(product_name(int, float), int, ifl); 130 | 131 | NESTED(int, add1, (product(int, float) ifl)) 132 | { 133 | return product_car(ifl) + 1; 134 | }; 135 | 136 | NESTED(int, id, (int x)) 137 | { 138 | return x; 139 | }; 140 | 141 | int si2 = sum_choice(si, add1, id); 142 | 143 | assert(4 == si2); 144 | 145 | 146 | 147 | Example 7 (vector of strings) 148 | ----------------------------- 149 | 150 | https://godbolt.org/z/hhT98vM7Y 151 | 152 | typedef string* strptr; 153 | vec(strptr)* s = vec_alloc(strptr); 154 | 155 | vec_push(strptr, &s, string_init(" Du!")); 156 | vec_push(strptr, &s, string_init("Hallo")); 157 | 158 | int cmp2(const strptr* a, const strptr* b) 159 | { 160 | return strcmp(string_cstr(*a), string_cstr(*b)); 161 | } 162 | 163 | vec_sort(strptr, s, cmp2); 164 | 165 | while (0 < vec_length(s)) 166 | free(vec_pop(strptr, &s)); 167 | 168 | free(s); 169 | 170 | 171 | 172 | Example 8 (array slice) 173 | ----------------------- 174 | 175 | https://godbolt.org/z/YqnKTT6ds 176 | 177 | char str[] = "Hallo"; 178 | auto slice = &array_slice(str, 1, 1 + 3); 179 | (*slice)[0] = 'A'; 180 | (*slice)[1] = 'L'; 181 | (*slice)[2] = 'L'; 182 | 183 | 184 | Example 9 (span + slice) 185 | ------------------------ 186 | 187 | https://godbolt.org/z/hjMaM3K3Y 188 | 189 | 190 | Example 10 (byte-level loads and stores) 191 | --------------------------------------- 192 | 193 | https://godbolt.org/z/xeYfn17zG 194 | 195 | char buf[sizeof(int)]; 196 | poke(int, &buf, 33); 197 | int i = peek(int, &buf); 198 | 199 | 200 | Example 11 (preorder tree traversal) 201 | ------------------------------------ 202 | 203 | https://godbolt.org/z/sjbT453dM 204 | 205 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /src/algebraic.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NOPLATE_ALGEBRAIC_H 3 | #define _NOPLATE_ALGEBRAIC_H 1 4 | 5 | #include "core.h" 6 | 7 | 8 | #define product_name(S, T) CONCAT(product_, CONCAT(CONCAT(S, _), T)) 9 | #define sum_name(S, T) CONCAT(sum_, CONCAT(CONCAT(S, _), T)) 10 | 11 | #ifdef TAGCOMPAT 12 | #define product(S, T) struct product_name(S, T) { S car; T cdr; } 13 | #define product_decl(S, T) 14 | #else 15 | #define product(S, T) struct product_name(S, T) 16 | #define product_decl(S, T) product(S, T) { S car; T cdr; } 17 | #endif 18 | 19 | #define remove_bracket(...) __VA_ARGS__ 20 | #define product_init(S, T, x) ((product(S, T)){ remove_bracket x }) 21 | #define product_car(x) ((x).car) 22 | #define product_cdr(x) ((x).cdr) 23 | 24 | 25 | #ifdef TAGCOMPAT 26 | #define sum(S, T) struct sum_name(S, T) { bool sel; union { S car; T cdr; }; } 27 | #define sum_decl(S, T) 28 | #else 29 | #define sum(S, T) struct sum_name(S, T) 30 | #define sum_decl(S, T) sum(S, T) { bool sel; union { S car; T cdr; }; } 31 | #endif 32 | 33 | #define sum_init(S, T, x) \ 34 | ({ \ 35 | auto _x = (x); \ 36 | sum(S, T) _n; \ 37 | *_Generic(_x, \ 38 | typeof(_n.car): (_n.sel = false, &_n.car), \ 39 | typeof(_n.cdr): (_n.sel = true, &_n.cdr) \ 40 | ) = _x; \ 41 | _n; \ 42 | }) 43 | 44 | #define _sum_car(id, x) ({ auto id = (x); CHECK(!id.sel); id.car; }) 45 | #define _sum_cdr(id, x) ({ auto id = (x); CHECK( id.sel); id.cdr; }) 46 | 47 | #define sum_car(x) _sum_car(__UNIQ, x) 48 | #define sum_cdr(x) _sum_cdr(__UNIQ, x) 49 | 50 | #define _sum_choice(id, x, f, g) ({ auto id = (x); (!id.sel) ? (f((sum_car(id)))) : (g((sum_cdr(id)))); }) 51 | #define sum_choice(x, f, g) _sum_choice(__UNIQ, x, f, g) 52 | 53 | 54 | #endif // _NOPLATE_ALGEBRAIC_H 55 | 56 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | 7 | #include "core.h" 8 | 9 | #define array(T, N) typeof(typeof(T)[N]) 10 | 11 | #if (GCC_VERSION >= 160000) && !defined __clang__ 12 | #define array_lengthof(x) _Countof(x) 13 | #else 14 | #define array_lengthof(x) \ 15 | (sizeof *({ \ 16 | auto __x = &(x); \ 17 | (void)TYPE_CHECK(typeof((*__x)[0])(*)[], __x); \ 18 | char (*__tmp)[(sizeof(*__x) / sizeof((*__x)[0]))] = 0; \ 19 | __tmp; \ 20 | })) 21 | #endif 22 | 23 | #define array_slice(x, start, end) \ 24 | (*({ \ 25 | auto __y = &(x); \ 26 | ssize_t __start = (start); \ 27 | ssize_t __end = (end); \ 28 | CHECK(__start >= 0); \ 29 | CHECK(__end >= __start); \ 30 | CHECK((size_t)__end <= array_lengthof(*__y)); \ 31 | (array(typeof((*__y)[0]), __end - __start)*)&(*__y)[__start]; \ 32 | })) 33 | 34 | 35 | #define array2span(T, x) \ 36 | ({ auto __y = &(x); \ 37 | (span(T)){ array_lengthof(*__y), &(*__y)[0] }; \ 38 | }) 39 | 40 | #define array_cast(S, x) \ 41 | *({ \ 42 | auto __y = &(x); \ 43 | typedef typeof(S) __S; \ 44 | CHECK(sizeof(__S) == sizeof((*__y)[0])); \ 45 | (__S(*)[array_lengthof(*__y)])__y; \ 46 | }) 47 | 48 | #define ptr2array(N, p) \ 49 | *({ \ 50 | auto __p = (p); \ 51 | auto __N = (N); \ 52 | size_t s = __builtin_dynamic_object_size(__p, 0); \ 53 | typedef typeof(*__p) eltype; \ 54 | CHECK(s != (size_t)-1); \ 55 | CHECK(sizeof(eltype[__N]) <= s); \ 56 | (eltype (*)[__N])__p; \ 57 | }) 58 | 59 | 60 | // note, this can not fail for non-arrays 61 | #define _array_element(x) \ 62 | (*({ \ 63 | auto _q = &(x); \ 64 | &choose_expr(function_or_array_p(*_q), (*_q), nil(int[1]))[0]; \ 65 | })) 66 | 67 | 68 | // FIXME: should fail if not an error 69 | #define array_eltype(x) typeof(_array_element(x)) 70 | 71 | 72 | 73 | #define _array_eltype9(T) choose_type(function_or_array_p(T), struct _mdarray_error, typeof(T)) 74 | #define _array_eltype8(T) choose_type(function_or_array_p(T), _array_eltype9(nil(array_eltype(T))), typeof(T)) 75 | #define _array_eltype7(T) choose_type(function_or_array_p(T), _array_eltype8(nil(array_eltype(T))), typeof(T)) 76 | #define _array_eltype6(T) choose_type(function_or_array_p(T), _array_eltype7(nil(array_eltype(T))), typeof(T)) 77 | #define _array_eltype5(T) choose_type(function_or_array_p(T), _array_eltype6(nil(array_eltype(T))), typeof(T)) 78 | #define _array_eltype4(T) choose_type(function_or_array_p(T), _array_eltype5(nil(array_eltype(T))), typeof(T)) 79 | #define _array_eltype3(T) choose_type(function_or_array_p(T), _array_eltype4(nil(array_eltype(T))), typeof(T)) 80 | #define _array_eltype2(T) choose_type(function_or_array_p(T), _array_eltype3(nil(array_eltype(T))), typeof(T)) 81 | #define _array_eltype1(T) choose_type(function_or_array_p(T), _array_eltype2(nil(array_eltype(T))), typeof(T)) 82 | #define _array_eltype0(T) choose_type(function_or_array_p(T), _array_eltype1(nil(array_eltype(T))), typeof(T)) 83 | #define array_nested_eltype(X) typeof(*({ auto _p = &(X); typedef _array_eltype0((*_p)) _eltype; nil(_eltype*); })) 84 | 85 | #define _array_rank9(T) choose_ice(function_or_array_p(T), 1, 1) 86 | #define _array_rank8(T) choose_ice(function_or_array_p(T), 1 + _array_rank9(nil(array_eltype(T))), 1) 87 | #define _array_rank7(T) choose_ice(function_or_array_p(T), 1 + _array_rank8(nil(array_eltype(T))), 1) 88 | #define _array_rank6(T) choose_ice(function_or_array_p(T), 1 + _array_rank7(nil(array_eltype(T))), 1) 89 | #define _array_rank5(T) choose_ice(function_or_array_p(T), 1 + _array_rank6(nil(array_eltype(T))), 1) 90 | #define _array_rank4(T) choose_ice(function_or_array_p(T), 1 + _array_rank5(nil(array_eltype(T))), 1) 91 | #define _array_rank3(T) choose_ice(function_or_array_p(T), 1 + _array_rank4(nil(array_eltype(T))), 1) 92 | #define _array_rank2(T) choose_ice(function_or_array_p(T), 1 + _array_rank3(nil(array_eltype(T))), 1) 93 | #define _array_rank1(T) choose_ice(function_or_array_p(T), 1 + _array_rank2(nil(array_eltype(T))), 1) 94 | #define _array_rank0(T) choose_ice(function_or_array_p(T), 1 + _array_rank1(nil(array_eltype(T))), 1) 95 | #define array_rank(X) (sizeof(*({ auto _p = &(X); enum { _N = _array_rank0((*_p)) }; nil(char(*)[_N]); })) - 1) 96 | 97 | #define _array_dims0(T) 98 | #define _array_dims1(T) sizeof(T) / sizeof(_array_element(T)) 99 | #define _array_dims2(T) _array_dims1(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 100 | #define _array_dims2(T) _array_dims1(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 101 | #define _array_dims3(T) _array_dims2(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 102 | #define _array_dims4(T) _array_dims3(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 103 | #define _array_dims5(T) _array_dims4(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 104 | #define _array_dims6(T) _array_dims5(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 105 | #define _array_dims7(T) _array_dims6(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 106 | #define _array_dims8(T) _array_dims7(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 107 | #define _array_dims9(T) _array_dims8(_array_element(T)), sizeof(T) / sizeof(_array_element(T)) 108 | #define array_dims(R, T) _array_dims ## R(T) 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/core.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "core.h" 5 | 6 | 7 | extern void xfree(const void* x) 8 | { 9 | free((void*)x); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | #ifndef GCC_VERSION 13 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 14 | #endif 15 | 16 | #if __STDC_VERSION__ >= 202000 17 | #if (GCC_VERSION >= 140000) || (__clang_major__ > 20) 18 | #define TAGCOMPAT 19 | #endif 20 | #endif 21 | 22 | #if (GCC_VERSION >= 150000) && !defined __clang__ 23 | #define COUNTED_BY(N) gnu::counted_by(N) 24 | #else 25 | #define COUNTED_BY(N) 26 | #endif 27 | 28 | 29 | #define _CONCAT1(A, B) A ## B 30 | #define CONCAT(A, B) _CONCAT1(A, B) 31 | 32 | #ifndef auto 33 | #define auto __auto_type 34 | #endif 35 | 36 | #ifndef typeof 37 | #define typeof(X) __typeof(X) 38 | #endif 39 | 40 | #define compound_literal(T, x) (struct { T data; }){ x }.data 41 | 42 | #define _Ptr(x) typeof(typeof(x)*) 43 | #define _Array(x, N) typeof(typeof(x)[N]) 44 | 45 | 46 | 47 | #define TYPE_CHECK(T, x) _Generic((x), T: (x)) 48 | #define containerof(x, T, member) \ 49 | ((T*)((char*)TYPE_CHECK(typeof(&((T*)0)->member), x) - offsetof(T, member))) 50 | 51 | #define nil(T) ((typeof(T)){ 0 }) 52 | #define nil2(T) ((typeof(T)){ { 0 } }) 53 | 54 | #define CONST(x) (struct { const typeof(x) data; }){ x }.data 55 | 56 | #define NODECL(T) typeof(({ nil(T); })) 57 | 58 | //#define nil(T) (*((typeof(T)*){ 0 })) 59 | #define same_type_p(T, x) _Generic(nil(typeof(x)*), typeof(T)*: 1, default: 0) 60 | #define same_type_unq_p(T, x) _Generic(nil(typeof(x)*), typeof(T)*: 1, const typeof(T)*: 1, default: 0) 61 | #define has_type_p(T, x) _Generic(nil(typeof(x)*), typeof(T)*: 1, default: 0) 62 | 63 | // seems to work on both clang and gcc 64 | #define lvalue_convert(x) ((void)0, x) 65 | 66 | #define function_or_array_p(T) \ 67 | ( (!has_type_p(lvalue_convert(T), T)) \ 68 | && (!has_type_p(const typeof(lvalue_convert(T)), T))) 69 | 70 | #define choose_expr(c, A, B) (_Generic(&(int[1 + !!(c)]){ 0 }, int(*)[2]: (A), int(*)[1]: (B))) 71 | #define choose_type(c, A, B) typeof(*_Generic(&(int[1 + !!(c)]){ 0 }, int(*)[2]: (typeof(A)*)0, int(*)[1]: (typeof(B)*)0)) 72 | #define choose_ice(c, A, B) sizeof(choose_type(c, nil(char[(A)]), nil(char[(B)]))) 73 | 74 | #if defined(__clang__) && !defined(__CUDACC__) 75 | #if __has_extension(blocks) 76 | #define NESTED(RET, NAME, ARGS) \ 77 | RET (^NAME)ARGS = ^ARGS 78 | #define CLOSURE_TYPE(x) (^x) 79 | #endif 80 | #else 81 | #define NESTED(RET, NAME, ARGS) \ 82 | RET NAME ARGS 83 | #define CLOSURE_TYPE(x) (*x) 84 | #define __block 85 | #endif 86 | 87 | #define __CONCAT0(x, y) x ## y 88 | #define __CONCAT1(x, y) __CONCAT0(x, y) 89 | #define __UNIQ CONCAT(__x, __COUNTER__) 90 | 91 | #if 0 92 | // -fsanitize=bounds,null 93 | #define CHECK(x) 94 | #else 95 | #define CHECK(x) if (!(x)) abort() 96 | #endif 97 | 98 | extern void xfree(const void*); 99 | 100 | -------------------------------------------------------------------------------- /src/cppmap.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2015. The Regents of the University of California. 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * 5 | * Authors: 6 | * 2015 Martin Uecker 7 | */ 8 | 9 | 10 | // some classic C preprocessor hackery 11 | 12 | /* Ideas from: 13 | * 14 | * https://github.com/swansontec/map-macro 15 | * https://github.com/pfultz2/Cloak/wiki/Is-the-C-preprocessor-Turing-complete%3F 16 | */ 17 | 18 | #define EMPTY() 19 | #define DEFER1(...) __VA_ARGS__ EMPTY() 20 | #define DEFER2(...) __VA_ARGS__ DEFER1(EMPTY)() 21 | #define DEFER3(...) __VA_ARGS__ DEFER2(EMPTY)() 22 | 23 | #define EXPAND6(...) __VA_ARGS__ 24 | #define EXPAND5(...) EXPAND6(EXPAND6(__VA_ARGS__)) 25 | #define EXPAND4(...) EXPAND5(EXPAND5(__VA_ARGS__)) 26 | #define EXPAND3(...) EXPAND4(EXPAND4(__VA_ARGS__)) 27 | #define EXPAND2(...) EXPAND3(EXPAND3(__VA_ARGS__)) 28 | #define EXPAND1(...) EXPAND2(EXPAND2(__VA_ARGS__)) 29 | #define EXPAND0(...) EXPAND1(EXPAND1(__VA_ARGS__)) 30 | #define EXPAND(...) EXPAND0(EXPAND0(__VA_ARGS__)) 31 | 32 | #define CAT0(x, y) x ## y 33 | #define CAT(x, y) CAT0(x, y) 34 | #define NIL_TEST() DUMMY, TRUE, 35 | #define RET2ND0(a, b, ...) b 36 | #define RET2ND(...) RET2ND0(__VA_ARGS__) 37 | #define NIL_P(x) RET2ND(NIL_TEST x, FALSE) 38 | #define IF_TRUE(a, b) a 39 | #define IF_FALSE(a, b) b 40 | #define IF(x, a, b) CAT(IF_, x)(a, b) 41 | #define MAP1() MAP0 42 | #define MAP0(f, arg, a, b, ...) f(arg, a) IF(NIL_P(b), , DEFER3(MAP1)()(f, arg, b, __VA_ARGS__)) 43 | #define MAP(f, arg, ...) EXPAND(MAP0(f, arg, __VA_ARGS__, ())) 44 | 45 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2023. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | 7 | #include "list.h" 8 | 9 | extern inline bool (list_empty)(const struct list* h); 10 | extern inline void (list_push)(struct list* h, struct list_node* n); 11 | extern inline struct list_node* (list_pop)(struct list* h); 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #ifndef _NOPLATE_LIST_H 7 | #define _NOPLATE_LIST_H 1 8 | 9 | #include 10 | #include 11 | 12 | #include "core.h" 13 | 14 | struct list_node { 15 | 16 | struct list_node* next; 17 | }; 18 | 19 | 20 | struct list { 21 | 22 | struct list_node* head; 23 | }; 24 | 25 | 26 | inline void list_push(struct list* h, struct list_node* n) 27 | { 28 | n->next = h->head; 29 | h->head = n; 30 | } 31 | 32 | inline struct list_node* list_pop(struct list* h) 33 | { 34 | struct list_node* t = h->head; 35 | 36 | if (NULL != t) 37 | h->head = t->next; 38 | 39 | return t; 40 | } 41 | 42 | inline bool list_empty(const struct list* h) 43 | { 44 | return (NULL == h->head); 45 | } 46 | 47 | #define _list_data(T) typeof(T[1]) 48 | 49 | #ifdef TAGCOMPAT 50 | #define list_node(T) struct CONCAT(list_node_, T) { struct list_node node; _list_data(T) data; } 51 | #define list(T) struct CONCAT(list_, T) { union { struct list list; list_node(T) *dummy; }; } 52 | #else 53 | #define list_node(T) struct CONCAT(list_node_, T) 54 | #define list_node_decl(T) list_node(T) { struct list_node node; _list_data(T) data; } 55 | 56 | #define list(T) struct CONCAT(list_, T) 57 | #define list_decl(T) list_node_decl(T); list(T) { union { struct list list; list_node(T) *dummy; }; } 58 | #endif 59 | 60 | #define list_eltype(l) typeof((__L)->dummy[0].data[0]) 61 | #define list_node_type(l) typeof((__L)->dummy[0]) 62 | 63 | #define list_empty(l) ((list_empty)(&((l)->list))) 64 | 65 | #define list_first(l) \ 66 | ({ \ 67 | auto __L = (l); \ 68 | typedef list_node_type(__L) __NT; \ 69 | \ 70 | containerof(__L->list.head, __NT, node); \ 71 | }) 72 | 73 | #define list_node_access(n) ((n)->data[0]) 74 | #define list_node_next(n) \ 75 | ({ \ 76 | auto __N = (n); \ 77 | \ 78 | (NULL == __N->node.next) ? NULL : \ 79 | containerof(__N->node.next, typeof(*__N), node); \ 80 | }) 81 | 82 | #define list_push(l, v) \ 83 | ({ \ 84 | auto __L = (l); \ 85 | list_eltype(__L) __v = (v); \ 86 | typedef list_node_type(__L) __NT; \ 87 | \ 88 | __NT* __n2 = malloc(sizeof(__NT)); \ 89 | if (NULL == __n2) \ 90 | abort(); \ 91 | __n2->data[0] = __v; \ 92 | (list_push)(&__L->list, &__n2->node); \ 93 | }) 94 | 95 | #define list_pop(l) \ 96 | ({ \ 97 | auto __L = (l); \ 98 | typedef list_node_type(__L) __NT; \ 99 | if (list_empty(__L)) \ 100 | abort(); \ 101 | \ 102 | struct list_node* __n = (list_pop)(&__L->list); \ 103 | __NT* __n2 = containerof(__n, __NT, node); \ 104 | list_eltype(__L) el = __n2->data[0]; \ 105 | free(__n2); \ 106 | el; \ 107 | }) 108 | 109 | #endif 110 | 111 | -------------------------------------------------------------------------------- /src/maybe.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NOPLATE_MAYBE_H 3 | #define _NOPLATE_MAYBE_H 1 4 | 5 | #include "core.h" 6 | 7 | #ifdef TAGCOMPAT 8 | #define maybe(T) struct CONCAT(maybe_, T) { bool ok; T value; } 9 | #define maybe_decl(T) 10 | #else 11 | #define maybe(T) struct CONCAT(maybe_, T) 12 | #define maybe_decl(T) maybe(T) { bool ok; T value; } 13 | #endif 14 | 15 | #define maybe_ok(T, x) (maybe(T)){ .value = (x), .ok = true } 16 | #define maybe_fail(T) (maybe(T)){ .value = (T){ 0 }, .ok = false } 17 | #define maybe_just(T, x) (*({ maybe(T) *__p = &(x); __p->ok ? &__p->value : (void*)0; })) 18 | 19 | #endif // _NOPLATE_MAYBE_H 20 | 21 | -------------------------------------------------------------------------------- /src/mdarray.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2022. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #ifndef _NOPLATE_MDARRAY_H 7 | #define _NOPLATE_MDARRAY_H 1 8 | 9 | #include "array.h" 10 | 11 | #ifdef TAGCOMPAT 12 | #define mdarray(R, T) struct CONCAT(mdarray_, CONCAT(CONCAT(R, _), T)) { T* data; size_t dims[R]; } 13 | #define mdarray_decl(R, T) 14 | #else 15 | #define mdarray(R, T) struct CONCAT(mdarray_, CONCAT(CONCAT(R, _), T)) 16 | #define mdarray_decl(R, T) mdarray(R, T) { T* data; size_t dims[R]; } 17 | #endif 18 | 19 | 20 | #define mdarray_rank(X) (array_lengthof((X).dims)) 21 | #define mdarray_eltype(X) typeof((X).data[0]) 22 | #define mdarray_dim(X, i) ((X).dims[i]) 23 | 24 | struct _mdarray_error { char c; }; 25 | #define _mdarray_type9(T, R, dims) choose_type(R, struct _mdarray_error, T) 26 | #define _mdarray_type8(T, R, dims) choose_type(R, array(_mdarray_type9(T, (R) - 1, dims), dims[(R) - 1]), T) 27 | #define _mdarray_type7(T, R, dims) choose_type(R, array(_mdarray_type8(T, (R) - 1, dims), dims[(R) - 1]), T) 28 | #define _mdarray_type6(T, R, dims) choose_type(R, array(_mdarray_type7(T, (R) - 1, dims), dims[(R) - 1]), T) 29 | #define _mdarray_type5(T, R, dims) choose_type(R, array(_mdarray_type6(T, (R) - 1, dims), dims[(R) - 1]), T) 30 | #define _mdarray_type4(T, R, dims) choose_type(R, array(_mdarray_type5(T, (R) - 1, dims), dims[(R) - 1]), T) 31 | #define _mdarray_type3(T, R, dims) choose_type(R, array(_mdarray_type4(T, (R) - 1, dims), dims[(R) - 1]), T) 32 | #define _mdarray_type2(T, R, dims) choose_type(R, array(_mdarray_type3(T, (R) - 1, dims), dims[(R) - 1]), T) 33 | #define _mdarray_type1(T, R, dims) choose_type(R, array(_mdarray_type2(T, (R) - 1, dims), dims[(R) - 1]), T) 34 | #define _mdarray_type0(T, R, dims) choose_type(R, array(_mdarray_type1(T, (R) - 1, dims), dims[(R) - 1]), T) 35 | #define _mdarray_type(T, R, dims) choose_type(R, array(_mdarray_type0(T, (R) - 1, dims), dims[(R) - 1]), T) 36 | 37 | #if (GCC_VERSION >= 110300) || defined __clang__ 38 | #define mdarray_array(X) \ 39 | (*({ \ 40 | auto _p = &(X); \ 41 | typedef mdarray_eltype(*_p) _eltype; \ 42 | enum { _rank = mdarray_rank(*_p) }; \ 43 | (_mdarray_type(_eltype, _rank, _p->dims)*)_p->data; \ 44 | })) 45 | #else 46 | // GCC: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91038 47 | #error "GCC too old" 48 | #endif 49 | 50 | 51 | // FIXME 52 | #define mdarray_init(R, T, X) (mdarray(R, T)){ (void*)&X, array_dims(R, (X)) } 53 | 54 | #endif // _NOPLATE_MDARRAY_H 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/mem.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NOPLATE_MEM_H 3 | #define _NOPLATE_MEM_H 1 4 | 5 | #include "core.h" 6 | 7 | #define _LDST_MEMCPY \ 8 | extern void *memcpy(void *dest, const void *src, typeof(sizeof(0)) n) 9 | 10 | #define peek(T, ptr) \ 11 | ({ \ 12 | typedef T _T; \ 13 | enum { _N = sizeof(_T) }; \ 14 | auto _ptr1 = (ptr); \ 15 | CHECK(_N == sizeof(*_ptr1)); \ 16 | char (*_ptr)[_N] = _ptr1; \ 17 | _LDST_MEMCPY; \ 18 | *(_T*)memcpy(&(_T){ 0 }, _ptr, _N); \ 19 | }) 20 | 21 | #define poke(T, ptr, x) \ 22 | ({ \ 23 | T _x = (x); \ 24 | enum { _N = sizeof(_x) }; \ 25 | auto _ptr1 = (ptr); \ 26 | CHECK(_N == sizeof(*_ptr1)); \ 27 | char (*_ptr)[_N] = _ptr1; \ 28 | _LDST_MEMCPY; \ 29 | memcpy(_ptr, &_x, _N); \ 30 | _x; \ 31 | }) 32 | 33 | 34 | #endif // _NOPLATE_MEM_H 35 | 36 | -------------------------------------------------------------------------------- /src/namespace.h: -------------------------------------------------------------------------------- 1 | 2 | #include "cppmap.h" 3 | 4 | #define NAMESPACE_PREFIX(ns, x) ns ## _ ## x 5 | #define NAMESPACE_ENTRY(ns, x) typeof(NAMESPACE_PREFIX(ns, x)) *x; 6 | #define NAMESPACE_INIT(ns, x) .x = &NAMESPACE_PREFIX(ns, x), 7 | #define NAMESPACE_MAKE(S, N) \ 8 | static const struct \ 9 | { S(N, NAMESPACE_ENTRY) } N = { S(N, NAMESPACE_INIT) } 10 | #define NAMESPACE_IMPORT(X, Y) const static typeof(X) Y = X 11 | 12 | 13 | 14 | // version 2 15 | 16 | #define NAMESPACE(S, ...) \ 17 | static const struct { MAP(NAMESPACE_ENTRY, S, __VA_ARGS__) } S = { MAP(NAMESPACE_INIT, S, __VA_ARGS__) } 18 | 19 | // const does not work with GCC here, PR116456 20 | #define using(S, ...) \ 21 | (struct { MAP(NAMESPACE_ENTRY, S, __VA_ARGS__) }){ MAP(NAMESPACE_INIT, S, __VA_ARGS__) } 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/nat.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2023. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | 8 | #include "string.h" 9 | 10 | #include "nat.h" 11 | 12 | _Static_assert((nat_base_t)-1 > (nat_base_t)0, "base type must be unsigned"); 13 | 14 | #define bitsof(t) (CHAR_BIT * sizeof(t)) 15 | #define maxof(t) ((1ULL << (bitsof(t) - 1ULL)) - 1ULL) 16 | 17 | extern inline nat nat_alloc(void); 18 | extern inline int net_length(const nat x); 19 | 20 | nat nat_init(int i) 21 | { 22 | if (i < 0) 23 | return NULL; 24 | 25 | nat x = vec_alloc_n(nat_base_t, 1); 26 | 27 | if (NULL == x) 28 | goto err; 29 | 30 | vec_access(nat_base_t, x, 0) = i; 31 | err: 32 | return x; 33 | } 34 | 35 | nat nat_dup(const nat x) 36 | { 37 | nat n = vec_alloc_n(nat_base_t, vec_length(nat_base_t, x)); 38 | 39 | if (NULL == n) 40 | goto err; 41 | 42 | memcpy( vec2array(nat_base_t, n), 43 | vec2array(nat_base_t, x), 44 | vec_sizeof(nat_base_t, x)); 45 | 46 | err: 47 | return n; 48 | } 49 | 50 | extern nat nat_mul(const nat a, const nat b) 51 | { 52 | int A = vec_length(nat_base_t, a); 53 | int B = vec_length(nat_base_t, b); 54 | 55 | nat p = vec_calloc_n(nat_base_t, A + B); 56 | 57 | if (NULL == p) 58 | goto err; 59 | 60 | typedef unsigned long nat_ext_t; 61 | _Static_assert(sizeof(nat_ext_t) == 2 * sizeof(nat_base_t), ""); 62 | 63 | for (int i = 0; i < A; i++) { 64 | 65 | nat_base_t carry = 0; 66 | 67 | for (int j = 0; j < B; j++) { 68 | 69 | nat_ext_t aa = vec_access(nat_base_t, a, i); 70 | nat_ext_t bb = vec_access(nat_base_t, b, j); 71 | 72 | nat_ext_t tmp = carry + aa * bb; 73 | 74 | carry = tmp >> bitsof(nat_base_t); 75 | 76 | vec_access(nat_base_t, p, i + j) += tmp; 77 | } 78 | 79 | vec_access(nat_base_t, p, i + B) = carry; 80 | } 81 | err: 82 | return p; 83 | } 84 | 85 | 86 | extern nat nat_add(const nat a, const nat b) 87 | { 88 | int A = vec_length(nat_base_t, a); 89 | int B = vec_length(nat_base_t, b); 90 | 91 | nat p = vec_alloc_n(nat_base_t, ((A > B) ? A : B) + 1); 92 | 93 | if (NULL == p) 94 | goto err; 95 | 96 | int C = vec_length(nat_base_t, p); 97 | 98 | nat_base_t carry = 0; 99 | 100 | for (int i = 0; i < C; i++) { 101 | 102 | nat_base_t aa = (i < A) ? vec_access(nat_base_t, a, i) : 0; 103 | nat_base_t bb = (i < B) ? vec_access(nat_base_t, b, i) : 0; 104 | 105 | vec_access(nat_base_t, p, i) = (nat_base_t)(aa + bb + carry); // wraps 106 | 107 | if (carry) 108 | carry = (aa >= maxof(nat_base_t) - bb); 109 | else 110 | carry = (aa > maxof(nat_base_t) - bb); 111 | } 112 | 113 | if (!carry) { 114 | 115 | // assert(0 == vec_access(p, C - 1)); 116 | vec_pop(nat_base_t, &p); 117 | } 118 | 119 | err: 120 | return p; 121 | } 122 | 123 | 124 | extern nat nat_div(const nat a, const nat b); 125 | extern nat nat_sub(const nat a, const nat b); 126 | 127 | 128 | 129 | string* nat_2string(const nat x) 130 | { 131 | int X = vec_length(nat_base_t, x); 132 | // int N = (X * bitsof(nat_base_t) + 2) / 3; 133 | 134 | char tmp[X * bitsof(nat_base_t) + 1]; 135 | int l = 0; 136 | 137 | for (int i = X - 1; i >= 0; i--) { 138 | 139 | nat_base_t v = vec_access(nat_base_t, x, i); 140 | 141 | for (int j = bitsof(nat_base_t) - 1; j >= 0; j--) { 142 | 143 | int bit = (v >> j) & 1; 144 | 145 | tmp[l++] = bit ? '1' : '0'; 146 | } 147 | } 148 | 149 | tmp[l] = '\0'; 150 | 151 | return string_init(tmp); 152 | } 153 | 154 | 155 | -------------------------------------------------------------------------------- /src/nat.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2022. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #ifndef __NAT_H 7 | #define __NAT_H 8 | 9 | #include "vec.h" 10 | 11 | typedef unsigned int nat_base_t; 12 | vec_decl(nat_base_t); 13 | 14 | typedef vec(nat_base_t)* nat; 15 | 16 | 17 | inline nat nat_alloc(void) 18 | { 19 | return vec_alloc(nat_base_t); 20 | } 21 | 22 | inline int net_length(const nat x) 23 | { 24 | return vec_length(nat_base_t, x); 25 | } 26 | 27 | extern nat nat_init(int x); 28 | 29 | #define NAT(x) (nat_init(x)) 30 | 31 | extern nat nat_dup(const nat x); 32 | 33 | extern nat nat_mul(const nat a, const nat b); 34 | extern nat nat_div(const nat a, const nat b); 35 | extern nat nat_add(const nat a, const nat b); 36 | extern nat nat_sub(const nat a, const nat b); 37 | 38 | #ifndef STRING 39 | vec_decl(char); 40 | typedef vec(char)* string; 41 | #endif 42 | 43 | extern string* nat_2string(const nat x); 44 | 45 | 46 | #endif // __NAT_H 47 | 48 | -------------------------------------------------------------------------------- /src/rb3ptr.c: -------------------------------------------------------------------------------- 1 | /* rb3ptr -- Intrusively linked 3-pointer Red-black tree implementation */ 2 | 3 | /* Copyright (C) 2019, Jens Stimpfle */ 4 | 5 | /* 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the 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, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "rb3ptr.h" 25 | #include // offsetof() 26 | 27 | enum { 28 | _RB3_DIR_BIT = 1 << 0, 29 | _RB3_COLOR_BIT = 1 << 1, 30 | _RB3_BLACK = 0, 31 | _RB3_RED = _RB3_COLOR_BIT, 32 | }; 33 | 34 | static inline rb3_ptr rb3_child_ptr(struct rb3_head *head, int color) 35 | { 36 | return (rb3_ptr) head | color; 37 | } 38 | 39 | static inline rb3_ptr rb3_parent_ptr(struct rb3_head *head, int dir) 40 | { 41 | return (rb3_ptr) head | dir; 42 | } 43 | 44 | static inline struct rb3_head *rb3_get_black_child(struct rb3_head *head, int dir) 45 | { 46 | return (struct rb3_head *) head->child[dir]; 47 | } 48 | 49 | static inline int rb3_get_color_bit(struct rb3_head *head, int dir) 50 | { 51 | return head->child[dir] & _RB3_COLOR_BIT; 52 | } 53 | 54 | static inline int rb3_is_red(struct rb3_head *head, int dir) 55 | { 56 | return rb3_get_color_bit(head, dir) != 0; 57 | } 58 | 59 | static inline void rb3_set_red(struct rb3_head *head, int dir) 60 | { 61 | head->child[dir] |= _RB3_COLOR_BIT; 62 | } 63 | 64 | static inline void rb3_set_black(struct rb3_head *head, int dir) 65 | { 66 | head->child[dir] &= ~_RB3_COLOR_BIT; 67 | } 68 | 69 | static inline void rb3_connect(struct rb3_head *head, int dir, struct rb3_head *child, int color) 70 | { 71 | head->child[dir] = rb3_child_ptr(child, color); 72 | child->parent = rb3_parent_ptr(head, dir); 73 | } 74 | 75 | static inline void rb3_connect_null(struct rb3_head *head, int dir, struct rb3_head *child, int color) 76 | { 77 | head->child[dir] = rb3_child_ptr(child, color); 78 | if (child) 79 | child->parent = rb3_parent_ptr(head, dir); 80 | } 81 | 82 | struct rb3_tree *rb3_get_containing_tree(struct rb3_head *head) 83 | { 84 | while (rb3_get_parent(head)) 85 | head = rb3_get_parent(head); 86 | return (struct rb3_tree *) ((char *) head - (offsetof(struct rb3_head, child[0]))); 87 | } 88 | 89 | static struct rb3_head *rb3_get_minmax_in_subtree(struct rb3_head *head, int dir) 90 | { 91 | if (!head) 92 | return _RB3_NULL; 93 | while (rb3_has_child(head, dir)) 94 | head = rb3_get_child(head, dir); 95 | return head; 96 | } 97 | 98 | struct rb3_head *rb3_get_minmax(struct rb3_tree *tree, int dir) 99 | { 100 | return rb3_get_minmax_in_subtree(rb3_get_root(tree), dir); 101 | } 102 | 103 | struct rb3_head *rb3_get_prevnext_descendant(struct rb3_head *head, int dir) 104 | { 105 | return rb3_get_minmax_in_subtree(rb3_get_child(head, dir), !dir); 106 | } 107 | 108 | struct rb3_head *rb3_get_prevnext_ancestor(struct rb3_head *head, int dir) 109 | { 110 | /* 111 | * Note: the direction is "reversed" for our purposes here, since 112 | * the bit indicates the direction from the parent to `head` 113 | */ 114 | while (head && rb3_get_parent_dir(head) == dir) { 115 | head = rb3_get_parent(head); 116 | } 117 | if (head) { 118 | head = rb3_get_parent(head); 119 | if (!head || rb3_is_base(head)) 120 | return _RB3_NULL; 121 | return head; 122 | } 123 | return _RB3_NULL; 124 | } 125 | 126 | struct rb3_head *rb3_get_prevnext(struct rb3_head *head, int dir) 127 | { 128 | if (rb3_has_child(head, dir)) 129 | return rb3_get_prevnext_descendant(head, dir); 130 | else 131 | return rb3_get_prevnext_ancestor(head, dir); 132 | } 133 | 134 | void rb3_update_augment(struct rb3_head *head, rb3_augment_func *augment) 135 | { 136 | while (!rb3_is_base(head)) { 137 | augment(head); 138 | head = rb3_get_parent(head); 139 | } 140 | } 141 | 142 | static void rb3_rebalance_after_link(struct rb3_head *head, rb3_augment_func *augment) 143 | { 144 | struct rb3_head *pnt; 145 | struct rb3_head *gpnt; 146 | struct rb3_head *ggpnt; 147 | int left; 148 | int right; 149 | int gdir; 150 | int ggdir; 151 | 152 | if (!rb3_get_parent(rb3_get_parent(head))) { 153 | rb3_set_black(rb3_get_parent(head), RB3_LEFT); 154 | if (augment) 155 | augment(head); 156 | return; 157 | } 158 | 159 | if (!rb3_is_red(rb3_get_parent(rb3_get_parent(head)), rb3_get_parent_dir(rb3_get_parent(head)))) { 160 | /* parent is black */ 161 | if (augment) 162 | rb3_update_augment(head, augment); 163 | return; 164 | } 165 | 166 | /* 167 | * Since parent is red parent can't be the root. 168 | * So we have at least a grandparent node, and grand-grandparent 169 | * is either a real node or the base head. 170 | */ 171 | pnt = rb3_get_parent(head); 172 | gpnt = rb3_get_parent(pnt); 173 | ggpnt = rb3_get_parent(gpnt); 174 | left = rb3_get_parent_dir(head); 175 | right = !rb3_get_parent_dir(head); 176 | gdir = rb3_get_parent_dir(pnt); 177 | ggdir = rb3_get_parent_dir(gpnt); 178 | 179 | if (rb3_is_red(gpnt, !gdir)) { 180 | /* uncle and parent are both red */ 181 | rb3_set_red(ggpnt, ggdir); 182 | rb3_set_black(gpnt, RB3_LEFT); 183 | rb3_set_black(gpnt, RB3_RIGHT); 184 | if (augment) 185 | rb3_update_augment(head, augment); 186 | rb3_rebalance_after_link(gpnt, augment); 187 | } else if (gdir == right) { 188 | rb3_connect_null(pnt, left, rb3_get_black_child(head, right), _RB3_BLACK); 189 | rb3_connect_null(gpnt, right, rb3_get_black_child(head, left), _RB3_BLACK); 190 | rb3_connect(head, left, gpnt, _RB3_RED); 191 | rb3_connect(head, right, pnt, _RB3_RED); 192 | rb3_connect(ggpnt, ggdir, head, _RB3_BLACK); 193 | if (augment) { 194 | augment(pnt); 195 | augment(gpnt); 196 | rb3_update_augment(head, augment); 197 | } 198 | } else { 199 | rb3_connect_null(gpnt, left, rb3_get_black_child(pnt, right), _RB3_BLACK); 200 | rb3_connect(pnt, right, gpnt, _RB3_RED); 201 | rb3_connect(ggpnt, ggdir, pnt, _RB3_BLACK); 202 | if (augment) { 203 | augment(gpnt); 204 | rb3_update_augment(head, augment); 205 | } 206 | } 207 | } 208 | 209 | static void rb3_rebalance_after_unlink(struct rb3_head *pnt, int pdir, rb3_augment_func *augment) 210 | { 211 | struct rb3_head *gpnt; 212 | struct rb3_head *sibling; 213 | struct rb3_head *sleft; 214 | struct rb3_head *sleftleft; 215 | struct rb3_head *sleftright; 216 | enum rb3_dir left; 217 | enum rb3_dir right; 218 | enum rb3_dir gdir; 219 | 220 | if (!rb3_get_parent(pnt)) 221 | return; 222 | 223 | left = pdir; // define "left" as the direction from parent to deleted node 224 | right = !pdir; 225 | gpnt = rb3_get_parent(pnt); 226 | gdir = rb3_get_parent_dir(pnt); 227 | sibling = rb3_get_child(pnt, right); 228 | sleft = rb3_get_child(sibling, left); 229 | 230 | if (rb3_is_red(pnt, right)) { 231 | /* sibling is red */ 232 | rb3_connect(pnt, right, sleft, _RB3_BLACK); 233 | rb3_connect(sibling, left, pnt, _RB3_RED); 234 | rb3_connect(gpnt, gdir, sibling, _RB3_BLACK); 235 | if (augment) 236 | augment(sleft); 237 | rb3_rebalance_after_unlink(pnt, pdir, augment); 238 | } else if (rb3_is_red(sibling, right)) { 239 | /* outer child of sibling is red */ 240 | rb3_connect_null(pnt, right, sleft, rb3_get_color_bit(sibling, left)); 241 | rb3_connect(sibling, left, pnt, _RB3_BLACK); 242 | rb3_connect(gpnt, gdir, sibling, rb3_get_color_bit(gpnt, gdir)); 243 | if (augment) { 244 | rb3_update_augment(pnt, augment); 245 | } 246 | rb3_set_black(sibling, right); 247 | } else if (rb3_is_red(sibling, left)) { 248 | /* inner child of sibling is red */ 249 | sleftleft = rb3_get_child(sleft, left); 250 | sleftright = rb3_get_child(sleft, right); 251 | rb3_connect_null(pnt, right, sleftleft, _RB3_BLACK); 252 | rb3_connect_null(sibling, left, sleftright, _RB3_BLACK); 253 | rb3_connect(sleft, left, pnt, _RB3_BLACK); 254 | rb3_connect(sleft, right, sibling, _RB3_BLACK); 255 | rb3_connect(gpnt, gdir, sleft, rb3_get_color_bit(gpnt, gdir)); 256 | if (augment) { 257 | augment(sibling); 258 | rb3_update_augment(pnt, augment); 259 | } 260 | } else if (rb3_is_red(gpnt, gdir)) { 261 | /* parent is red */ 262 | rb3_set_red(pnt, right); 263 | rb3_set_black(gpnt, gdir); 264 | if (augment) 265 | rb3_update_augment(pnt, augment); 266 | } else { 267 | /* all relevant nodes are black */ 268 | rb3_set_red(pnt, right); 269 | if (augment) 270 | augment(pnt); 271 | rb3_rebalance_after_unlink(gpnt, gdir, augment); 272 | } 273 | } 274 | 275 | void rb3_link_and_rebalance_and_maybe_augment(struct rb3_head *head, struct rb3_head *parent, int dir, rb3_augment_func *augment) 276 | { 277 | _RB3_ASSERT(dir == RB3_LEFT || dir == RB3_RIGHT); 278 | _RB3_ASSERT(!rb3_has_child(parent, dir)); 279 | 280 | parent->child[dir] = rb3_child_ptr(head, _RB3_RED); 281 | head->parent = rb3_parent_ptr(parent, dir); 282 | head->child[RB3_LEFT] = rb3_child_ptr(_RB3_NULL, _RB3_BLACK); 283 | head->child[RB3_RIGHT] = rb3_child_ptr(_RB3_NULL, _RB3_BLACK); 284 | rb3_rebalance_after_link(head, augment); 285 | } 286 | 287 | void rb3_replace_and_maybe_augment(struct rb3_head *head, struct rb3_head *newhead, rb3_augment_func *augment) 288 | { 289 | struct rb3_head *left; 290 | struct rb3_head *right; 291 | struct rb3_head *parent; 292 | int pdir; 293 | int pcol; 294 | 295 | *newhead = *head; 296 | 297 | left = rb3_get_child(head, RB3_LEFT); 298 | right = rb3_get_child(head, RB3_RIGHT); 299 | parent = rb3_get_parent(head); 300 | pdir = rb3_get_parent_dir(head); 301 | pcol = rb3_get_color_bit(parent, pdir); 302 | 303 | if (left) 304 | left->parent = rb3_parent_ptr(newhead, RB3_LEFT); 305 | if (right) 306 | right->parent = rb3_parent_ptr(newhead, RB3_RIGHT); 307 | parent->child[pdir] = rb3_child_ptr(newhead, pcol); 308 | 309 | if (augment) 310 | rb3_update_augment(newhead, augment); 311 | } 312 | 313 | static void rb3_unlink_noninternal_and_rebalance_and_maybe_augment(struct rb3_head *head, rb3_augment_func *augment) 314 | { 315 | struct rb3_head *pnt; 316 | struct rb3_head *cld; 317 | int pdir; 318 | int dir; 319 | 320 | dir = rb3_get_child(head, RB3_RIGHT) ? RB3_RIGHT : RB3_LEFT; 321 | pnt = rb3_get_parent(head); 322 | cld = rb3_get_child(head, dir); 323 | pdir = rb3_get_parent_dir(head); 324 | 325 | int mustRebalance = !rb3_is_red(pnt, pdir) && !rb3_is_red(head, dir); 326 | 327 | /* since we added the possibility for augmentation, 328 | we need to remove `head` *before* the rebalancing that we do below. 329 | (Otherwise the augmentation function would still see the to-be-deleted child). */ 330 | rb3_connect_null(pnt, pdir, cld, _RB3_BLACK); 331 | 332 | if (mustRebalance) 333 | /* To be deleted node is black (and child cannot be repainted) 334 | * => height decreased */ 335 | rb3_rebalance_after_unlink(pnt, pdir, augment); 336 | else if (augment) 337 | /* the augment wasn't done since we didn't rebalance. So we need to do it separately. 338 | TODO: Could we restrict the augmentation done during rebalancing to just the 339 | nodes that aren't not be augmented by a regular rb3_augment_ancestors(pnt, augment)? */ 340 | rb3_update_augment(pnt, augment); 341 | } 342 | 343 | static void rb3_unlink_internal_and_rebalance_and_maybe_augment(struct rb3_head *head, rb3_augment_func *augment) 344 | { 345 | struct rb3_head *subst; 346 | 347 | subst = rb3_get_next_descendant(head); 348 | rb3_unlink_noninternal_and_rebalance_and_maybe_augment(subst, augment); 349 | rb3_replace_and_maybe_augment(head, subst, augment); 350 | } 351 | 352 | void rb3_unlink_and_rebalance_and_maybe_augment(struct rb3_head *head, rb3_augment_func *augment) 353 | { 354 | if (rb3_has_child(head, RB3_LEFT) && rb3_has_child(head, RB3_RIGHT)) 355 | rb3_unlink_internal_and_rebalance_and_maybe_augment(head, augment); 356 | else 357 | rb3_unlink_noninternal_and_rebalance_and_maybe_augment(head, augment); 358 | } 359 | 360 | struct rb3_head *rb3_find_parent_in_subtree(struct rb3_head *parent, int dir, rb3_cmp cmp, void *data, struct rb3_head **parent_out, int *dir_out) 361 | { 362 | return rb3_INLINE_find(parent, dir, cmp, data, parent_out, dir_out); 363 | } 364 | 365 | struct rb3_head *rb3_insert(struct rb3_tree *tree, struct rb3_head *head, rb3_cmp cmp, void *data) 366 | { 367 | struct rb3_head *found; 368 | struct rb3_head *parent; 369 | int dir; 370 | 371 | parent = rb3_get_base(tree); 372 | dir = RB3_LEFT; 373 | found = rb3_find_parent_in_subtree(parent, dir, cmp, data, &parent, &dir); 374 | if (found) 375 | return found; 376 | rb3_link_and_rebalance(head, parent, dir); 377 | return _RB3_NULL; 378 | } 379 | 380 | struct rb3_head *rb3_delete(struct rb3_tree *tree, rb3_cmp cmp, void *data) 381 | { 382 | struct rb3_head *found; 383 | 384 | found = rb3_find(tree, cmp, data); 385 | if (found) { 386 | rb3_unlink_and_rebalance(found); 387 | return found; 388 | } 389 | return _RB3_NULL; 390 | } 391 | 392 | struct rb3_head *rb3_find_parent(struct rb3_tree *tree, rb3_cmp cmp, void *data, struct rb3_head **parent_out, int *dir_out) 393 | { 394 | return rb3_find_parent_in_subtree(rb3_get_base(tree), RB3_LEFT, cmp, data, parent_out, dir_out); 395 | } 396 | 397 | struct rb3_head *rb3_find(struct rb3_tree *tree, rb3_cmp cmp, void *data) 398 | { 399 | return rb3_find_parent_in_subtree(rb3_get_base(tree), RB3_LEFT, cmp, data, _RB3_NULL, _RB3_NULL); 400 | } 401 | 402 | void rb3_link_and_rebalance(struct rb3_head *head, struct rb3_head *parent, int dir) 403 | { 404 | rb3_link_and_rebalance_and_maybe_augment(head, parent, dir, _RB3_NULL); 405 | } 406 | 407 | void rb3_unlink_and_rebalance(struct rb3_head *head) 408 | { 409 | rb3_unlink_and_rebalance_and_maybe_augment(head, _RB3_NULL); 410 | } 411 | 412 | void rb3_replace(struct rb3_head *head, struct rb3_head *newhead) 413 | { 414 | rb3_replace_and_maybe_augment(head, newhead, _RB3_NULL); 415 | } 416 | 417 | void rb3_link_and_rebalance_and_augment(struct rb3_head *head, struct rb3_head *parent, int dir, rb3_augment_func *augment) 418 | { 419 | rb3_link_and_rebalance_and_maybe_augment(head, parent, dir, augment); 420 | } 421 | 422 | void rb3_unlink_and_rebalance_and_augment(struct rb3_head *head, rb3_augment_func *augment) 423 | { 424 | rb3_unlink_and_rebalance_and_maybe_augment(head, augment); 425 | } 426 | 427 | void rb3_replace_and_augment(struct rb3_head *head, struct rb3_head *newhead, rb3_augment_func *augment) 428 | { 429 | rb3_replace_and_maybe_augment(head, newhead, augment); 430 | } 431 | 432 | 433 | /* DEBUG STUFF */ 434 | 435 | #include 436 | static void visit_inorder_helper(struct rb3_head *head, int isred) 437 | { 438 | if (!head) 439 | return; 440 | printf(" ("); 441 | visit_inorder_helper(rb3_get_child(head, RB3_LEFT), rb3_is_red(head, RB3_LEFT)); 442 | printf("%s", isred ? "R" : "B"); 443 | visit_inorder_helper(rb3_get_child(head, RB3_RIGHT), rb3_is_red(head, RB3_RIGHT)); 444 | printf(")"); 445 | } 446 | 447 | static void visit_inorder(struct rb3_tree *tree) 448 | { 449 | visit_inorder_helper(rb3_get_root(tree), 0); 450 | printf("\n"); 451 | } 452 | 453 | static int rb3_is_valid_tree_helper(struct rb3_head *head, int isred, int dir, int *depth) 454 | { 455 | int i; 456 | int depths[2] = { 1,1 }; 457 | 458 | *depth = 1; 459 | 460 | if (!head) { 461 | if (isred) { 462 | printf("red leaf child!\n"); 463 | return 0; 464 | } 465 | return 1; 466 | } 467 | 468 | if (rb3_get_parent_dir(head) != dir) { 469 | printf("Directions messed up!\n"); 470 | return 0; 471 | } 472 | 473 | for (i = 0; i < 2; i++) { 474 | if (isred && rb3_get_color_bit(head, i)) { 475 | printf("two red in a row!\n"); 476 | return 0; 477 | } 478 | if (!rb3_is_valid_tree_helper(rb3_get_child(head, i), 479 | rb3_is_red(head, i), i, &depths[i])) 480 | return 0; 481 | } 482 | if (depths[0] != depths[1]) { 483 | printf("Unbalanced tree! got %d and %d\n", depths[0], depths[1]); 484 | return 0; 485 | } 486 | *depth = depths[0] + !isred; 487 | 488 | return 1; 489 | } 490 | 491 | int rb3_check_tree(struct rb3_tree *tree) 492 | { 493 | int depth; 494 | int valid; 495 | 496 | if (rb3_is_red(&tree->base, RB3_LEFT)) { 497 | printf("Error! root is red.\n"); 498 | return 0; 499 | } 500 | 501 | valid = rb3_is_valid_tree_helper(rb3_get_root(tree), 0, 0, &depth); 502 | if (!valid) 503 | visit_inorder(tree); 504 | return valid; 505 | } 506 | 507 | extern inline void rb3_reset_head(struct rb3_head *head); 508 | extern inline void rb3_reset_tree(struct rb3_tree *tree); 509 | extern inline struct rb3_head *rb3_get_base(struct rb3_tree *tree); 510 | extern inline int rb3_is_base(struct rb3_head *head); 511 | extern inline int rb3_is_head_linked(struct rb3_head *head); 512 | extern inline struct rb3_head *rb3_get_child(struct rb3_head *head, int dir); 513 | extern inline int rb3_has_child(struct rb3_head *head, int dir); 514 | extern inline int rb3_get_parent_dir(struct rb3_head *head); 515 | extern inline struct rb3_head *rb3_get_parent(struct rb3_head *head); 516 | extern inline struct rb3_head *rb3_get_root(struct rb3_tree *tree); 517 | extern inline int rb3_is_empty(struct rb3_tree *tree); 518 | extern inline struct rb3_head *rb3_get_min(struct rb3_tree *tree); 519 | extern inline struct rb3_head *rb3_get_max(struct rb3_tree *tree); 520 | extern inline struct rb3_head *rb3_get_prev(struct rb3_head *head); 521 | extern inline struct rb3_head *rb3_get_next(struct rb3_head *head); 522 | extern inline struct rb3_head *rb3_get_prev_descendant(struct rb3_head *head); 523 | extern inline struct rb3_head *rb3_get_next_descendant(struct rb3_head *head); 524 | extern inline struct rb3_head *rb3_get_prev_ancestor(struct rb3_head *head); 525 | extern inline struct rb3_head *rb3_get_next_ancestor(struct rb3_head *head); 526 | extern inline struct rb3_head *rb3_INLINE_find(struct rb3_head *parent, int dir, rb3_cmp cmp, void *data, struct rb3_head **parent_out, int *dir_out); 527 | 528 | -------------------------------------------------------------------------------- /src/rb3ptr.h: -------------------------------------------------------------------------------- 1 | /* rb3ptr -- Intrusively linked 3-pointer Red-black tree implementation */ 2 | 3 | /* Copyright (C) 2019, Jens Stimpfle */ 4 | 5 | /* 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the 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, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifdef RB3PTR_H_INCLUDED 25 | #error rb3ptr.h included twice! 26 | #endif 27 | #define RB3PTR_H_INCLUDED 28 | 29 | #ifndef UINTPTR_MAX /* detect stdint.h */ 30 | #include /* uintptr_t */ 31 | #endif 32 | 33 | #ifndef _RB3_ASSERT 34 | #ifndef assert 35 | #include 36 | #endif 37 | #define _RB3_ASSERT(x) assert(x) 38 | #endif 39 | 40 | #ifndef _RB3_NULL 41 | #ifndef NULL 42 | #include 43 | #endif 44 | #define _RB3_NULL NULL 45 | #endif 46 | 47 | 48 | #ifdef __cplusplus // not yet tested 49 | extern "C" { 50 | #endif 51 | 52 | 53 | /** 54 | * Directions for navigation in the tree. 55 | */ 56 | enum rb3_dir { 57 | RB3_LEFT = 0, 58 | RB3_RIGHT = 1, 59 | }; 60 | 61 | /** 62 | * This type is used to efficiently store a pointer (at least 4-byte aligned) 63 | * and some more information in the unused low bits. 64 | */ 65 | typedef uintptr_t rb3_ptr; 66 | 67 | /** 68 | * Node type for 3-pointer Red-black trees. 69 | * Contains left, right, and parent pointers. 70 | * The left and right pointers have additional color bits. 71 | * The parent pointer contains a direction bit indicating the direction 72 | * to this child. 73 | */ 74 | struct rb3_head { 75 | rb3_ptr child[2]; 76 | rb3_ptr parent; 77 | }; 78 | 79 | /** 80 | * Tree type. It's just a fake base head that is wrapped for type safety and 81 | * future extensibility. 82 | */ 83 | struct rb3_tree { 84 | struct rb3_head base; 85 | }; 86 | 87 | /** 88 | * User-provided comparison function. It is used during tree searches. 89 | * At each visited node, the function is called with that node as first 90 | * argument and some additional user-provided data. 91 | * 92 | * It should returns a value less than, equal to, or greater than, 0, 93 | * depending on whether the node compares less than, equal to, or greater 94 | * than, the user-provided data. 95 | */ 96 | typedef int rb3_cmp(struct rb3_head *head, void *data); 97 | 98 | /** 99 | * User-provided augment function. Used to do recomputations when a child changed. 100 | */ 101 | typedef void rb3_augment_func(struct rb3_head *head /*, void *data */); 102 | 103 | /** 104 | * Initialize an rb3_head. 105 | * After initialization, rb3_is_head_linked() will return false. 106 | */ 107 | inline void rb3_reset_head(struct rb3_head *head) 108 | { 109 | head->child[RB3_LEFT] = 0; 110 | head->child[RB3_RIGHT] = 0; 111 | head->parent = 0; 112 | } 113 | 114 | /** 115 | * Initialize an rb3_tree. 116 | */ 117 | inline void rb3_reset_tree(struct rb3_tree *tree) 118 | { 119 | tree->base.child[RB3_LEFT] = 0; 120 | /* ! see doc of rb3_is_base(). */ 121 | #if 0 122 | tree->base.child[RB3_RIGHT] = 3; 123 | #else 124 | tree->base.child[RB3_RIGHT] = 0; 125 | #endif 126 | tree->base.parent = 0; 127 | } 128 | 129 | /** 130 | * Get base head of tree. 131 | * 132 | * Warning: the base head is never embedded in a client payload structure. 133 | * It's just a link to host the real root of the tree as its left child. 134 | */ 135 | inline struct rb3_head *rb3_get_base(struct rb3_tree *tree) 136 | { 137 | return &tree->base; 138 | } 139 | 140 | /** 141 | * Test if given head is base of tree. 142 | */ 143 | inline int rb3_is_base(struct rb3_head *head) 144 | { 145 | #if 1 146 | return 0 == head->parent; 147 | #else 148 | /* We could check for the parent pointer being null, but by having 149 | * a special sentinel right child value instead, we can make this 150 | * function distinguish the base from unlinked pointers as well. 151 | * 152 | * A side effect is that this breaks programs with trees that are not 153 | * initialized with rb3_init(), which could be a good or a bad thing, 154 | * I don't know. */ 155 | return head->child[RB3_RIGHT] == 3; 156 | #endif 157 | } 158 | 159 | /** 160 | * Check if a non-base head is linked in a (any) tree. 161 | */ 162 | inline int rb3_is_head_linked(struct rb3_head *head) 163 | { 164 | return head->parent != 0; 165 | } 166 | 167 | /** 168 | * Get child in given direction, or NULL if there is no such child. `dir` 169 | * must be RB3_LEFT or RB3_RIGHT. 170 | */ 171 | inline struct rb3_head *rb3_get_child(struct rb3_head *head, int dir) 172 | { 173 | return (struct rb3_head *)((head->child[dir]) & ~3); 174 | } 175 | 176 | /* 177 | * Test if a (left or right) child exists. 178 | * This is slightly more efficient than calling rb3_get_child() and comparing 179 | * to NULL. 180 | */ 181 | inline int rb3_has_child(struct rb3_head *head, int dir) 182 | { 183 | return head->child[dir] != 0; 184 | } 185 | 186 | /** 187 | * Get direction from parent to child by testing the direction. 188 | * 189 | * Return RB3_LEFT or RB3_RIGHT, depending on whether this node is the left or 190 | * right child of its parent node. If the given node is the root node, 191 | * RB3_LEFT is returned. (Technically the root node is the left child of the 192 | * base node). 193 | * 194 | * This is more convenient and (in theory) more efficient than getting the 195 | * parent and testing its left and right child. 196 | */ 197 | inline int rb3_get_parent_dir(struct rb3_head *head) 198 | { 199 | return head->parent & 1; 200 | } 201 | 202 | /** 203 | * Get parent head, or NULL if given node is the base head. 204 | * 205 | * Note that normally you don't want to visit the base head but stop already 206 | * at the root node. 207 | */ 208 | inline struct rb3_head *rb3_get_parent(struct rb3_head *head) 209 | { 210 | return (struct rb3_head *)(head->parent & ~3); 211 | } 212 | 213 | /** 214 | * Get topmost element of tree (or NULL if empty) 215 | */ 216 | inline struct rb3_head *rb3_get_root(struct rb3_tree *tree) 217 | { 218 | return rb3_get_child(&tree->base, RB3_LEFT); 219 | } 220 | 221 | /** 222 | * Check if tree is empty. 223 | */ 224 | inline int rb3_is_empty(struct rb3_tree *tree) 225 | { 226 | struct rb3_head *base = rb3_get_base(tree); 227 | return !rb3_has_child(base, RB3_LEFT); 228 | } 229 | 230 | /** 231 | * Get minimum or maximum node in the tree, depending on the value of `dir` 232 | * (RB3_LEFT or RB3_RIGHT) 233 | * 234 | * Time complexity: O(log n) 235 | */ 236 | extern struct rb3_head *rb3_get_minmax(struct rb3_tree *tree, int dir); 237 | 238 | /** 239 | * Get minimum (leftmost) element, or NULL if tree is empty. 240 | * 241 | * Time complexity: O(log n) 242 | */ 243 | inline struct rb3_head *rb3_get_min(struct rb3_tree *tree) 244 | { 245 | return rb3_get_minmax(tree, RB3_LEFT); 246 | } 247 | 248 | /** 249 | * Get previous or next in-order descendant, depending on the value of `dir` 250 | * (RB3_LEFT or RB3_RIGHT). 251 | * 252 | * Time complexity: O(log n) 253 | */ 254 | extern struct rb3_head *rb3_get_prevnext_descendant(struct rb3_head *head, int dir); 255 | 256 | /** 257 | * Get previous or next in-order ancestor, depending on the value of `dir` 258 | * (RB3_LEFT or RB3_RIGHT). 259 | * 260 | * Time complexity: O(log n) 261 | */ 262 | extern struct rb3_head *rb3_get_prevnext_ancestor(struct rb3_head *head, int dir); 263 | 264 | /** 265 | * Get previous or next in-order node, depending on the value of `dir`. 266 | * 267 | * Time complexity: O(log n), amortized over sequential scan: O(1) 268 | */ 269 | extern struct rb3_head *rb3_get_prevnext(struct rb3_head *head, int dir); 270 | 271 | /** 272 | * Get maximum (rightmost) element, or NULL if tree is empty 273 | * 274 | * Time complexity: O(log n) 275 | */ 276 | inline struct rb3_head *rb3_get_max(struct rb3_tree *tree) 277 | { 278 | return rb3_get_minmax(tree, RB3_RIGHT); 279 | } 280 | 281 | /** 282 | * Get previous in-order node (maximal node in the tree that sorts before the 283 | * given element) or NULL if no such element is in the tree. 284 | * 285 | * Time complexity: O(log n), amortized over sequential scan: O(1) 286 | */ 287 | inline struct rb3_head *rb3_get_prev(struct rb3_head *head) 288 | { 289 | return rb3_get_prevnext(head, RB3_LEFT); 290 | } 291 | 292 | /** 293 | * Get next in-order node (minimal node in the tree that sorts after the given 294 | * element) or NULL if no such element is in the tree. 295 | * 296 | * Time complexity: O(log n), amortized over sequential scan: O(1) 297 | */ 298 | inline struct rb3_head *rb3_get_next(struct rb3_head *head) 299 | { 300 | return rb3_get_prevnext(head, RB3_RIGHT); 301 | } 302 | 303 | /** 304 | * Get previous in-order descendant (maximal descendant node that sorts before 305 | * the given element) or NULL if no such element is in the tree. 306 | * 307 | * Time complexity: O(log n) 308 | */ 309 | inline struct rb3_head *rb3_get_prev_descendant(struct rb3_head *head) 310 | { 311 | return rb3_get_prevnext_descendant(head, RB3_LEFT); 312 | } 313 | 314 | /** 315 | * Get next in-order descendant (minimal descendant node that sorts after the 316 | * given element) or NULL if no such element is in the tree. 317 | * 318 | * Time complexity: O(log n) 319 | */ 320 | inline struct rb3_head *rb3_get_next_descendant(struct rb3_head *head) 321 | { 322 | return rb3_get_prevnext_descendant(head, RB3_RIGHT); 323 | } 324 | 325 | /** 326 | * Get previous in-order ancestor (maximal ancestor node that sorts before the 327 | * given element) or NULL if no such element is in the tree. 328 | * 329 | * Time complexity: O(log n) 330 | */ 331 | inline struct rb3_head *rb3_get_prev_ancestor(struct rb3_head *head) 332 | { 333 | return rb3_get_prevnext_ancestor(head, RB3_LEFT); 334 | } 335 | 336 | /** 337 | * Get next in-order ancestor (minimal ancestor node that sorts after the 338 | * given element) or NULL if no such element is in the tree. 339 | * 340 | * Time complexity: O(log n) 341 | */ 342 | inline struct rb3_head *rb3_get_next_ancestor(struct rb3_head *head) 343 | { 344 | return rb3_get_prevnext_ancestor(head, RB3_RIGHT); 345 | } 346 | 347 | /** 348 | * Find a node in `tree` using `cmp` to direct the search. At each visited 349 | * node in the tree `cmp` is called with that node and `data` as arguments. 350 | * If a node that compares equal is found, it is returned. Otherwise, NULL is 351 | * returned. 352 | * 353 | * Time complexity: O(log n) 354 | */ 355 | extern struct rb3_head *rb3_find(struct rb3_tree *tree, rb3_cmp cmp, void *data); 356 | 357 | /** 358 | * Find a suitable insertion point for a new node in `tree` using `cmp` and 359 | * `data` to direct the search. At each visited node in the tree `cmp` is 360 | * called with that node and `data` as arguments. If a node that compares 361 | * equal is found, it is returned. Otherwise, NULL is returned and the 362 | * insertion point is returned as parent node and child direction in 363 | * `parent_out` and `dir_out`. 364 | * 365 | * Time complexity: O(log n) 366 | */ 367 | extern struct rb3_head *rb3_find_parent(struct rb3_tree *tree, rb3_cmp cmp, void *data, struct rb3_head **parent_out, int *dir_out); 368 | 369 | /** 370 | * Link `head` into `tree` below another node in the given direction (RB3_LEFT 371 | * or RB3_RIGHT). The new node must replace a leaf. You can use 372 | * rb3_find_parent() to find the insertion point. 373 | * 374 | * `head` must not be linked into another tree when this function is called. 375 | * 376 | * Time complexity: O(log n) 377 | */ 378 | extern void rb3_link_and_rebalance(struct rb3_head *head, struct rb3_head *parent, int dir); 379 | 380 | /** 381 | * Unlink `head` from its current tree. 382 | * 383 | * Time complexity: O(log n) 384 | */ 385 | extern void rb3_unlink_and_rebalance(struct rb3_head *head); 386 | 387 | /** 388 | * Replace `head` with `newhead`. `head` must be linked in a tree and 389 | * `newhead` must not be linked in a tree. 390 | */ 391 | extern void rb3_replace(struct rb3_head *head, struct rb3_head *newhead); 392 | 393 | /** 394 | * Like rb3_link_and_rebalance(), but call an augmentation function for each 395 | * subtree that has been changed. 396 | */ 397 | extern void rb3_link_and_rebalance_and_augment(struct rb3_head *head, struct rb3_head *parent, int dir, rb3_augment_func *augment); 398 | 399 | /** 400 | * Like rb3_unlink_and_rebalance(), but call an augmentation function for each 401 | * subtree that has been changed. 402 | */ 403 | extern void rb3_unlink_and_rebalance_and_augment(struct rb3_head *head, rb3_augment_func *augment); 404 | 405 | /** 406 | * Like rb3_replace(), but call an augmentation function for each subtree that has changed. 407 | */ 408 | extern void rb3_replace_and_augment(struct rb3_head *head, struct rb3_head *newhead, rb3_augment_func *augment); 409 | 410 | /** 411 | * Update by calling the augmentation func for `head` and all its ancestors. 412 | */ 413 | extern void rb3_update_augment(struct rb3_head *head, rb3_augment_func *augment); 414 | 415 | /** 416 | * Find suitable insertion point for a new node in a subtree, directed by the 417 | * given search function. The subtree is given by its parent node `parent` and 418 | * child direction `dir`. The insertion point and its child direction are 419 | * returned in `parent_out` and `dir_out`. 420 | * 421 | * If the searched node is already in the tree (the compare function returns 422 | * 0), it is returned. In this case `parent_out` and `dir_out` are left 423 | * untouched. Otherwise NULL is returned. 424 | */ 425 | extern struct rb3_head *rb3_find_parent_in_subtree(struct rb3_head *parent, int dir, rb3_cmp cmp, void *data, struct rb3_head **parent_out, int *dir_out); 426 | 427 | /** 428 | * Insert `head` into `tree` using `cmp` and `data` to direct the search. At 429 | * each visited node in the tree `cmp` is called with that node and `data` as 430 | * arguments (in that order). If a node that compares equal is found, it is 431 | * returned. Otherwise, `head` is inserted into the tree and NULL is 432 | * returned. 433 | * 434 | * Time complexity: O(log n) 435 | */ 436 | extern struct rb3_head *rb3_insert(struct rb3_tree *tree, struct rb3_head *head, rb3_cmp cmp, void *data); 437 | 438 | /** 439 | * Find and delete a node from `tree` using `cmp` to direct the search. At 440 | * each visited node in the tree `cmp` is called with that node and `head` as 441 | * arguments (in that order). If a node that compares equal is found, it is 442 | * unlinked from the tree and returned. Otherwise, NULL is returned. 443 | * 444 | * Time complexity: O(log n) 445 | */ 446 | extern struct rb3_head *rb3_delete(struct rb3_tree *tree, rb3_cmp cmp, void *data); 447 | 448 | /** 449 | * Given a node that is known to be linked in _some_ tree, find that tree. 450 | * 451 | * This involves a little hackery with offsetof(3) 452 | */ 453 | extern struct rb3_tree *rb3_get_containing_tree(struct rb3_head *head); 454 | 455 | 456 | /* 457 | XXX: is inlining the search function advantageous? 458 | */ 459 | inline struct rb3_head *rb3_INLINE_find(struct rb3_head *parent, int dir, rb3_cmp cmp, void *data, struct rb3_head **parent_out, int *dir_out) 460 | { 461 | _RB3_ASSERT(parent != _RB3_NULL); 462 | while (rb3_has_child(parent, dir)) { 463 | parent = rb3_get_child(parent, dir); 464 | int r = cmp(parent, data); 465 | if (r == 0) 466 | return parent; 467 | dir = (r < 0) ? RB3_RIGHT : RB3_LEFT; 468 | } 469 | if (parent_out) 470 | *parent_out = parent; 471 | if (dir_out) 472 | *dir_out = dir; 473 | return _RB3_NULL; 474 | } 475 | 476 | /**************** DEBUG STUFF *******************/ 477 | int rb3_check_tree(struct rb3_tree *tree); 478 | /************************************************/ 479 | 480 | #ifdef __cplusplus 481 | } // extern "C" 482 | #endif 483 | -------------------------------------------------------------------------------- /src/rbtree.h: -------------------------------------------------------------------------------- 1 | 2 | #include "rb3ptr.h" 3 | 4 | #include "core.h" 5 | 6 | #ifndef TAGCOMPAT 7 | #define rbtree_node(T) struct CONCAT(rbtree_node_, T) 8 | #define rbtree_node_decl(T) rbtree_node(T) { struct rb3_head head; T data; } 9 | 10 | #define rbtree(T) struct CONCAT(rbtree_, T) 11 | #define rbtree_decl(T) rbtree_node_decl(T); rbtree(T) { struct rb3_tree tree; rb3_cmp* compare; rbtree_node(T) type[]; } 12 | #else 13 | #define rbtree_node(T) struct CONCAT(rbtree_node_, T) { struct rb3_head head; T data; } 14 | #define rbtree_node_decl(T) 15 | 16 | #define rbtree_decl(T) 17 | #define rbtree(T) struct CONCAT(rbtree_, T) { struct rb3_tree tree; rb3_cmp* compare; rbtree_node(T) type[]; } 18 | #endif 19 | 20 | #define rbtree_eltype(l) typeof((l)->type[0].data) 21 | #define rbtree_node_type(l) typeof((l)->type[0]) 22 | 23 | #if 0 24 | struct __rbtree_cmp { 25 | 26 | void* ptr; 27 | size_t size; 28 | off_t offset; 29 | }; 30 | 31 | inline int __rbtree_cmp_default(struct rb3_head* a, void* ptr) 32 | { 33 | struct __rbtree_cmp* data = ptr; 34 | return memcmp(a + data->offset, data->ptr, data->size); 35 | } 36 | #endif 37 | 38 | #define __rbtree_cmp_default(NT, ET) \ 39 | int __compare(struct rb3_head* a, void* ptr) { \ 40 | return memcmp(&containerof(a, NT, head)->data, ptr, sizeof(ET)); \ 41 | }; \ 42 | 43 | #define rbtree_set_compare(l, cmp) \ 44 | ({ \ 45 | auto __L = (l); \ 46 | typedef rbtree_eltype(__L) __ET; \ 47 | typedef rbtree_node_type(__L) __NT; \ 48 | \ 49 | int __compare(struct rb3_head* a, void* ptr) { \ 50 | return cmp(&containerof(a, __NT, head)->data, (__ET*)ptr); \ 51 | }; \ 52 | __L->compare = __compare; \ 53 | }) 54 | 55 | #define rbtree_first(l) \ 56 | ({ \ 57 | auto __L = (l); \ 58 | typedef rbtree_node_type(__L) __NT; \ 59 | auto __N2 = rb3_get_min(&__L->tree); \ 60 | (NULL == __N2) ? NULL : containerof(__N2, __NT, head); \ 61 | }) 62 | 63 | #define rbtree_last(l) \ 64 | ({ \ 65 | auto __L = (l); \ 66 | typedef rbtree_node_type(__L) __NT; \ 67 | auto __N2 = rb3_get_max(&__L->tree); \ 68 | (NULL == __N2) ? NULL : containerof(__N2, __NT, head); \ 69 | }) 70 | 71 | 72 | 73 | #define rbtree_node_access(n) ((n)->data) 74 | 75 | #define rbtree_node_next(n) \ 76 | ({ \ 77 | auto __N = (n); \ 78 | auto __N2 = rb3_get_next(&__N->head); \ 79 | (NULL == __N2) ? NULL : \ 80 | containerof(__N2, typeof(*__N), head); \ 81 | }) 82 | 83 | #define rbtree_node_prev(n) \ 84 | ({ \ 85 | auto __N = (n); \ 86 | auto __N2 = rb3_get_prev(&__N->head); \ 87 | (NULL == __N2) ? NULL : \ 88 | containerof(__N2, typeof(*__N), head); \ 89 | }) 90 | 91 | 92 | #define rbtree_insert(l, v) \ 93 | ({ \ 94 | auto __L = (l); \ 95 | typedef rbtree_eltype(__L) __ET; \ 96 | typedef rbtree_node_type(__L) __NT; \ 97 | __ET __v = (v); \ 98 | \ 99 | __NT* __n2 = malloc(sizeof(__NT)); \ 100 | if (NULL == __n2) \ 101 | abort(); \ 102 | __n2->data = __v; \ 103 | __rbtree_cmp_default(__NT, __ET); \ 104 | rb3_insert(&__L->tree, &__n2->head, __L->compare ? __L->compare : __compare, &__n2->data); \ 105 | }) 106 | 107 | 108 | #define rbtree_delete(l, v) \ 109 | ({ \ 110 | auto __L = (l); \ 111 | typedef rbtree_eltype(__L) __ET; \ 112 | typedef rbtree_node_type(__L) __NT; \ 113 | \ 114 | __rbtree_cmp_default(__NT, __ET); \ 115 | struct rb3_head* nt = rb3_delete(&__L->tree, __L->compare ? __L->compare : __compare, &compound_literal(__ET, v)); \ 116 | free(containerof(nt, __NT, head)); \ 117 | }) 118 | 119 | 120 | #define rbtree_find(l, v) \ 121 | ({ \ 122 | auto __L = (l); \ 123 | typedef rbtree_eltype(__L) __ET; \ 124 | typedef rbtree_node_type(__L) __NT; \ 125 | \ 126 | __rbtree_cmp_default(__NT, __ET); \ 127 | struct rb3_head* nt = rb3_find(&__L->tree, __L->compare ? __L->compare : __compare, &(__ET){ v }); \ 128 | containerof(nt, __NT, head); \ 129 | }) 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/span.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uecker/noplate/08307a3d30612badc12ade9338631a44e037fdb0/src/span.c -------------------------------------------------------------------------------- /src/span.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #ifndef _NOPLATE_SPAN_H 7 | #define _NOPLATE_SPAN_H 1 8 | 9 | #include "core.h" 10 | 11 | #ifdef TAGCOMPAT 12 | #define span(T) struct CONCAT(span_, T) { ssize_t N; T* data; } 13 | #define span_decl(T) 14 | #else 15 | #define span(T) struct CONCAT(span_, T) 16 | #define span_decl(T) span(T) { ssize_t N; T* data; } 17 | #endif 18 | 19 | 20 | #define vec2span(T, x) array2span(T, vec2array(T, x)) 21 | 22 | #define span_length(T, x) vec_length(T, TYPE_CHECK(span(T)*, x)) 23 | #define span2array(T, x) vec2array(T, TYPE_CHECK(span(T)*, x)) 24 | #define span_access(T, x, i) vec_access(T, TYPE_CHECK(span(T)*, x), i) 25 | 26 | #define vec_append(T, a, b) \ 27 | ({ \ 28 | vec(T) **__v = (a); \ 29 | span(T) __w = (b); \ 30 | ssize_t alen = vec_length(T, *__v); \ 31 | ssize_t blen = vec_length(T, &__w); \ 32 | vec_realloc(T, __v, alen + blen); \ 33 | if (NULL != *__v) { \ 34 | memcpy(&vec_access(T, *__v, alen), \ 35 | &vec_access(T, &__w, 0), blen * sizeof(T)); \ 36 | } \ 37 | *__v; \ 38 | }) 39 | 40 | 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2023. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "array.h" 12 | #include "string.h" 13 | 14 | #ifdef UTF8_NORMALIZE 15 | #include "utf8proc.h" 16 | #endif 17 | 18 | 19 | extern inline string* string_alloc(void); 20 | 21 | static string* string_init0(int len, const char8_t c[static len]) 22 | { 23 | string_priv* s = vec_alloc_n(char8_t, len + 1); 24 | 25 | if (NULL == s) 26 | goto err; 27 | 28 | memcpy(vec2array(char8_t, s), c, len); 29 | vec_access(char8_t, s, len) = '\0'; 30 | 31 | err: 32 | return (string*)s; 33 | } 34 | 35 | string* string_init(const char* c) 36 | { 37 | return string_init0(strlen(c), (const char8_t*)c); 38 | } 39 | 40 | 41 | string* strview_dup(const strview x) 42 | { 43 | return string_init0(string_length(&x), string_utf8(&x)); 44 | } 45 | 46 | string* string_dup(const string *x) 47 | { 48 | return string_init0(string_length(x), string_utf8(x)); 49 | } 50 | 51 | void string_append_view(string * restrict *a, const strview b) 52 | { 53 | ssize_t blen = string_length(&b); 54 | 55 | string_priv *x = STRING_UNWRAP(*a); 56 | ssize_t alen = string_length(x); 57 | vec_realloc(char8_t, &x, alen + blen + 1); 58 | 59 | *a = (string*)x; 60 | 61 | if (NULL == x) 62 | goto err; 63 | 64 | memcpy(&vec_access(char8_t, x, alen), string_cstr(&b), blen); 65 | vec_access(char8_t, x, alen + blen) = '\0'; 66 | 67 | #ifdef UTF8_NORMALIZE 68 | utf8proc_uint8_t *retval; 69 | utf8proc_map(vec2array(char8_t, x), 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE); 70 | *a = string_init0(strlen((char*)retval), retval); 71 | free(retval); 72 | free(x); 73 | #else 74 | *a = (string*)x; 75 | #endif 76 | err: 77 | } 78 | 79 | void string_puts(string **a, const char *str) 80 | { 81 | char8_t buf[strlen(str) + 1]; 82 | memcpy(buf, str, sizeof buf); 83 | string_append_view(a, array2span(char8_t, buf)); 84 | } 85 | 86 | void string_append(string * restrict *a, const string *b) 87 | { 88 | string_append_view(a, string_view((string*)b)); 89 | } 90 | 91 | string* strview_concat(const strview a, const strview b) 92 | { 93 | ssize_t alen = string_length(&a); 94 | ssize_t blen = string_length(&b); 95 | 96 | string_priv *x = vec_alloc_n(char8_t, alen + blen + 1); 97 | 98 | if (NULL == x) 99 | goto err; 100 | 101 | memcpy(&vec_access(char8_t, x, 0), string_cstr(&a), alen); 102 | memcpy(&vec_access(char8_t, x, alen), string_cstr(&b), blen); 103 | vec_access(char8_t, x, alen + blen) = '\0'; 104 | 105 | err: 106 | return (string*)x; 107 | } 108 | 109 | 110 | string* string_concat(const string *a, const string *b) 111 | { 112 | return strview_concat(string_view((string*)a), string_view((string*)b)); 113 | } 114 | 115 | 116 | string* string_printf(const char* fmt, ...) 117 | { 118 | va_list ap; 119 | 120 | va_start(ap, fmt); 121 | 122 | int len = vsnprintf(NULL, 0, fmt, ap); 123 | 124 | va_end(ap); 125 | 126 | string_priv *s = NULL; 127 | 128 | if (len < 0) 129 | goto err; 130 | 131 | s = vec_alloc_n(char8_t, len + 1); 132 | 133 | if (NULL == s) 134 | goto err; 135 | 136 | va_start(ap, fmt); 137 | 138 | int rlen = vsnprintf((char*)s->data, s->N, fmt, ap); 139 | 140 | va_end(ap); 141 | 142 | if (len < rlen) { 143 | 144 | free(s); 145 | s = NULL; 146 | goto err; 147 | } 148 | err: 149 | return (string*)s; 150 | } 151 | 152 | -------------------------------------------------------------------------------- /src/string.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #ifndef _NOPLATE_STRING_H 7 | #define _NOPLATE_STRING_H 8 | 9 | #include "vec.h" 10 | #include "span.h" 11 | 12 | typedef unsigned char char8_t; 13 | vec_decl(char8_t); 14 | span_decl(char8_t); 15 | span_decl(char); 16 | 17 | // #define STRING_OPAQUE 18 | #ifdef STRING_OPAQUE 19 | struct string; 20 | typedef vec(char8_t) string_priv; 21 | typedef struct string string; 22 | #else 23 | typedef vec(char8_t) string; 24 | typedef string string_priv; 25 | #endif 26 | typedef span(char8_t) strview; 27 | 28 | #define string_check(x) \ 29 | ({ \ 30 | auto __s = (x); \ 31 | CHECK('\0' == vec2array(char8_t, __s)[string_length(__s)]); __s; \ 32 | }) 33 | 34 | #ifdef STRING_OPAQUE 35 | #define STRING_UNWRAP(x) \ 36 | ({ \ 37 | auto __y = (x); \ 38 | _Generic(__y, \ 39 | string_priv*: __y, \ 40 | const string_priv*: __y, \ 41 | string*: (string_priv*)__y, \ 42 | const string*: (const string_priv*)__y, \ 43 | strview*: __y, \ 44 | const strview*: __y); \ 45 | }) 46 | #else 47 | #define STRING_UNWRAP(x) (x) 48 | #endif 49 | 50 | #define string_utf8(x) (vec2array(char8_t, string_check(STRING_UNWRAP(x)))) 51 | #define string_cstr(x) (array_cast(char, string_utf8(x))) 52 | #define string_view(x) (vec2span(char8_t, STRING_UNWRAP(x))) 53 | #define string_length(x) (vec_length(char8_t, STRING_UNWRAP(x)) - 1) 54 | 55 | 56 | inline string *string_alloc(void) 57 | { 58 | string_priv *s = vec_alloc(char8_t); 59 | 60 | if (NULL == s) 61 | goto err; 62 | 63 | vec_push(char8_t, &s, '\0'); 64 | err: 65 | return (string*)s; // ! 66 | } 67 | 68 | 69 | extern string* string_init(const char* c); 70 | 71 | #define STRING(x) (string_init(x)) 72 | 73 | extern string *string_dup(const string *s); 74 | extern string *string_concat(const string *a, const string *b); 75 | extern void string_append(string * restrict *a, const string *b); 76 | extern string *string_printf(const char* fmt, ...); 77 | extern void string_puts(string **a, const char *cstr); 78 | 79 | extern void string_append_view(string * restrict *a, const strview b); 80 | extern string *strview_dup(const strview v); 81 | extern string *strview_concat(const strview a, const strview b); 82 | 83 | #endif // _NOPLATE_STRING_H 84 | 85 | -------------------------------------------------------------------------------- /src/using.h: -------------------------------------------------------------------------------- 1 | 2 | #include "cppmap.h" 3 | 4 | #define NAMESPACE_PREFIX(ns, x) ns ## _ ## x 5 | #define NAMESPACE_ENTRY(ns, x) typeof(NAMESPACE_PREFIX(ns, x)) *x; 6 | #define NAMESPACE_INIT(ns, x) .x = &NAMESPACE_PREFIX(ns, x), 7 | #define NAMESPACE_MAKE(S, N) \ 8 | static const struct \ 9 | { S(N, NAMESPACE_ENTRY) } N = { S(N, NAMESPACE_INIT) } 10 | #define NAMESPACE_IMPORT(X, Y) const static typeof(X) Y = X 11 | 12 | 13 | 14 | // version 2 15 | 16 | #define NAMESPACE(S, ...) \ 17 | static const struct { MAP(NAMESPACE_ENTRY, S, __VA_ARGS__) } S = { MAP(NAMESPACE_INIT, S, __VA_ARGS__) } 18 | 19 | // const does not work with GCC here, PR116456 20 | #define using(S, ...) \ 21 | (struct { MAP(NAMESPACE_ENTRY, S, __VA_ARGS__) }){ MAP(NAMESPACE_INIT, S, __VA_ARGS__) } 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/variadic.h: -------------------------------------------------------------------------------- 1 | 2 | #include "core.h" 3 | 4 | typedef struct variadic_id { } *variadic; 5 | 6 | #ifdef TAGCOMPAT 7 | #define variadic(T) struct CONCAT(val_, T) { variadic super; T value; } 8 | #define variadic_decl(T) 9 | #else 10 | #define variadic(T) struct CONCAT(val_, T) 11 | #define variadic_decl(T) variadic(T) { variadic super; T value; } 12 | #endif 13 | 14 | #define _variadic_id(T) ({ extern struct variadic_id CONCAT(variadic_id_, T); &CONCAT(variadic_id_, T); }) 15 | #define variadic_access(T, x) \ 16 | (*({ \ 17 | auto _x = (x); \ 18 | CHECK(*_x == _variadic_id(T)); \ 19 | &containerof(_x, variadic(T), super)->value; \ 20 | })) 21 | #define variadic_make(T, x) &((variadic(T)){ _variadic_id(T), (x) }.super) 22 | 23 | -------------------------------------------------------------------------------- /src/vec.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include "vec.h" 7 | 8 | #if (GCC_VERSION < 110300) && !defined __clang__ 9 | _Thread_local struct vec_a vec_array_tmp = { 0 }; 10 | #endif 11 | 12 | extern inline ssize_t vec_capacity_auto(ssize_t x); 13 | 14 | 15 | #ifdef __clang__ 16 | struct qsort_frw_data { noplate_qsort_cmp_func_t fun; void* data; }; 17 | static int qsort_block_forward(const void* a, const void* b, void* _d) 18 | { 19 | struct qsort_frw_data* data = _d; 20 | return data->fun(a, b, data->data); 21 | } 22 | extern void noplate_qsort(void* ptr, size_t N, size_t si, noplate_qsort_cmp_func_t cmp, void* data) 23 | { 24 | qsort_r(ptr, N, si, qsort_block_forward, &(struct qsort_frw_data){ cmp, data }); 25 | } 26 | #endif 27 | 28 | -------------------------------------------------------------------------------- /src/vec.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #ifndef _NOPLATE_VEC_H 7 | #define _NOPLATE_VEC_H 1 8 | 9 | #include "core.h" 10 | 11 | 12 | #ifdef TAGCOMPAT 13 | #define vec(T) CONCAT(struct vec_, T) { ssize_t N; [[COUNTED_BY(N)]] T data[]; } 14 | #define vec_decl(T) 15 | #else 16 | #define vec(T) CONCAT(struct vec_, T) 17 | #define vec_decl(T) vec(T) { ssize_t N; [[COUNTED_BY(N)]] T data[]; } 18 | #endif 19 | 20 | #define vec_eltype(T, x) typeof((x)->data[0]) 21 | #define vec_length(T, x) ((size_t)((x)->N)) 22 | //#define vec_length(T, x) ((size_t)(TYPE_CHECK(typeof(vec(T)*), x)->N)) 23 | 24 | #define NULL_CHECK(x) ({ auto __x = (x); if (!__x) abort(); __x; }) 25 | 26 | #define _vec_sizeof(T, N) ((ssize_t)sizeof(vec(T)) + (N) * (ssize_t)sizeof(T)) 27 | #define vec_sizeof(T, x) _vec_sizeof(T, (x)->N) 28 | 29 | 30 | inline ssize_t vec_capacity_auto(ssize_t x) 31 | { 32 | ssize_t dw = x & 0xFFFF; 33 | ssize_t up = x - dw; 34 | 35 | if (!dw) 36 | return x; 37 | #if 1 38 | // round up to power of two, 16 bit 39 | // fibonacci is said to work better 40 | dw--; 41 | for (int i = 0; i < 4; i++) 42 | dw |= dw >> (1 << i); 43 | dw++; 44 | #else 45 | dw = 1u << (_INT_WIDTH_ - _builtin_clz(dw)); 46 | #endif 47 | return up + dw; 48 | } 49 | 50 | 51 | #define vec_realloc_cap(T, capacity, x, M, C) \ 52 | ({ \ 53 | vec(T) **_Ta = (x); \ 54 | (void)TYPE_CHECK(typeof(typeof(vec_eltype(T, *_Ta)[])*), \ 55 | &(*_Ta)->data); \ 56 | ssize_t *_c = (capacity); \ 57 | (*_Ta)->N = (M); \ 58 | if ((*_Ta)->N > *_c) { \ 59 | *_c = (C); \ 60 | *_Ta = realloc(*_Ta, _vec_sizeof(T, *_c)); \ 61 | } \ 62 | *_Ta; \ 63 | }) 64 | 65 | #define vec_push_cap(T, capacity, v2, x2) \ 66 | ({ \ 67 | vec(T) **_T3 = (v2); \ 68 | ssize_t _N = vec_length(T, *_T3); \ 69 | ssize_t _C = vec_capacity_auto(_N + 1); \ 70 | NULL_CHECK(vec_realloc_cap(T, capacity, _T3, _N + 1, _C)); \ 71 | vec_access(T, *_T3, _N) = (x2); \ 72 | }) 73 | 74 | #define vec_push_auto(T, v2, x2) \ 75 | ({ \ 76 | vec(T) **_T4 = (v2); \ 77 | ssize_t _N = vec_length(T, *_T4); \ 78 | ssize_t _cap = vec_capacity_auto(_N); \ 79 | vec_push_cap(T, &_cap, _T4, x2); \ 80 | }) 81 | 82 | #define vec_fit(T, v2) \ 83 | ({ \ 84 | vec(T) **_T3 = (v2); \ 85 | NULL_CHECK(vec_realloc(T, _T3, vec_length(*_T3))); \ 86 | }) 87 | 88 | 89 | #define vec_realloc(T, x, M) \ 90 | ({ \ 91 | vec(T) **_Ta = (x); \ 92 | (void)TYPE_CHECK(typeof(typeof(vec_eltype(T, *_Ta)[])*), \ 93 | &(*_Ta)->data); \ 94 | (*_Ta)->N = (M); \ 95 | *_Ta = realloc(*_Ta, vec_sizeof(T, *_Ta)); \ 96 | *_Ta; \ 97 | }) 98 | 99 | #define vec_calloc_n(T, M) \ 100 | ({ \ 101 | ssize_t _Na = (M); \ 102 | vec(T)* _t = calloc(1, _vec_sizeof(T, _Na)); \ 103 | if (_t) _t->N = _Na; \ 104 | _t; \ 105 | }) 106 | 107 | #define vec_alloc_n(T, M) \ 108 | ({ \ 109 | ssize_t _Na = (M); \ 110 | vec(T)* _t = malloc(_vec_sizeof(T, _Na)); \ 111 | if (_t) _t->N = _Na; \ 112 | _t; \ 113 | }) 114 | 115 | #define vec_alloc(T) (vec_alloc_n(T, 0)) 116 | 117 | 118 | #if (GCC_VERSION >= 110300) || defined __clang__ 119 | #define vec2array(T, x) \ 120 | (*({ \ 121 | auto _x = (x); \ 122 | _Static_assert(same_type_unq_p(T, vec_eltype(T, _x)), ""); \ 123 | (vec_eltype(T, _x)(*)[vec_length(T, _x)])((x)->data); \ 124 | })) 125 | #else 126 | // work around a compiler bug 127 | // GCC: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91038 128 | extern _Thread_local struct vec_a { ssize_t N; const void* data; } vec_array_tmp; 129 | #define vec2array(T, x) \ 130 | (*(({ \ 131 | auto _x = (x); \ 132 | _Static_assert(same_type_unq_p(T, vec_eltype(T, _x)), ""); \ 133 | vec_array_tmp.N = _x->N; \ 134 | vec_array_tmp.data = _x->data; \ 135 | }), (vec_eltype(T, x)(*)[vec_array_tmp.N])(vec_array_tmp.data))) 136 | #endif 137 | 138 | #define vec_access(T, x4, i) \ 139 | (*({ \ 140 | auto _x4 = (x4); \ 141 | ssize_t _i = (i); \ 142 | CHECK((0 <= _i) && (_i < (ssize_t)vec_length(T, _x4))); \ 143 | &(vec2array(T, _x4)[_i]); \ 144 | })) 145 | 146 | #define vec_push(T, v2, x2) \ 147 | ({ \ 148 | vec(T) **_T3 = (v2); \ 149 | ssize_t _N = vec_length(T, *_T3); \ 150 | NULL_CHECK(vec_realloc(T, _T3, _N + 1)); \ 151 | vec_access(T, *_T3, _N) = (x2); \ 152 | }) 153 | 154 | #define vec_pop(T, v2) \ 155 | ({ \ 156 | vec(T) **_T2 = (v2); \ 157 | ssize_t _N = vec_length(T, *_T2); \ 158 | CHECK(0 < _N); \ 159 | auto _r = vec_access(T, *_T2, _N - 1); \ 160 | NULL_CHECK(vec_realloc(T, _T2, _N - 1)); \ 161 | _r; \ 162 | }) 163 | 164 | #define vec_pop_cap(T, capacity, v2) \ 165 | ({ \ 166 | vec(T) **_T2 = (v2); \ 167 | ssize_t _N = vec_length(T, *_T2); \ 168 | CHECK(0 < _N); \ 169 | ssize_t *_c = (capacity); \ 170 | ssize_t _C = vec_capacity_auto(_N - 1); \ 171 | auto _r = vec_access(T, *_T2, _N - 1); \ 172 | if (_C < *_c) { \ 173 | NULL_CHECK(vec_realloc(T, _T2, _N - 1)); \ 174 | *_c = _C; \ 175 | } \ 176 | _r; \ 177 | }) 178 | 179 | #define vec_pop_auto(T, v2) \ 180 | ({ \ 181 | vec(T) **_T3 = (v2); \ 182 | ssize_t _N = vec_length(T, *_T3); \ 183 | ssize_t _cap = vec_capacity_auto(_N); \ 184 | vec_pop_cap(T, &_cap, _T3); \ 185 | }) 186 | 187 | 188 | 189 | #ifndef __clang__ 190 | #define noplate_qsort(ptr, N, si, cmp, data) qsort_r(ptr, N, si, cmp, data) 191 | #else 192 | #ifdef CLOSURE_TYPE 193 | typedef int CLOSURE_TYPE(noplate_qsort_cmp_func_t)(const void*, const void*, void*); 194 | extern void noplate_qsort(void* ptr, size_t N, size_t si, noplate_qsort_cmp_func_t cmp, void* data); 195 | #endif 196 | #endif 197 | 198 | #define vec_sort(T, v2, cmp) \ 199 | ({ \ 200 | auto _T1 = (v2); \ 201 | typedef vec_eltype(T, _T1) _ET; \ 202 | _Static_assert(same_type_unq_p(T, _ET), ""); \ 203 | struct { int CLOSURE_TYPE(xcmp)(const _ET*, const _ET*); } \ 204 | _d2 = { (cmp) }; \ 205 | NESTED(int, _cmp, (const void* a, const void* b, void* _d)) \ 206 | { \ 207 | typeof(_d2)* d = _d; \ 208 | return d->xcmp(a, b); \ 209 | }; \ 210 | noplate_qsort(_T1->data, _T1->N, sizeof(_ET), _cmp, &_d2); \ 211 | }) 212 | 213 | #endif 214 | 215 | -------------------------------------------------------------------------------- /test_mdarray.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | 8 | #include "mdarray.h" 9 | 10 | mdarray_decl(3, int); 11 | mdarray_decl(3, float); 12 | 13 | 14 | void fill(mdarray(3, float) arg, float val) 15 | { 16 | auto x = &mdarray_array(arg); 17 | 18 | for (size_t i = 0; i < array_lengthof((*x)); i++) 19 | for (size_t j = 0; j < array_lengthof((*x)[0]); j++) 20 | for (size_t k = 0; k < array_lengthof((*x)[0][0]); k++) 21 | (*x)[i][j][k] = val; 22 | } 23 | 24 | int main() 25 | { 26 | mdarray(3, int) x = mdarray_init(3, int, (int[4][3][2]){ 0 }); 27 | 28 | // mdarray_array provides an lvalue with a conventional array type 29 | 30 | int (*y)[4][3][2] = &mdarray_array(x); 31 | 32 | assert(4 == array_lengthof(*y)); 33 | assert(3 == array_lengthof((*y)[0])); 34 | assert(2 == array_lengthof((*y)[0][0])); 35 | 36 | mdarray_array(x)[0][1][1] = 3; 37 | 38 | (*y)[1][0][1] = 1; 39 | 40 | 41 | // array_rank retuns the rank of a multi-dimensional array 42 | 43 | float u3[3][2][1]; 44 | float u2[3][3]; 45 | float u1[2]; 46 | extern float v; 47 | 48 | _Static_assert(3 == array_rank(u3), ""); 49 | _Static_assert(2 == array_rank(u2), ""); 50 | _Static_assert(1 == array_rank(u1), ""); 51 | _Static_assert(0 == array_rank(v), ""); 52 | 53 | 54 | // array_dims retuns the dimensions as a comma-separated list 55 | 56 | size_t dims[3] = { array_dims(3, u3) }; 57 | 58 | assert(dims[2] == 3); 59 | assert(dims[1] == 2); 60 | assert(dims[0] == 1); 61 | 62 | 63 | extern array_eltype(u1) v; 64 | extern array_nested_eltype(u1) v; 65 | extern array_nested_eltype(u2) v; 66 | extern array_nested_eltype(u3) v; 67 | 68 | u3[1][1][0] = 1.; 69 | 70 | auto z = mdarray_init(3, float, u3); 71 | mdarray(3, float)* zp = &z; 72 | 73 | assert(1. == mdarray_array(*zp)[1][1][0]); 74 | 75 | fill(z, 2.); 76 | 77 | assert(2. == mdarray_array(*zp)[1][1][0]); 78 | } 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /tests/list.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "list.h" 11 | 12 | typedef char* string_ptr; 13 | 14 | #ifndef TAGCOMPAT 15 | list_decl(int); 16 | list_decl(string_ptr); 17 | #endif 18 | 19 | 20 | void list_int() 21 | { 22 | list(int) l = { 0 }; 23 | 24 | for (int i = 0; i < 10; i++) 25 | list_push(&l, i); 26 | 27 | list_node(int)* n = list_first(&l); 28 | 29 | for (int i = 0; i < 5; i++) 30 | n = list_node_next(n); 31 | 32 | assert(4 == list_node_access(n)); 33 | 34 | int c = 9; 35 | 36 | while (!list_empty(&l)) 37 | if (c-- != list_pop(&l)) 38 | abort(); 39 | } 40 | 41 | void list_string() 42 | { 43 | list(string_ptr) r = { 0 }; 44 | list_push(&r, "Dir?"); 45 | list_push(&r, "es"); 46 | list_push(&r, "geht"); 47 | list_push(&r, "Wie"); 48 | list_push(&r, "Hallo!"); 49 | 50 | if (0 != strcmp("Hallo!", list_node_access(list_first(&r)))) 51 | abort(); 52 | 53 | while (!list_empty(&r)) 54 | list_pop(&r); 55 | } 56 | 57 | int main() 58 | { 59 | list_int(); 60 | list_string(); 61 | 62 | return 0; 63 | } 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /tests/maybe.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | 8 | #include "maybe.h" 9 | 10 | #ifndef TAGCOMPAT 11 | maybe_decl(int); 12 | #endif 13 | 14 | 15 | void test_maybe() 16 | { 17 | maybe(int) m = maybe_fail(int); 18 | 19 | if (m.ok) 20 | abort(); 21 | 22 | m = maybe_ok(int, 3); 23 | 24 | if (!m.ok) 25 | abort(); 26 | 27 | if (3 != maybe_just(int, m)) 28 | abort(); 29 | } 30 | 31 | 32 | int main() 33 | { 34 | test_maybe(); 35 | 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /tests/span.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | #include 8 | 9 | #include "vec.h" 10 | #include "array.h" 11 | #include "span.h" 12 | 13 | 14 | #ifndef TAGCOMPAT 15 | vec_decl(int); 16 | span_decl(int); 17 | span_decl(float); 18 | #endif 19 | 20 | 21 | void test_span() 22 | { 23 | float bb[4]; 24 | span(float) vf = array2span(float, bb); 25 | 26 | span_access(float, &vf, 1) = 1.; 27 | 28 | vec(int) *v = NULL_CHECK(vec_alloc(int)); 29 | 30 | vec_push(int, &v, 1); 31 | vec_push(int, &v, 3); 32 | vec_push(int, &v, 8); 33 | 34 | span(int) vi = vec2span(int, v); 35 | 36 | span_access(int, &vi, 1)++; 37 | 38 | int (*t)[3] = &span2array(int, &vi); 39 | 40 | if (4 != span2array(int, &vi)[1]) 41 | abort(); 42 | 43 | if ((*t)[2] != span2array(int, &vi)[2]) 44 | abort(); 45 | } 46 | 47 | int main() 48 | { 49 | test_span(); 50 | return 0; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tests/string.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | #include 8 | 9 | #include "array.h" 10 | #include "string.h" 11 | 12 | typedef string* string_ptr; 13 | 14 | #ifndef TAGCOMPAT 15 | vec_decl(string_ptr); 16 | #endif 17 | 18 | void test_string() 19 | { 20 | vec(string_ptr)* s = NULL_CHECK(vec_alloc(string_ptr)); 21 | 22 | vec_push(string_ptr, &s, string_init(" Du!")); 23 | vec_push(string_ptr, &s, string_init("Hallo")); 24 | 25 | string* ss = NULL_CHECK(string_alloc()); 26 | 27 | for (int i = 0; i < (int)vec_length(int, s); i++) 28 | string_append(&ss, vec_access(string_ptr, s, vec_length(int, s) - 1 - i)); 29 | 30 | if (vec_length(string_ptr, s) != array_lengthof(vec2array(string_ptr, s))) 31 | abort(); 32 | 33 | free(ss); 34 | 35 | while (0 < vec_length(string_ptr, s)) 36 | free(vec_pop(string_ptr, &s)); 37 | 38 | free(s); 39 | } 40 | 41 | void test_string_array() 42 | { 43 | string* s3 = string_init("hallo"); 44 | 45 | // this assignment is not checked by compilers 46 | // char (*slice)[3] 47 | auto slice = &array_slice(string_cstr(s3), 1, 1 + 3); 48 | 49 | (*slice)[0] = 'A'; 50 | (*slice)[1] = 'L'; 51 | (*slice)[2] = 'L'; 52 | 53 | strview v3 = string_view(s3); 54 | 55 | if (0 != strcmp("hALLo", string_cstr(&v3))) 56 | abort(); 57 | 58 | if (vec_length(char8_t, &v3) != string_length(s3) + 1) 59 | abort(); 60 | 61 | free(s3); 62 | } 63 | 64 | int main() 65 | { 66 | test_string(); 67 | test_string_array(); 68 | 69 | return 0; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /tests/variadic.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | 8 | #include "variadic.h" 9 | 10 | variadic_decl(int); 11 | 12 | void test_variadic() 13 | { 14 | variadic *v = variadic_make(int, 3); 15 | 16 | if (3 != variadic_access(int, v)) 17 | abort(); 18 | } 19 | 20 | 21 | int main() 22 | { 23 | test_variadic(); 24 | 25 | return 0; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tests/vec.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2021-2025. Martin Uecker 2 | * All rights reserved. Use of this source code is governed by 3 | * a BSD-style license which can be found in the LICENSE file. 4 | * */ 5 | 6 | #include 7 | #include 8 | 9 | #include "vec.h" 10 | #include "span.h" 11 | #include "array.h" 12 | 13 | #ifndef TAGCOMPAT 14 | vec_decl(int); 15 | span_decl(int); 16 | #endif 17 | 18 | void test_vec() 19 | { 20 | vec(int)* v = NULL_CHECK(vec_alloc(int)); 21 | 22 | vec_push(int, &v, 1); 23 | vec_push(int, &v, 3); 24 | vec_push(int, &v, 8); 25 | 26 | vec(int)* w = NULL_CHECK(vec_alloc(int)); 27 | 28 | vec_push(int, &w, 1); 29 | vec_push(int, &w, 3); 30 | vec_push(int, &w, 8); 31 | 32 | vec_append(int, &v, vec2span(int, w)); 33 | 34 | free(w); 35 | 36 | vec_access(int, v, 1)++; 37 | 38 | int good[] = { 1, 4, 8, 1, 3, 8 }; 39 | 40 | for (int i = 0; i < (int)vec_length(int, v); i++) 41 | if (good[i] != vec_access(int, v, i)) 42 | abort(); 43 | 44 | NESTED(int, cmp, (const int* a, const int* b)) 45 | { 46 | return *a - *b; 47 | }; 48 | 49 | vec_sort(int, v, cmp); 50 | 51 | int good2[] = { 1, 1, 3, 4, 8, 8 }; 52 | 53 | for (int i = 0; i < (int)vec_length(int, v); i++) 54 | if (good2[i] != vec_access(int, v, i)) 55 | abort(); 56 | 57 | free(v); 58 | } 59 | 60 | int main() 61 | { 62 | test_vec(); 63 | 64 | return 0; 65 | } 66 | 67 | --------------------------------------------------------------------------------