├── .gitignore ├── dyn_kvp ├── tests │ ├── .gitignore │ ├── main.c │ ├── Makefile │ ├── basic │ │ ├── basic.h │ │ └── basic.c │ └── charp_key │ │ ├── charp_key.h │ │ └── charp_key.c ├── examples │ ├── two_files │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── inc.h │ │ ├── foo_kvp.h │ │ ├── foo_kvp.c │ │ └── main.c │ ├── makefile_gen │ │ ├── .gitignore │ │ ├── inc.h │ │ ├── main.c │ │ └── Makefile │ └── one_file │ │ └── main.c ├── LICENSE ├── README.md └── dyn_kvp.h ├── sagoma ├── tests │ ├── .gitignore │ ├── main.c │ ├── Makefile │ ├── no_output │ │ └── main.c │ ├── build_info │ │ └── main.c │ └── ez │ │ └── main.c ├── examples │ ├── enabled │ │ ├── .gitignore │ │ └── main.c │ └── disabled │ │ ├── .gitignore │ │ └── main.c └── README.md ├── dyn_dllist ├── tests │ ├── .gitignore │ ├── main.c │ ├── Makefile │ └── basic │ │ ├── basic.h │ │ └── basic.c ├── examples │ ├── two_files │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── inc.h │ │ ├── foo_list.h │ │ ├── foo_list.c │ │ └── main.c │ ├── makefile_gen │ │ ├── .gitignore │ │ ├── inc.h │ │ ├── main.c │ │ └── Makefile │ └── one_file │ │ └── main.c ├── LICENSE ├── README.md └── dyn_dllist.h ├── dyn_arr ├── examples │ ├── two_files │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── inc.h │ │ ├── foo_arr.h │ │ ├── foo_arr.c │ │ └── main.c │ ├── makefile_gen │ │ ├── .gitignore │ │ ├── inc.h │ │ ├── main.c │ │ └── Makefile │ └── one_file │ │ └── main.c ├── tests │ ├── basic │ │ ├── basic.h │ │ ├── remove.c │ │ ├── add.c │ │ ├── iterate.c │ │ └── basic.c │ ├── Makefile │ └── main.c ├── README.md └── dyn_arr.h ├── tap ├── tap.h ├── tap.c └── README.md ├── LICENSE ├── .github └── workflows │ ├── codeql.yml │ └── main.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .ccls-cache 2 | -------------------------------------------------------------------------------- /dyn_kvp/tests/.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /sagoma/tests/.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /dyn_dllist/tests/.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /sagoma/examples/enabled/.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /sagoma/examples/disabled/.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /dyn_arr/examples/two_files/.gitignore: -------------------------------------------------------------------------------- 1 | two_files 2 | -------------------------------------------------------------------------------- /dyn_kvp/examples/two_files/.gitignore: -------------------------------------------------------------------------------- 1 | two_files 2 | -------------------------------------------------------------------------------- /dyn_dllist/examples/two_files/.gitignore: -------------------------------------------------------------------------------- 1 | two_files 2 | -------------------------------------------------------------------------------- /dyn_arr/examples/makefile_gen/.gitignore: -------------------------------------------------------------------------------- 1 | foo_arr.h 2 | foo_arr.c 3 | makefile_gen 4 | -------------------------------------------------------------------------------- /dyn_kvp/examples/makefile_gen/.gitignore: -------------------------------------------------------------------------------- 1 | foo_kvp.h 2 | foo_kvp.c 3 | makefile_gen 4 | -------------------------------------------------------------------------------- /dyn_dllist/examples/makefile_gen/.gitignore: -------------------------------------------------------------------------------- 1 | foo_list.h 2 | foo_list.c 3 | makefile_gen 4 | -------------------------------------------------------------------------------- /dyn_arr/examples/two_files/Makefile: -------------------------------------------------------------------------------- 1 | two_files: main.c foo_arr.c foo_arr.h ../../dyn_arr.h 2 | gcc -o two_files main.c foo_arr.c 3 | -------------------------------------------------------------------------------- /dyn_kvp/examples/two_files/Makefile: -------------------------------------------------------------------------------- 1 | two_files: main.c foo_kvp.c foo_kvp.h ../../dyn_kvp.h 2 | gcc -o two_files main.c foo_kvp.c 3 | -------------------------------------------------------------------------------- /dyn_dllist/examples/two_files/Makefile: -------------------------------------------------------------------------------- 1 | two_files: main.c foo_list.c foo_list.h ../../dyn_dllist.h 2 | gcc -o two_files main.c foo_list.c 3 | -------------------------------------------------------------------------------- /dyn_arr/examples/two_files/inc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | -------------------------------------------------------------------------------- /dyn_kvp/examples/two_files/inc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | -------------------------------------------------------------------------------- /dyn_arr/examples/makefile_gen/inc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | -------------------------------------------------------------------------------- /dyn_dllist/examples/makefile_gen/inc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | -------------------------------------------------------------------------------- /dyn_dllist/examples/two_files/inc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | -------------------------------------------------------------------------------- /dyn_kvp/examples/makefile_gen/inc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | -------------------------------------------------------------------------------- /dyn_dllist/tests/main.c: -------------------------------------------------------------------------------- 1 | #include "../../tap/tap.h" 2 | extern void test_basic_foo(void); 3 | int main(void) { 4 | tap_run_self_tests(); 5 | test_basic_foo(); 6 | tap_done_testing(); 7 | } 8 | -------------------------------------------------------------------------------- /dyn_arr/examples/two_files/foo_arr.h: -------------------------------------------------------------------------------- 1 | #define DYN_ARR_VALUE_TYPE struct foo 2 | #define DYN_ARR_TYPE_NAME foo_arr 3 | #include "../../dyn_arr.h" 4 | #undef DYN_ARR_TYPE_NAME 5 | #undef DYN_ARR_VALUE_TYPE 6 | -------------------------------------------------------------------------------- /dyn_kvp/examples/two_files/foo_kvp.h: -------------------------------------------------------------------------------- 1 | #define DYN_KVP_VALUE_TYPE struct foo 2 | #define DYN_KVP_TYPE_NAME foo_kvp 3 | #include "../../dyn_kvp.h" 4 | #undef DYN_KVP_TYPE_NAME 5 | #undef DYN_KVP_VALUE_TYPE 6 | -------------------------------------------------------------------------------- /dyn_dllist/examples/two_files/foo_list.h: -------------------------------------------------------------------------------- 1 | #define DYN_DLLIST_VALUE_TYPE struct foo 2 | #define DYN_DLLIST_TYPE_NAME foo_list 3 | #include "../../dyn_dllist.h" 4 | #undef DYN_DLLIST_TYPE_NAME 5 | #undef DYN_DLLIST_VALUE_TYPE 6 | -------------------------------------------------------------------------------- /dyn_kvp/tests/main.c: -------------------------------------------------------------------------------- 1 | #include "../../tap/tap.h" 2 | extern void test_basic_foo(void); 3 | extern void test_charp_foo(void); 4 | int main(void) { 5 | tap_run_self_tests(); 6 | test_basic_foo(); 7 | test_charp_foo(); 8 | tap_done_testing(); 9 | } 10 | -------------------------------------------------------------------------------- /dyn_dllist/examples/two_files/foo_list.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #define DYN_DLLIST_VALUE_TYPE struct foo 3 | #define DYN_DLLIST_TYPE_NAME foo_list 4 | #define DYN_DLLIST_IMPLEMENTATION 5 | #include "../../dyn_dllist.h" 6 | #undef DYN_DLLIST_IMPLEMENTATION 7 | #undef DYN_DLLIST_TYPE_NAME 8 | #undef DYN_DLLIST_VALUE_TYPE 9 | -------------------------------------------------------------------------------- /sagoma/tests/main.c: -------------------------------------------------------------------------------- 1 | #include "../../tap/tap.h" 2 | extern void test_ez(void); 3 | extern void test_no_output(void); 4 | extern void test_build_info(void); 5 | int main(void) { 6 | tap_run_self_tests(); 7 | test_ez(); 8 | test_no_output(); 9 | test_build_info(); 10 | tap_done_testing(); 11 | } 12 | -------------------------------------------------------------------------------- /dyn_arr/examples/two_files/foo_arr.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_arr.h" 3 | #define DYN_ARR_VALUE_TYPE struct foo 4 | #define DYN_ARR_TYPE_NAME foo_arr 5 | #define DYN_ARR_IMPLEMENTATION 6 | #include "../../dyn_arr.h" 7 | #undef DYN_ARR_IMPLEMENTATION 8 | #undef DYN_ARR_TYPE_NAME 9 | #undef DYN_ARR_VALUE_TYPE 10 | -------------------------------------------------------------------------------- /dyn_kvp/examples/two_files/foo_kvp.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_kvp.h" 3 | #define DYN_KVP_VALUE_TYPE struct foo 4 | #define DYN_KVP_TYPE_NAME foo_kvp 5 | #define DYN_KVP_IMPLEMENTATION 6 | #include "../../dyn_kvp.h" 7 | #undef DYN_KVP_IMPLEMENTATION 8 | #undef DYN_KVP_TYPE_NAME 9 | #undef DYN_KVP_VALUE_TYPE 10 | -------------------------------------------------------------------------------- /sagoma/tests/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run_tests 2 | run_tests: test 3 | prove -v /bin/bash :: -c 'valgrind -s --tool=memcheck --leak-check=full --error-exitcode=99 ./test' 4 | 5 | C_FILES=$(wildcard */*.c) 6 | C_FILES+=main.c 7 | C_FILES+=../../tap/tap.c 8 | H_FILES=$(wildcard */*.h) 9 | 10 | test: $(C_FILES) $(H_FILES) ../sagoma.h 11 | $(CC) $(CFLAGS) -o test $(C_FILES) -lm 12 | -------------------------------------------------------------------------------- /dyn_arr/tests/basic/basic.h: -------------------------------------------------------------------------------- 1 | #include 2 | struct basic_foo { 3 | int a; 4 | int b; 5 | }; 6 | struct basic_foo *basic_foo_new(int a, int b); 7 | void basic_foo_free(struct basic_foo *f); 8 | #define DYN_ARR_VALUE_TYPE struct basic_foo 9 | #define DYN_ARR_TYPE_NAME bfoo_arr 10 | #include "../../dyn_arr.h" 11 | #undef DYN_ARR_TYPE_NAME 12 | #undef DYN_ARR_VALUE_TYPE 13 | -------------------------------------------------------------------------------- /dyn_kvp/tests/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run_tests 2 | run_tests: test 3 | @# prove -v ./test 4 | prove -v /bin/bash :: -c 'valgrind -s --tool=memcheck --leak-check=full --error-exitcode=99 ./test' 5 | 6 | C_FILES=$(wildcard */*.c) 7 | C_FILES+=main.c 8 | C_FILES+=../../tap/tap.c 9 | H_FILES=$(wildcard */*.h) 10 | 11 | test: $(C_FILES) $(H_FILES) 12 | $(CC) $(CFLAGS) -o test $(C_FILES) 13 | -------------------------------------------------------------------------------- /dyn_kvp/tests/basic/basic.h: -------------------------------------------------------------------------------- 1 | #include 2 | struct basic_foo { 3 | int a; 4 | int b; 5 | }; 6 | struct basic_foo *basic_foo_new(int a, int b); 7 | void basic_foo_free(struct basic_foo *f); 8 | #define DYN_KVP_VALUE_TYPE struct basic_foo 9 | #define DYN_KVP_TYPE_NAME bfoo_kvp 10 | #include "../../dyn_kvp.h" 11 | #undef DYN_KVP_TYPE_NAME 12 | #undef DYN_KVP_VALUE_TYPE 13 | -------------------------------------------------------------------------------- /dyn_dllist/tests/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run_tests 2 | run_tests: test 3 | @# prove -v ./test 4 | prove -v /bin/bash :: -c 'valgrind -s --tool=memcheck --leak-check=full --error-exitcode=99 ./test' 5 | 6 | C_FILES=$(wildcard */*.c) 7 | C_FILES+=main.c 8 | C_FILES+=../../tap/tap.c 9 | H_FILES=$(wildcard */*.h) 10 | 11 | test: $(C_FILES) $(H_FILES) 12 | $(CC) $(CFLAGS) -o test $(C_FILES) 13 | -------------------------------------------------------------------------------- /dyn_dllist/tests/basic/basic.h: -------------------------------------------------------------------------------- 1 | #include 2 | struct basic_foo { 3 | int a; 4 | int b; 5 | }; 6 | struct basic_foo *basic_foo_new(int a, int b); 7 | void basic_foo_free(struct basic_foo *f); 8 | #define DYN_DLLIST_VALUE_TYPE struct basic_foo 9 | #define DYN_DLLIST_TYPE_NAME bfoo_list 10 | #include "../../dyn_dllist.h" 11 | #undef DYN_DLLIST_TYPE_NAME 12 | #undef DYN_DLLIST_VALUE_TYPE 13 | -------------------------------------------------------------------------------- /dyn_arr/tests/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run_tests 2 | run_tests: test 3 | prove -v /bin/bash :: -c 'valgrind -s --tool=memcheck --leak-check=full --show-leak-kinds=all --error-exitcode=99 ./test' 4 | 5 | C_FILES=$(wildcard */*.c) 6 | C_FILES+=main.c 7 | C_FILES+=../../tap/tap.c 8 | H_FILES=$(wildcard */*.h) ../../tap/tap.h ../dyn_arr.h 9 | 10 | test: $(C_FILES) $(H_FILES) 11 | $(CC) $(CFLAGS) -ggdb3 -o test $(C_FILES) 12 | -------------------------------------------------------------------------------- /dyn_arr/tests/main.c: -------------------------------------------------------------------------------- 1 | #include "../../tap/tap.h" 2 | extern void test_basic_foo(void); 3 | extern void test_basic_foo_remove(void); 4 | extern void test_basic_foo_add(void); 5 | extern void test_basic_foo_iterate(void); 6 | int main(void) { 7 | tap_run_self_tests(); 8 | test_basic_foo(); 9 | test_basic_foo_remove(); 10 | test_basic_foo_add(); 11 | test_basic_foo_iterate(); 12 | tap_done_testing(); 13 | } 14 | -------------------------------------------------------------------------------- /dyn_arr/examples/makefile_gen/main.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_arr.h" 3 | 4 | int main(void) 5 | { 6 | struct foo *val = malloc(sizeof(struct foo)); 7 | if (!val) { 8 | perror("malloc"); 9 | return EXIT_FAILURE; 10 | } 11 | struct foo_arr *arr = foo_arr_new(8, 2, 1); 12 | foo_arr_push(arr, val); 13 | // Do stuff with it; later on... 14 | foo_arr_del(arr, 0); 15 | // Clean things up: 16 | foo_arr_free(arr); 17 | // You'll have to clean up the MEMBERS yourself! 18 | free(val); 19 | } 20 | -------------------------------------------------------------------------------- /dyn_kvp/examples/makefile_gen/main.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_kvp.h" 3 | 4 | int main(void) 5 | { 6 | struct foo *val = malloc(sizeof(struct foo)); 7 | if (!val) { 8 | perror("malloc"); 9 | return EXIT_FAILURE; 10 | } 11 | struct foo_kvp *hash = foo_kvp_new(8); 12 | // Populate val... 13 | foo_kvp_set(hash, 1, val); 14 | // Do stuff with it; later on... 15 | foo_kvp_del(hash, 1); 16 | // Clean things up: 17 | foo_kvp_free(hash); 18 | // You'll have to clean up the MEMBERS yourself! 19 | free(val); 20 | } 21 | -------------------------------------------------------------------------------- /dyn_arr/examples/two_files/main.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_arr.h" 3 | 4 | int main(void) 5 | { 6 | struct foo *val = malloc(sizeof(struct foo)); 7 | if (!val) { 8 | perror("malloc"); 9 | return EXIT_FAILURE; 10 | } 11 | val->a = 1; 12 | val->b = 2; 13 | struct foo_arr *arr = foo_arr_new(8, 2, 1); 14 | foo_arr_push(arr, val); 15 | // Do stuff with it; later on... 16 | foo_arr_del(arr, 0); 17 | // Clean things up: 18 | foo_arr_free(arr); 19 | // You'll have to clean up the MEMBERS yourself! 20 | free(val); 21 | } 22 | -------------------------------------------------------------------------------- /dyn_dllist/examples/two_files/main.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_list.h" 3 | 4 | int main(void) 5 | { 6 | struct foo *val = malloc(sizeof(struct foo)); 7 | if (!val) { 8 | perror("malloc"); 9 | return EXIT_FAILURE; 10 | } 11 | struct foo_list *list = foo_list_new(); 12 | // Populate val... 13 | foo_list_append(list, val); 14 | // Do stuff with it; later on... 15 | foo_list_remove_all(list, val); 16 | // Clean things up: 17 | foo_list_free(list); 18 | // You'll have to clean up the MEMBERS yourself! 19 | free(val); 20 | } 21 | -------------------------------------------------------------------------------- /dyn_dllist/examples/makefile_gen/main.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_list.h" 3 | 4 | int main(void) 5 | { 6 | struct foo *val = malloc(sizeof(struct foo)); 7 | if (!val) { 8 | perror("malloc"); 9 | return EXIT_FAILURE; 10 | } 11 | struct foo_list *list = foo_list_new(); 12 | // Populate val... 13 | foo_list_append(list, val); 14 | // Do stuff with it; later on... 15 | foo_list_remove_all(list, val); 16 | // Clean things up: 17 | foo_list_free(list); 18 | // You'll have to clean up the MEMBERS yourself! 19 | free(val); 20 | } 21 | -------------------------------------------------------------------------------- /dyn_kvp/tests/charp_key/charp_key.h: -------------------------------------------------------------------------------- 1 | #include 2 | struct custom_foo { 3 | int a; 4 | int b; 5 | }; 6 | struct custom_foo *custom_foo_new(int a, int b); 7 | void custom_foo_free(struct custom_foo *f); 8 | size_t custom_foo_hash(size_t size, const char *key); 9 | #define DYN_KVP_VALUE_TYPE struct custom_foo 10 | #define DYN_KVP_TYPE_NAME cfoo_kvp 11 | #define DYN_KVP_KEY_TYPE const char * 12 | #define DYN_KVP_HASH_FUNCTION custom_foo_hash 13 | #include "../../dyn_kvp.h" 14 | #undef DYN_KVP_HASH_FUNCTION 15 | #undef DYN_KVP_KEY_TYPE 16 | #undef DYN_KVP_TYPE_NAME 17 | #undef DYN_KVP_VALUE_TYPE 18 | -------------------------------------------------------------------------------- /dyn_kvp/examples/two_files/main.c: -------------------------------------------------------------------------------- 1 | #include "inc.h" 2 | #include "foo_kvp.h" 3 | 4 | int main(void) 5 | { 6 | struct foo *val = malloc(sizeof(struct foo)); 7 | if (!val) { 8 | perror("malloc"); 9 | return EXIT_FAILURE; 10 | } 11 | val->a = 1; 12 | val->b = 2; 13 | struct foo_kvp *hash = foo_kvp_new(8); 14 | // Populate val... 15 | foo_kvp_set(hash, 1, val); 16 | // Do stuff with it; later on... 17 | foo_kvp_del(hash, 1); 18 | // Clean things up: 19 | foo_kvp_free(hash); 20 | // You'll have to clean up the MEMBERS yourself! 21 | free(val); 22 | } 23 | -------------------------------------------------------------------------------- /sagoma/examples/disabled/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../../sagoma.h" 4 | 5 | int main(void) 6 | { 7 | printf("Starting...\n"); 8 | sagoma_init(s, "main.c"); 9 | sagoma_block(s, foo); 10 | for (int i = 0; i < 10; i++) 11 | { 12 | sagoma_block(s, bar); 13 | for (int j = 0; j < 2; j++) 14 | { 15 | sagoma_block(s, baz); 16 | printf("Hello, world! i=%d, j=%d\n", i, j); 17 | sagoma_block_done(s); 18 | } 19 | sagoma_block_done(s); 20 | } 21 | sagoma_block_done(s); 22 | sagoma_print(s, 0, -1); 23 | printf("Done...\n"); 24 | } 25 | -------------------------------------------------------------------------------- /sagoma/tests/no_output/main.c: -------------------------------------------------------------------------------- 1 | #include "../../../tap/tap.h" 2 | #include 3 | 4 | #define SAGOMA_DONT_PRINT 5 | #include "../../sagoma.h" 6 | 7 | void test_no_output(void) 8 | { 9 | tap_diag("Running test for no output"); 10 | { 11 | sagoma_this( 12 | usleep(100); 13 | tap_is_int(sagoma.start_tsc > 0, 1, "sagoma.start_tsc > 0"); 14 | tap_is_int(sagoma.end_tsc, 0, "sagoma.end_tsc == 0 (as profile going on)"); 15 | sagoma_this_block("This block should not be printed", 16 | usleep(100); 17 | ); 18 | ); 19 | tap_is_int(sagoma.end_tsc > sagoma.start_tsc, 1, "sagoma.end_tsc > sagoma.start_tsc"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dyn_dllist/examples/one_file/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | 10 | #define DYN_DLLIST_VALUE_TYPE struct foo 11 | #define DYN_DLLIST_TYPE_NAME foo_list 12 | #define DYN_DLLIST_IMPLEMENTATION 13 | #include "../../dyn_dllist.h" 14 | #undef DYN_DLLIST_IMPLEMENTATION 15 | #undef DYN_DLLIST_TYPE_NAME 16 | #undef DYN_DLLIST_VALUE_TYPE 17 | 18 | int main(void) 19 | { 20 | struct foo *val = malloc(sizeof(struct foo)); 21 | if (!val) { 22 | perror("malloc"); 23 | return EXIT_FAILURE; 24 | } 25 | struct foo_list *list = foo_list_new(); 26 | // Populate val... 27 | foo_list_append(list, val); 28 | // Do stuff with it; later on... 29 | foo_list_remove_all(list, val); 30 | // Clean things up: 31 | foo_list_free(list); 32 | // You'll have to clean up the MEMBERS yourself! 33 | free(val); 34 | } 35 | -------------------------------------------------------------------------------- /dyn_dllist/examples/makefile_gen/Makefile: -------------------------------------------------------------------------------- 1 | makefile_gen: main.c foo_list.c foo_list.h 2 | gcc -o makefile_gen main.c foo_list.c 3 | 4 | foo_list.h: Makefile ../../dyn_dllist.h 5 | rm -f $@ 6 | printf '// Automatically generated by %s.\n' "$^" > $@ 7 | printf '// Generated on %s\n' "$$(date)" >> $@ 8 | printf '#ifndef FOO_LIST_H_\n#define FOO_LIST_H_\n' >> $@ 9 | gcc -E -P -CC -D DYN_DLLIST_VALUE_TYPE='struct foo' -D DYN_DLLIST_TYPE_NAME=foo_list ../../dyn_dllist.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 10 | printf '#endif\n' >> $@ 11 | chmod -w $@ 12 | 13 | foo_list.c: Makefile foo_list.h ../../dyn_dllist.h 14 | rm -f $@ 15 | printf '// Automatically generated by %s.\n' "$^" > $@ 16 | printf '// Generated on %s\n' "$$(date)" >> $@ 17 | printf '#include "inc.h"\n' >> $@ 18 | gcc -E -P -CC -D DYN_DLLIST_VALUE_TYPE='struct foo' -D DYN_DLLIST_TYPE_NAME=foo_list -D DYN_DLLIST_IMPLEMENTATION ../../dyn_dllist.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 19 | chmod -w $@ 20 | -------------------------------------------------------------------------------- /dyn_arr/examples/makefile_gen/Makefile: -------------------------------------------------------------------------------- 1 | makefile_gen: main.c foo_arr.c foo_arr.h 2 | gcc -o makefile_gen main.c foo_arr.c 3 | 4 | foo_arr.h: Makefile ../../dyn_arr.h 5 | rm -f $@ 6 | printf '// Automatically generated by %s.\n' "$^" > $@ 7 | printf '// Generated on %s\n' "$$(date)" >> $@ 8 | printf '#ifndef FOO_ARR_H_\n#define FOO_ARR_H_\n' >> $@ 9 | gcc -E -P -CC -D DYN_ARR_VALUE_TYPE='struct foo' -D DYN_ARR_TYPE_NAME=foo_arr ../../dyn_arr.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 10 | printf '#endif\n' >> $@ 11 | chmod -w $@ 12 | 13 | foo_arr.c: Makefile foo_arr.h ../../dyn_arr.h 14 | rm -f $@ 15 | printf '// Automatically generated by %s.\n' "$^" > $@ 16 | printf '// Generated on %s\n' "$$(date)" >> $@ 17 | printf '#include "inc.h"\n' >> $@ 18 | printf '#include "foo_arr.h"\n' >> $@ 19 | gcc -E -P -CC -D DYN_ARR_VALUE_TYPE='struct foo' -D DYN_ARR_TYPE_NAME=foo_arr -D DYN_ARR_IMPLEMENTATION ../../dyn_arr.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 20 | chmod -w $@ 21 | -------------------------------------------------------------------------------- /dyn_kvp/examples/makefile_gen/Makefile: -------------------------------------------------------------------------------- 1 | makefile_gen: main.c foo_kvp.c foo_kvp.h 2 | gcc -o makefile_gen main.c foo_kvp.c 3 | 4 | foo_kvp.h: Makefile ../../dyn_kvp.h 5 | rm -f $@ 6 | printf '// Automatically generated by %s.\n' "$^" > $@ 7 | printf '// Generated on %s\n' "$$(date)" >> $@ 8 | printf '#ifndef FOO_KVP_H_\n#define FOO_KVP_H_\n' >> $@ 9 | gcc -E -P -CC -D DYN_KVP_VALUE_TYPE='struct foo' -D DYN_KVP_TYPE_NAME=foo_kvp ../../dyn_kvp.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 10 | printf '#endif\n' >> $@ 11 | chmod -w $@ 12 | 13 | foo_kvp.c: Makefile foo_kvp.h ../../dyn_kvp.h 14 | rm -f $@ 15 | printf '// Automatically generated by %s.\n' "$^" > $@ 16 | printf '// Generated on %s\n' "$$(date)" >> $@ 17 | printf '#include "inc.h"\n' >> $@ 18 | printf '#include "foo_kvp.h"\n' >> $@ 19 | gcc -E -P -CC -D DYN_KVP_VALUE_TYPE='struct foo' -D DYN_KVP_TYPE_NAME=foo_kvp -D DYN_KVP_IMPLEMENTATION ../../dyn_kvp.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 20 | chmod -w $@ 21 | -------------------------------------------------------------------------------- /tap/tap.h: -------------------------------------------------------------------------------- 1 | #ifndef TAP_H 2 | #define TAP_H 3 | #include 4 | #include 5 | extern unsigned int tap_test_number; 6 | extern void tap_done_testing(void); 7 | #define TAP_STRINGIFY(x) #x 8 | #define TAP_TOSTRING(x) TAP_STRINGIFY(x) 9 | #define TAP_AT " [ at " __FILE__ ":" TAP_TOSTRING(__LINE__) " ]" 10 | #define tap_diag(fmt, ...) printf("# " fmt TAP_AT "\n", ##__VA_ARGS__) 11 | extern int tap_is_int_(int got, int wanted, char *test_description); 12 | #define tap_is_int(got, wanted, test_description) \ 13 | tap_is_int_(got, wanted, test_description TAP_AT) 14 | extern int tap_is_ulong_(unsigned long got, unsigned long wanted, char *test_description); 15 | #define tap_is_ulong(got, wanted, test_description) \ 16 | tap_is_ulong_(got, wanted, test_description TAP_AT) 17 | extern int tap_is_voidp_(void *got, void *wanted, char *test_description); 18 | #define tap_is_voidp(got, wanted, test_description) \ 19 | tap_is_voidp_(got, wanted, test_description TAP_AT) 20 | extern void tap_run_self_tests(void); 21 | #endif 22 | -------------------------------------------------------------------------------- /dyn_arr/examples/one_file/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | 10 | #define DYN_ARR_VALUE_TYPE struct foo 11 | #define DYN_ARR_TYPE_NAME foo_arr 12 | #include "../../dyn_arr.h" 13 | #undef DYN_ARR_TYPE_NAME 14 | #undef DYN_ARR_VALUE_TYPE 15 | 16 | #define DYN_ARR_VALUE_TYPE struct foo 17 | #define DYN_ARR_TYPE_NAME foo_arr 18 | #define DYN_ARR_IMPLEMENTATION 19 | #include "../../dyn_arr.h" 20 | #undef DYN_ARR_IMPLEMENTATION 21 | #undef DYN_ARR_TYPE_NAME 22 | #undef DYN_ARR_VALUE_TYPE 23 | 24 | int main(void) 25 | { 26 | struct foo *val = malloc(sizeof(struct foo)); 27 | if (!val) { 28 | perror("malloc"); 29 | return EXIT_FAILURE; 30 | } 31 | val->a = 1; 32 | val->b = 2; 33 | struct foo_arr *arr = foo_arr_new(8, 2, 1); 34 | foo_arr_push(arr, val); 35 | // Do stuff with it; later on... 36 | foo_arr_del(arr, 0); 37 | // Clean things up: 38 | foo_arr_free(arr); 39 | // You'll have to clean up the MEMBERS yourself! 40 | free(val); 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /dyn_dllist/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /dyn_kvp/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /dyn_kvp/examples/one_file/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct foo { 6 | int a; 7 | int b; 8 | }; 9 | 10 | #define DYN_KVP_VALUE_TYPE struct foo 11 | #define DYN_KVP_TYPE_NAME foo_kvp 12 | #include "../../dyn_kvp.h" 13 | #undef DYN_KVP_TYPE_NAME 14 | #undef DYN_KVP_VALUE_TYPE 15 | 16 | #define DYN_KVP_VALUE_TYPE struct foo 17 | #define DYN_KVP_TYPE_NAME foo_kvp 18 | #define DYN_KVP_IMPLEMENTATION 19 | #include "../../dyn_kvp.h" 20 | #undef DYN_KVP_IMPLEMENTATION 21 | #undef DYN_KVP_TYPE_NAME 22 | #undef DYN_KVP_VALUE_TYPE 23 | 24 | int main(void) 25 | { 26 | struct foo *val = malloc(sizeof(struct foo)); 27 | if (!val) { 28 | perror("malloc"); 29 | return EXIT_FAILURE; 30 | } 31 | val->a = 1; 32 | val->b = 2; 33 | struct foo_kvp *hash = foo_kvp_new(8); 34 | // Populate val... 35 | foo_kvp_set(hash, 1, val); 36 | // Do stuff with it; later on... 37 | foo_kvp_del(hash, 1); 38 | // Clean things up: 39 | foo_kvp_free(hash); 40 | // You'll have to clean up the MEMBERS yourself! 41 | free(val); 42 | } 43 | -------------------------------------------------------------------------------- /tap/tap.c: -------------------------------------------------------------------------------- 1 | #include "tap.h" 2 | unsigned int tap_test_number = 0; 3 | void tap_done_testing(void) 4 | { 5 | printf("1..%u\n", tap_test_number); 6 | } 7 | int tap_is_int_(int got, int wanted, char *test_description) 8 | { 9 | tap_test_number++; 10 | if (got == wanted) 11 | printf("ok %u %s\n", tap_test_number, test_description); 12 | else 13 | printf("not ok %u %s (got %d wanted %d)\n", 14 | tap_test_number, test_description, got, wanted); 15 | return got == wanted; 16 | } 17 | int tap_is_ulong_(unsigned long got, unsigned long wanted, char *test_description) 18 | { 19 | tap_test_number++; 20 | if (got == wanted) 21 | printf("ok %u %s\n", tap_test_number, test_description); 22 | else 23 | printf("not ok %u %s (got %lu wanted %lu)\n", 24 | tap_test_number, test_description, got, wanted); 25 | return got == wanted; 26 | } 27 | int tap_is_voidp_(void *got, void *wanted, char *test_description) 28 | { 29 | tap_test_number++; 30 | if (got == wanted) 31 | printf("ok %u %s\n", tap_test_number, test_description); 32 | else 33 | printf("not ok %u %s (got %p wanted %p)\n", 34 | tap_test_number, test_description, got, wanted); 35 | return got == wanted; 36 | } 37 | void tap_run_self_tests(void) 38 | { 39 | tap_diag("Running TAP tests..."); 40 | tap_is_int(1, 1, "1 is 1"); 41 | tap_is_voidp(NULL, NULL, "NULL is NULL"); 42 | char *foo = "foo"; 43 | char *bar = foo; 44 | tap_is_voidp(foo, bar, "foo is bar"); 45 | } 46 | -------------------------------------------------------------------------------- /dyn_arr/tests/basic/remove.c: -------------------------------------------------------------------------------- 1 | #include "basic.h" 2 | #include "../../../tap/tap.h" 3 | 4 | void test_basic_foo_remove(void) 5 | { 6 | tap_diag("Running tests for test_basic_foo_remove()"); 7 | struct basic_foo *f1 = basic_foo_new(1, 2); 8 | struct basic_foo *f2 = basic_foo_new(3, 4); 9 | struct basic_foo *f3 = basic_foo_new(5, 6); 10 | struct bfoo_arr *arr = bfoo_arr_new(32, 4, 2); 11 | { 12 | arr = bfoo_arr_push(arr, f1); 13 | arr = bfoo_arr_push(arr, f2); 14 | arr = bfoo_arr_push(arr, f3); 15 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after push"); 16 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2 after push"); 17 | tap_is_voidp(bfoo_arr_get(arr, 2), f3, "arr get(2) is f3 after push"); 18 | struct basic_foo *p1 = bfoo_arr_pop(arr); 19 | tap_is_voidp(p1, f3, "popped element is f3"); 20 | p1 = bfoo_arr_pop(arr); 21 | tap_is_voidp(p1, f2, "popped element is f2"); 22 | p1 = bfoo_arr_pop(arr); 23 | tap_is_voidp(p1, f1, "popped element is f1"); 24 | tap_is_int(bfoo_arr_length(arr), 0, "arr has length 0 after pops"); 25 | } 26 | { 27 | arr = bfoo_arr_push(arr, f1); 28 | arr = bfoo_arr_push(arr, f2); 29 | arr = bfoo_arr_push(arr, f3); 30 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after push"); 31 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2 after push"); 32 | tap_is_voidp(bfoo_arr_get(arr, 2), f3, "arr get(2) is f3 after push"); 33 | struct basic_foo *p1 = bfoo_arr_shift(arr); 34 | tap_is_voidp(p1, f1, "shifted element is f1"); 35 | p1 = bfoo_arr_shift(arr); 36 | tap_is_voidp(p1, f2, "shifted element is f2"); 37 | p1 = bfoo_arr_shift(arr); 38 | tap_is_voidp(p1, f3, "shifted element is f3"); 39 | tap_is_int(bfoo_arr_length(arr), 0, "arr has length 0 after shifts"); 40 | } 41 | bfoo_arr_free(arr); 42 | basic_foo_free(f1); 43 | basic_foo_free(f2); 44 | basic_foo_free(f3); 45 | } 46 | -------------------------------------------------------------------------------- /sagoma/examples/enabled/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SAGOMA 1 4 | #define SAGOMA_ASSERT 1 5 | #define SAGOMA_CLOCK_CYCLES 1 6 | #include "../../sagoma.h" 7 | 8 | int main(void) 9 | { 10 | printf("Starting...\n"); 11 | sagoma_init(s, "main.c"); 12 | sagoma_block(s, foo); 13 | for (int i = 0; i < 10; i++) 14 | { 15 | sagoma_block(s, bar); 16 | if (i % 2 == 0) 17 | for (int j = 0; j < 6; j++) 18 | { 19 | sagoma_block(s, baz2); 20 | printf("Hello, world[2]! i=%d, j=%d\n", i, j); 21 | sagoma_block_done(s); 22 | } 23 | if (i % 6 == 0) 24 | for (int j = 0; j < 9; j++) 25 | { 26 | sagoma_block(s, baz6); 27 | printf("Hello, world[6]! i=%d, j=%d\n", i, j); 28 | sagoma_block_done(s); 29 | } 30 | if (i % 3 == 0) 31 | for (int j = 0; j < 5; j++) 32 | { 33 | sagoma_block(s, baz3); 34 | printf("Hello, world[3]! i=%d, j=%d\n", i, j); 35 | if (j % 2 == 0) 36 | { 37 | sagoma_block(s, baz3_mod2); 38 | printf("Hello, world[3][mod2]! i=%d, j=%d\n", i, j); 39 | sagoma_block_done(s); 40 | } 41 | sagoma_block_done(s); 42 | } 43 | if (i % 5 == 0) 44 | for (int j = 0; j < 5; j++) 45 | { 46 | sagoma_block(s, baz5); 47 | printf("Hello, world[5]! i=%d, j=%d\n", i, j); 48 | sagoma_block_done(s); 49 | } 50 | sagoma_block_done(s); 51 | } 52 | for (int i = 0; i < 5; i++) 53 | { 54 | sagoma_block(s, quux); 55 | printf("Hello, world! i=%d\n", i); 56 | sagoma_block_done(s); 57 | } 58 | sagoma_block_done(s); 59 | sagoma_print(s, 0, -1); 60 | sagoma_print_color(s, 0, -1); 61 | printf("Done...\n"); 62 | 63 | // sagoma_init_loop(s, "pippo"); 64 | } 65 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL Advanced" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: '39 9 * * 1' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze C 14 | runs-on: 'ubuntu-latest' 15 | permissions: 16 | # required for all workflows 17 | security-events: write 18 | # required to fetch internal or private CodeQL packs 19 | packages: read 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | # Initializes the CodeQL tools for scanning. 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v3 28 | with: 29 | languages: c 30 | build-mode: manual 31 | 32 | - name: test dyn_arr 33 | shell: bash 34 | run: | 35 | cd dyn_arr/tests 36 | make test 37 | prove -v ./test 38 | cd ../examples/makefile_gen/ 39 | make 40 | ./makefile_gen 41 | cd ../one_file/ 42 | gcc -o main main.c 43 | ./main 44 | cd ../two_files/ 45 | make 46 | ./two_files 47 | 48 | - name: test dyn_dllist 49 | shell: bash 50 | run: | 51 | cd dyn_dllist/tests 52 | make test 53 | prove -v ./test 54 | cd ../examples/makefile_gen/ 55 | make 56 | ./makefile_gen 57 | cd ../one_file/ 58 | gcc -o main main.c 59 | ./main 60 | cd ../two_files/ 61 | make 62 | ./two_files 63 | 64 | - name: test dyn_kvp 65 | shell: bash 66 | run: | 67 | cd dyn_kvp/tests 68 | make test 69 | prove -v ./test 70 | cd ../examples/makefile_gen/ 71 | make 72 | ./makefile_gen 73 | cd ../one_file/ 74 | gcc -o main main.c 75 | ./main 76 | cd ../two_files/ 77 | make 78 | ./two_files 79 | 80 | - name: test sagoma 81 | shell: bash 82 | run: | 83 | cd sagoma/tests 84 | make test 85 | prove -v ./test 86 | cd ../examples/disabled/ 87 | gcc -o main main.c -lm 88 | ./main 89 | cd ../enabled/ 90 | gcc -o main main.c -lm 91 | ./main 92 | 93 | - name: Perform CodeQL Analysis 94 | uses: github/codeql-action/analyze@v3 95 | with: 96 | category: "/language:${{matrix.language}}" 97 | -------------------------------------------------------------------------------- /dyn_arr/tests/basic/add.c: -------------------------------------------------------------------------------- 1 | #include "basic.h" 2 | #include "../../../tap/tap.h" 3 | 4 | void test_basic_foo_add(void) 5 | { 6 | tap_diag("Running tests for test_basic_foo_add()"); 7 | struct basic_foo *f1 = basic_foo_new(1, 2); 8 | struct basic_foo *f2 = basic_foo_new(3, 4); 9 | struct basic_foo *f3 = basic_foo_new(5, 6); 10 | struct bfoo_arr *arr = bfoo_arr_new(32, 4, 2); 11 | { 12 | arr = bfoo_arr_push(arr, f1); 13 | arr = bfoo_arr_push(arr, f2); 14 | arr = bfoo_arr_push(arr, f3); 15 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after push"); 16 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2 after push"); 17 | tap_is_voidp(bfoo_arr_get(arr, 2), f3, "arr get(2) is f3 after push"); 18 | arr = bfoo_arr_unshift(arr, f3); 19 | tap_is_voidp(bfoo_arr_get(arr, 0), f3, "arr get(0) is f3 after unshift"); 20 | tap_is_voidp(bfoo_arr_get(arr, 1), f1, "arr get(1) is f1 after unshift"); 21 | tap_is_voidp(bfoo_arr_get(arr, 2), f2, "arr get(2) is f2 after unshift"); 22 | tap_is_voidp(bfoo_arr_get(arr, 3), f3, "arr get(3) is f3 after unshift"); 23 | bfoo_arr_clear(arr); 24 | } 25 | { 26 | arr = bfoo_arr_fill(arr, 3, f1); 27 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after fill"); 28 | tap_is_voidp(bfoo_arr_get(arr, 1), f1, "arr get(1) is f2 after fill"); 29 | tap_is_voidp(bfoo_arr_get(arr, 2), f1, "arr get(2) is f3 after fill"); 30 | tap_is_voidp(bfoo_arr_get(arr, 3), NULL, "arr get(3) is NULL after fill"); 31 | bfoo_arr_clear(arr); 32 | } 33 | { 34 | arr = bfoo_arr_fill(arr, 3, f1); 35 | tap_is_int(bfoo_arr_length(arr), 3, "arr has length 3 after fill"); 36 | bfoo_arr_unique(arr); 37 | tap_is_int(bfoo_arr_length(arr), 1, "arr has length 1 after unique"); 38 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after unique"); 39 | tap_is_voidp(bfoo_arr_get(arr, 1), NULL, "arr get(1) is NULL after unique"); 40 | bfoo_arr_clear(arr); 41 | } 42 | { 43 | tap_is_int(bfoo_arr_length(arr), 0, "arr has length 0"); 44 | bfoo_arr_set(arr, 0, f1); 45 | tap_is_voidp(bfoo_arr_get(arr, 0), NULL, "arr get(0) is NULL after set"); 46 | bfoo_arr_push(arr, f1); 47 | bfoo_arr_set(arr, 0, f2); 48 | tap_is_voidp(bfoo_arr_get(arr, 0), f2, "arr get(0) is f2 after set"); 49 | bfoo_arr_clear(arr); 50 | } 51 | bfoo_arr_free(arr); 52 | basic_foo_free(f1); 53 | basic_foo_free(f2); 54 | basic_foo_free(f3); 55 | } 56 | -------------------------------------------------------------------------------- /dyn_arr/tests/basic/iterate.c: -------------------------------------------------------------------------------- 1 | #include "basic.h" 2 | #include "../../../tap/tap.h" 3 | 4 | int sum_all(struct bfoo_arr *arr, struct basic_foo *it, void *sum) 5 | { 6 | int *isum = (int *)sum; 7 | *isum += it->a; 8 | return 0; 9 | } 10 | 11 | int sum_even(struct bfoo_arr *arr, struct basic_foo *it, void *sum) 12 | { 13 | if (it->a % 2 == 0) { 14 | int *isum = (int *)sum; 15 | *isum += it->a; 16 | } 17 | return 0; 18 | } 19 | 20 | int sum_odd(struct bfoo_arr *arr, struct basic_foo *it, void *sum) 21 | { 22 | if (it->a % 2 == 1) { 23 | int *isum = (int *)sum; 24 | *isum += it->a; 25 | } 26 | return 0; 27 | } 28 | 29 | int sum_stop_at_sum_over_2(struct bfoo_arr *arr, struct basic_foo *it, void *sum) 30 | { 31 | int *isum = (int *)sum; 32 | *isum += it->a; 33 | if (*isum > 2) { 34 | return 1; 35 | } 36 | return 0; 37 | } 38 | 39 | void test_basic_foo_iterate(void) 40 | { 41 | tap_diag("Running tests for test_basic_foo_iterate()"); 42 | struct basic_foo *f1 = basic_foo_new(1, 2); 43 | struct basic_foo *f2 = basic_foo_new(2, 4); 44 | struct basic_foo *f3 = basic_foo_new(5, 6); 45 | struct bfoo_arr *arr = bfoo_arr_new(32, 4, 2); 46 | arr = bfoo_arr_push(arr, f1); 47 | arr = bfoo_arr_push(arr, f2); 48 | arr = bfoo_arr_push(arr, f3); 49 | { 50 | int sum = 0; 51 | bfoo_arr_iterate_ftl(arr, sum_all, &sum); 52 | tap_is_int(sum, 8, "sum of all a elements is 8"); 53 | sum = 0; 54 | bfoo_arr_iterate_ftl(arr, sum_even, &sum); 55 | tap_is_int(sum, 2, "sum of even a elements is 2"); 56 | sum = 0; 57 | bfoo_arr_iterate_ftl(arr, sum_odd, &sum); 58 | tap_is_int(sum, 6, "sum of odd a elements is 6"); 59 | sum = 0; 60 | bfoo_arr_iterate_ftl(arr, sum_stop_at_sum_over_2, &sum); 61 | tap_is_int(sum, 3, "sum of a elements over 2 stops at 3"); 62 | } 63 | { 64 | int sum = 0; 65 | bfoo_arr_iterate_ltf(arr, sum_all, &sum); 66 | tap_is_int(sum, 8, "sum of all a elements is 8"); 67 | sum = 0; 68 | bfoo_arr_iterate_ltf(arr, sum_even, &sum); 69 | tap_is_int(sum, 2, "sum of even a elements is 2"); 70 | sum = 0; 71 | bfoo_arr_iterate_ltf(arr, sum_odd, &sum); 72 | tap_is_int(sum, 6, "sum of odd a elements is 6"); 73 | sum = 0; 74 | bfoo_arr_iterate_ltf(arr, sum_stop_at_sum_over_2, &sum); 75 | tap_is_int(sum, 5, "sum of a elements over 2 stops at 5"); 76 | } 77 | bfoo_arr_free(arr); 78 | basic_foo_free(f1); 79 | basic_foo_free(f2); 80 | basic_foo_free(f3); 81 | } 82 | -------------------------------------------------------------------------------- /sagoma/tests/build_info/main.c: -------------------------------------------------------------------------------- 1 | #include "../../../tap/tap.h" 2 | 3 | #define SAGOMA 4 | #define SAGOMA_ASSERT 5 | #define SAGOMA_TRACK_STATS 6 | #define SAGOMA_CLOCK_CYCLES 7 | // #define SAGOMA_DEBUG 8 | // #define SAGOMA_DONT_PRINT 9 | #include "../../sagoma.h" 10 | 11 | void test_build_info(void) 12 | { 13 | tap_diag("Running test for sagoma__build__info()"); 14 | { 15 | struct Sagoma sagoma = {0}; 16 | struct SagomaInfo info = {0}; 17 | info.longest_block_name = 2; 18 | uint64_t cpufreq = sagoma__estimate_cpu_timer_frequency(); 19 | sagoma__build__info(&info, &sagoma, cpufreq, -1); 20 | tap_is_int(info.longest_block_name, 2, "longest_block_name is 2"); 21 | tap_is_int(info.longest_indent, 0, "longest_indent is 0"); 22 | tap_is_int(info.longest_entrance_count, 0, "longest_entrance_count is 0"); 23 | tap_is_int(info.width_max_entrance_count, 1, "width_max_entrance_count is 1"); 24 | tap_is_int(info.longest_cycles_total_time, 0, "longest_cycles_total_time is 0"); 25 | tap_is_int(info.width_max_total_time_s, 1, "width_max_total_time_s is 1"); 26 | tap_is_int(info.width_max_total_time_us, 1, "width_max_total_time_us is 1"); 27 | } 28 | { 29 | uint64_t cpufreq = sagoma__estimate_cpu_timer_frequency(); 30 | // tap_diag("cpufreq = %lu", cpufreq); 31 | struct Sagoma sagoma = {0}; 32 | sagoma.block_count = 1; 33 | sagoma.blocks[0].name = "block"; 34 | sagoma.blocks[0].total_time = cpufreq; 35 | sagoma.blocks[0].exclusive_time = cpufreq; 36 | sagoma.blocks[0].entrance_count = 1; 37 | sagoma.blocks[0].stack_count = 1; 38 | sagoma.blocks[0].indent = 0; 39 | sagoma.blocks[0].min_time = cpufreq; 40 | sagoma.blocks[0].max_time = cpufreq; 41 | struct SagomaInfo info = {0}; 42 | info.longest_block_name = 2; 43 | sagoma__build__info(&info, &sagoma, cpufreq, -1); 44 | tap_is_int(info.longest_block_name, 5, "longest_block_name is 5 for 'block'"); 45 | tap_is_int(info.longest_indent, 0, "longest_indent is 0"); 46 | tap_is_int(info.longest_entrance_count, 1, "longest_entrance_count is 1"); 47 | tap_is_int(info.width_max_entrance_count, 1, "width_max_entrance_count is 1"); 48 | tap_is_ulong(info.longest_cycles_total_time, cpufreq, "longest_cycles_total_time is cpufreq"); 49 | // 1 cpufreq = 1s = 7w 50 | tap_is_int(info.width_max_total_time_s, 1, "width_max_total_time_s is 1"); 51 | // 1 cpufreq = 1,000,000us = 7w 52 | tap_is_int(info.width_max_total_time_us, 7, "width_max_total_time_us is 7"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /dyn_kvp/tests/charp_key/charp_key.c: -------------------------------------------------------------------------------- 1 | #include "charp_key.h" 2 | #include "../../../tap/tap.h" 3 | #include 4 | #include 5 | 6 | struct custom_foo *custom_foo_new(int a, int b) 7 | { 8 | struct custom_foo *f = malloc(sizeof(struct custom_foo)); 9 | assert(f != NULL); 10 | f->a = a; 11 | f->b = b; 12 | return f; 13 | } 14 | void custom_foo_free(struct custom_foo *f) 15 | { 16 | assert(f != NULL); 17 | free(f); 18 | } 19 | 20 | // Hash the "char *" key using this fuction: 21 | size_t custom_foo_hash(size_t size, const char *key) 22 | { 23 | size_t hash = 0; 24 | for (const char *p = key; *p != '\0'; p++) { 25 | hash += *p; 26 | } 27 | return hash % size; 28 | } 29 | 30 | #define DYN_KVP_VALUE_TYPE struct custom_foo 31 | #define DYN_KVP_TYPE_NAME cfoo_kvp 32 | #define DYN_KVP_KEY_TYPE const char * 33 | #define DYN_KVP_HASH_FUNCTION custom_foo_hash 34 | #define DYN_KVP_IMPLEMENTATION 35 | #define DYN_KVP_COMPARE_FUNCTION(a, b) ((a[0] == b[0]) && !strcmp(a, b)) 36 | #include // strcmp 37 | #include "../../dyn_kvp.h" 38 | #undef DYN_KVP_IMPLEMENTATION 39 | #undef DYN_KVP_HASH_FUNCTION 40 | #undef DYN_KVP_KEY_TYPE 41 | #undef DYN_KVP_TYPE_NAME 42 | #undef DYN_KVP_VALUE_TYPE 43 | 44 | void test_charp_foo(void) 45 | { 46 | tap_diag("Running tests for test_charp_foo()"); 47 | { 48 | struct cfoo_kvp *hash = cfoo_kvp_new(8); 49 | tap_is_int(cfoo_kvp_nkeys(hash), 0, "cfoo_kvp_nkeys() returns 0 after cfoo_kvp_new()"); 50 | tap_is_int(cfoo_kvp_exists(hash, "foo"), 0, "cfoo_kvp_exists(foo) returns 0 after cfoo_kvp_new()"); 51 | cfoo_kvp_del(hash, "foo"); // no-op 52 | tap_is_int(cfoo_kvp_exists(hash, "foo"), 0, "cfoo_kvp_exists(foo) returns 0 after cfoo_kvp_del()"); 53 | tap_is_int(cfoo_kvp_nkeys(hash), 0, "cfoo_kvp_nkeys() returns 0 after _del()"); 54 | cfoo_kvp_free(hash); 55 | } 56 | { 57 | struct cfoo_kvp *hash = cfoo_kvp_new(8); 58 | struct custom_foo *c1 = custom_foo_new(1, 2); 59 | cfoo_kvp_set(hash, "foo", c1); 60 | tap_is_int(cfoo_kvp_nkeys(hash), 1, "cfoo_kvp_nkeys() returns 1 after cfoo_kvp_set()"); 61 | char foo1[4]; 62 | foo1[0] = 'f'; 63 | foo1[1] = 'o'; 64 | foo1[2] = 'o'; 65 | foo1[3] = '\0'; 66 | tap_is_int(cfoo_kvp_exists(hash, foo1), 1, "cfoo_kvp_exists(foo) returns 1 after cfoo_kvp_set()"); 67 | tap_is_int(cfoo_kvp_exists(hash, "foo"), 1, "cfoo_kvp_exists(foo) returns 1 after cfoo_kvp_set()"); 68 | tap_is_int(cfoo_kvp_exists(hash, "bar"), 0, "cfoo_kvp_exists(bar) returns 0 after cfoo_kvp_set()"); 69 | tap_is_voidp(cfoo_kvp_get(hash, "foo"), c1, "cfoo_kvp_get(foo) returns c1 after cfoo_kvp_set()"); 70 | tap_is_voidp(cfoo_kvp_get(hash, "bar"), NULL, "cfoo_kvp_get(bar) returns NULL after cfoo_kvp_set()"); 71 | cfoo_kvp_del(hash, "bar"); // no-op 72 | tap_is_int(cfoo_kvp_nkeys(hash), 1, "cfoo_kvp_nkeys() returns 1 after _del()"); 73 | cfoo_kvp_set(hash, "bar", c1); 74 | tap_is_int(cfoo_kvp_nkeys(hash), 2, "cfoo_kvp_nkeys() returns 2 after cfoo_kvp_set()"); 75 | tap_is_int(cfoo_kvp_exists(hash, "bar"), 1, "cfoo_kvp_exists(bar) returns 1 after cfoo_kvp_set()"); 76 | cfoo_kvp_set(hash, "bar", NULL); 77 | tap_is_int(cfoo_kvp_nkeys(hash), 2, "cfoo_kvp_nkeys() returns 2 after cfoo_kvp_set(NULL)"); 78 | tap_is_int(cfoo_kvp_exists(hash, "bar"), 1, "cfoo_kvp_exists(bar) returns 1 after cfoo_kvp_set(NULL)"); 79 | cfoo_kvp_del(hash, "bar"); 80 | tap_is_int(cfoo_kvp_nkeys(hash), 1, "cfoo_kvp_nkeys() returns 1 after _del()"); 81 | tap_is_int(cfoo_kvp_exists(hash, "bar"), 0, "cfoo_kvp_exists(bar) returns 0 after _del()"); 82 | cfoo_kvp_free(hash); 83 | custom_foo_free(c1); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chol - my C Header-Only Libraries 2 | 3 | C header-only libraries I've created, used, and which might be useful to others. 4 | 5 | See each subdirectory for more information, usage, etc. 6 | 7 | # Nota Bene 8 | 9 | Most should be in working condition, but might lack tests or examples or might just be of a bad quality. 10 | 11 | Use at your peril, and/or submit issues and pull requests. 12 | 13 | # What is included? 14 | 15 | ## `dyn_dllist` 16 | 17 | A C "dynamic" doubly-linked list header-only library. 18 | 19 | You choose the type of the member item and the new struct name. You get a bunch of functions you can use to interact with that new struct type: create one, free one, prepend or append an item, iterate the list, or even grep it returning a new one. 20 | 21 | See [dyn_dllist/README.md](dyn_dllist/README.md) for more information. 22 | 23 | ## `dyn_kvp` 24 | 25 | A C "dynamic" key/value pair (aka "hash" or "dict") library. 26 | 27 | You choose the type of the "value" of the pair (the "key" defaults to "unsigned int") and the new struct name. Optionally you can also choose the type of the "key" of the pair and provide a hashing function. You get a bunch of functions you can use to interact with that new struct type: create one, free one, set or get or delete an item from the hash, but also perform intersection or merging into new structures. 28 | 29 | See [dyn_kvp/README.md](dyn_kvp/README.md) for more information. 30 | 31 | ## `dyn_arr` 32 | 33 | A C "dynamic" array-like (vector-like, as the structure has "slack" at either end) header-only library. 34 | 35 | You choose the type of "items" contained in the array and the new struct name. You get a bunch of functions you can use to interact with that new struct type: create one, free one, push or pop an item, iterate the array, pre-fill it with values, or remove duplicate items. 36 | 37 | See [dyn_arr/README.md](dyn_arr/README.md) for more information. 38 | 39 | ## `sagoma` 40 | 41 | A somewhat simple, but powerful, C block profiler which doesn't waste too many resources. 42 | 43 | You choose which overall blocks to profile and print stats for, even down to how many clock cycles those took, or how many min/max time they took if they were called multiple times. There's hooks to make it very easy to use, or more verbose but more tweakable. 44 | 45 | See [sagoma/README.md](sagoma/README.md) for more information. 46 | 47 | ## `tap` 48 | 49 | This is mostly for internal use by this repository, but might be useful to others. 50 | 51 | A minimalistic Test Anything Protocol for C. 52 | 53 | You include the header, and gain the ability to call functions like `test_is_int(a, b, "test name")` and ultimately `tap_done_testing()` once done. 54 | 55 | You gain the ability to `prove ./a.out :: some parameters` and have a nice output for your tests' failures. 56 | 57 | See [tap/README.md](tap/README.md) for more information. 58 | 59 | # LICENSE 60 | 61 | The MIT License (MIT) 62 | 63 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 64 | 65 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 68 | 69 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 70 | -------------------------------------------------------------------------------- /tap/README.md: -------------------------------------------------------------------------------- 1 | # `tap.h` & `tap.c` - a minimalistic Test Anything Protocol for C 2 | 3 | A couple _very_ simple/minimalistic `.c` and `.h` file to write TAP-like tests in C. 4 | 5 | # Usage 6 | 7 | ```c 8 | #include "tap.h" 9 | void my_run_tests(void) 10 | { 11 | int one = 1; 12 | int two = 2; 13 | if (!tap_is_int(one, two, "one == two")) 14 | tap_diag("one=%d != two=%d?!", one, two); 15 | char *foo = "foo"; 16 | char *bar = foo; 17 | if (!tap_is_voidp(foo, bar, "foo == bar")) 18 | tap_diag("foo='%s' != bar='%s'?!", foo, bar); 19 | tap_done_testing(); 20 | } 21 | // Maybe call it from main? 22 | int main(void) 23 | { 24 | my_run_tests(); 25 | } 26 | ``` 27 | 28 | This can then be compiled, and ran via prove: 29 | 30 | ```bash 31 | prove ./program :: --params-for-program 32 | ``` 33 | 34 | # Functions & Macros 35 | 36 | ## `tap_done_testing` 37 | 38 | You **have** to call this once you're done sending `tap_is_*` or `tap_diag`, so that `prove` can know your tests are over, and your program hasn't crashed (which signifies a failure). 39 | 40 | ## `tap_diag(fmt, args)` 41 | 42 | A macro to standardize a printf-like strings, which also prints out "where" in the code the diagnostic is coming from. 43 | 44 | ```c 45 | tap_diag("foo='%s' != bar='%s'?!", foo, bar); 46 | ``` 47 | 48 | Prints: 49 | 50 | ``` 51 | # foo='...' != bar='..' [ at foo.c:1234 ] 52 | ``` 53 | 54 | ## `tap_is_int` 55 | 56 | Sugar to check whether two `int` values are the same. Returns the result of the comparison, so it can be checked in order to provide further diagnostics in case the two values don't match, i.e.: 57 | 58 | ```c 59 | int one1 = 1; 60 | int one2 = one1; 61 | if (!tap_is_int(one1, one2, "one1 == one2")) 62 | tap_diag("one1=%d != one2=%d?!", one1, one2); 63 | ``` 64 | 65 | If the comparison works, it prints: 66 | 67 | ``` 68 | ok 1 one1 == one2 [ at foo.c:1234 ] 69 | ``` 70 | 71 | If it fails (i.e. assume `int one2 = 2;`, above), it prints: 72 | 73 | ``` 74 | not ok 1 one1 == one2 (got 1, wanted 2) [ at foo.c:1234 ] 75 | # one1=1 != one2=2?! 76 | ``` 77 | 78 | ## `tap_is_ulong` 79 | 80 | Same as `tap_is_int`, but for `unsigned long` arguments. 81 | 82 | ## `tap_is_voidp` 83 | 84 | Sugar to check whether two `void *` values point to the same place. Returns the result of the comparison. 85 | 86 | ```c 87 | char *foo = "foo"; 88 | char *bar = foo; 89 | if (!tap_is_voidp(foo, bar, "foo == bar")) 90 | tap_diag("foo='%s' != bar='%s'?!", foo, bar); 91 | ``` 92 | 93 | If the comparison works, it prints: 94 | 95 | ``` 96 | ok 1 foo == bar [ at foo.c:1234 ] 97 | ``` 98 | 99 | If it fails (i.e. assume `char *bar = "bar";`, above), it prints: 100 | 101 | ``` 102 | not ok 1 foo == bar (got 0x12345, wanted 0xf0f0f0) [ at foo.c:1234 ] 103 | # foo='foo' != bar='bar'?! 104 | ``` 105 | 106 | # LICENSE 107 | 108 | The MIT License (MIT) 109 | 110 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 111 | 112 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 113 | 114 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 115 | 116 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 117 | -------------------------------------------------------------------------------- /sagoma/tests/ez/main.c: -------------------------------------------------------------------------------- 1 | #include "../../../tap/tap.h" 2 | #include // for usleep 3 | 4 | #define SAGOMA 5 | #define SAGOMA_ASSERT 6 | #define SAGOMA_TRACK_STATS 7 | #define SAGOMA_CLOCK_CYCLES 8 | #define SAGOMA_DEBUG 9 | // #define SAGOMA_DONT_PRINT 10 | #include "../../sagoma.h" 11 | 12 | void test_ez(void) 13 | { 14 | tap_diag("Running test for sagoma_this()"); 15 | { 16 | sagoma_this( 17 | tap_is_int(sagoma.start_tsc > 0, 1, "sagoma.start_tsc > 0"); 18 | tap_is_int(sagoma.block_count, 1, "sagoma.block_count == 1 (the main block)"); 19 | tap_is_int(sagoma.stack_count, 1, "sagoma.stack_count == 1 (the main block)"); 20 | tap_is_int(sagoma.blocks[0].stack_count, 1, "sagoma.block[0].stack_count == 1 (as profile going on)"); 21 | tap_is_int(sagoma.end_tsc, 0, "sagoma.end_tsc == 0 (as profile going on)"); 22 | usleep(100); 23 | ); 24 | tap_is_int(sagoma.end_tsc > sagoma.start_tsc, 1, "sagoma.end_tsc > sagoma.start_tsc"); 25 | tap_is_int(sagoma.block_count, 1, "sagoma.block_count == 1"); 26 | tap_is_int(sagoma.stack_count, 0, "sagoma.stack_count == 0 (as profile completed / blocks popped)"); 27 | tap_is_int(sagoma.blocks[0].entrance_count, 1, "sagoma.block[0].entrance_count == 1"); 28 | tap_is_int(sagoma.blocks[1].entrance_count, 0, "sagoma.block[1].entrance_count == 0"); 29 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 30 | tap_is_voidp(sagoma.stacks[1].block, NULL, "sagoma.stacks[1].block == NULL"); 31 | } 32 | { 33 | sagoma_this( 34 | usleep(100); 35 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 36 | tap_is_voidp(sagoma.stacks[1].block, NULL, "sagoma.stacks[1].block == NULL"); 37 | tap_is_voidp(sagoma.stacks[2].block, NULL, "sagoma.stacks[2].block == NULL"); 38 | sagoma_this_block("second usleep", 39 | usleep(100); 40 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 41 | tap_is_voidp(sagoma.stacks[1].block, &sagoma.blocks[1], "sagoma.stacks[1].block == &sagoma.blocks[1]"); 42 | tap_is_voidp(sagoma.stacks[2].block, NULL, "sagoma.stacks[2].block == NULL"); 43 | ); 44 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 45 | tap_is_voidp(sagoma.stacks[1].block, &sagoma.blocks[1], "sagoma.stacks[1].block == &sagoma.blocks[1]"); 46 | tap_is_voidp(sagoma.stacks[2].block, NULL, "sagoma.stacks[2].block == NULL"); 47 | sagoma_this_block("third usleep", 48 | usleep(100); 49 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 50 | tap_is_voidp(sagoma.stacks[1].block, &sagoma.blocks[2], "sagoma.stacks[1].block == &sagoma.blocks[2]"); 51 | tap_is_voidp(sagoma.stacks[2].block, NULL, "sagoma.stacks[2].block == NULL"); 52 | ); 53 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 54 | tap_is_voidp(sagoma.stacks[1].block, &sagoma.blocks[2], "sagoma.stacks[1].block == &sagoma.blocks[2]"); 55 | tap_is_voidp(sagoma.stacks[2].block, NULL, "sagoma.stacks[2].block == NULL"); 56 | ); 57 | tap_is_int(sagoma.end_tsc > sagoma.start_tsc, 1, "sagoma.end_tsc > sagoma.start_tsc"); 58 | tap_is_int(sagoma.block_count, 3, "sagoma.block_count == 3"); 59 | tap_is_int(sagoma.stack_count, 0, "sagoma.stack_count == 0 (as profile completed / blocks popped)"); 60 | tap_is_int(sagoma.blocks[0].entrance_count, 1, "sagoma.block[0].entrance_count == 1"); 61 | tap_is_int(sagoma.blocks[1].entrance_count, 1, "sagoma.block[1].entrance_count == 1"); 62 | tap_is_int(sagoma.blocks[2].entrance_count, 1, "sagoma.block[2].entrance_count == 1"); 63 | tap_is_int(sagoma.blocks[3].entrance_count, 0, "sagoma.block[3].entrance_count == 0"); 64 | tap_is_voidp(sagoma.stacks[0].block, &sagoma.blocks[0], "sagoma.stacks[0].block == &sagoma.blocks[0]"); 65 | tap_is_voidp(sagoma.stacks[1].block, &sagoma.blocks[2], "sagoma.stacks[1].block == &sagoma.blocks[2]"); 66 | tap_is_voidp(sagoma.stacks[2].block, NULL, "sagoma.stacks[2].block == NULL"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "Compile and test" 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | jobs: 13 | exhaustive-tests: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: dyn_dllist all functions tested at least once 18 | run: | 19 | set +e 20 | cd dyn_dllist 21 | used_in_tests=$( perl -nE'm!DYN_DLLIST_F\(DYN_DLLIST_TYPE_NAME, (_\w+)\)\((void|struct DYN_DLLIST_TYPE_NAME.*).+\);$! and say $1' dyn_dllist.h | while read -r which ; do printf '%-20s %s\n' "$which" "$( git grep -o "$which" -- tests | wc -l )" ; done | sort -rnk2 ) 22 | not_tested=$( printf '%s\n' "$used_in_tests" | grep '[ ]0$' ) 23 | if [[ -n "$not_tested" ]]; then 24 | >&2 echo "dyn_dllist NOT TESTED:" 25 | printf '%s\n' "$not_tested" 26 | exit 1 27 | fi 28 | echo "dyn_dllist tests by function:" 29 | printf '%s\n' "$used_in_tests" 30 | - name: dyn_kvp all functions tested at least once 31 | run: | 32 | set +e 33 | cd dyn_kvp 34 | used_in_tests=$( perl -nE'm!DYN_KVP_F\(DYN_KVP_TYPE_NAME, (_\w+)\)\((void|struct DYN_KVP_TYPE_NAME.*).+\);$! and say $1' dyn_kvp.h | while read -r which ; do printf '%-20s %s\n' "$which" "$( git grep -o "$which" -- tests | wc -l )" ; done | sort -rnk2 ) 35 | not_tested=$( printf '%s\n' "$used_in_tests" | grep -v _stats_printf | grep '[ ]0$' ) 36 | if [[ -n "$not_tested" ]]; then 37 | >&2 echo "dyn_kvp NOT TESTED:" 38 | printf '%s\n' "$not_tested" 39 | exit 1 40 | fi 41 | echo "dyn_kvp tests by function:" 42 | printf '%s\n' "$used_in_tests" 43 | - name: dyn_arr all functions tested at least once 44 | run: | 45 | set +e 46 | cd dyn_arr 47 | used_in_tests=$( perl -nE'm!DYN_ARR_F\(DYN_ARR_TYPE_NAME, (_\w+)\)\((void|struct DYN_ARR_TYPE_NAME.*).+\);$! and say $1' dyn_arr.h | while read -r which ; do printf '%-20s %s\n' "$which" "$( git grep -o "$which" -- tests | wc -l )" ; done | sort -rnk2 ) 48 | not_tested=$( printf '%s\n' "$used_in_tests" | grep -v _status | grep '[ ]0$' ) 49 | if [[ -n "$not_tested" ]]; then 50 | >&2 echo "dyn_arr NOT TESTED:" 51 | printf '%s\n' "$not_tested" 52 | exit 1 53 | fi 54 | echo "dyn_arr tests by function:" 55 | printf '%s\n' "$used_in_tests" 56 | debian-bullseye: 57 | needs: exhaustive-tests 58 | if: always() 59 | runs-on: ubuntu-latest 60 | container: debian:bullseye 61 | steps: 62 | - uses: actions/checkout@v4 63 | - name: install prereqs 64 | timeout-minutes: 10 65 | run: | 66 | apt-get -qq update 67 | apt-get -qq install -y build-essential git-core perl valgrind 68 | - name: dyn_dllist examples 69 | timeout-minutes: 2 70 | run: | 71 | cd dyn_dllist 72 | ( cd examples/one_file ; gcc -o ./one_file main.c ; ./one_file ) 73 | ( cd examples/two_files ; make ; ./two_files ) 74 | ( cd examples/makefile_gen ; make ; ./makefile_gen ) 75 | - name: dyn_dllist tests 76 | timeout-minutes: 2 77 | run: | 78 | cd dyn_dllist/tests 79 | make 80 | - name: dyn_kvp examples 81 | timeout-minutes: 2 82 | run: | 83 | cd dyn_kvp 84 | ( cd examples/one_file ; gcc -o ./one_file main.c ; ./one_file ) 85 | ( cd examples/two_files ; make ; ./two_files ) 86 | ( cd examples/makefile_gen ; make ; ./makefile_gen ) 87 | - name: dyn_kvp tests 88 | timeout-minutes: 2 89 | run: | 90 | cd dyn_kvp/tests 91 | make 92 | - name: dyn_arr examples 93 | timeout-minutes: 2 94 | run: | 95 | cd dyn_arr 96 | ( cd examples/one_file ; gcc -o ./one_file main.c ; ./one_file ) 97 | ( cd examples/two_files ; make ; ./two_files ) 98 | ( cd examples/makefile_gen ; make ; ./makefile_gen ) 99 | - name: dyn_arr tests 100 | timeout-minutes: 2 101 | run: | 102 | cd dyn_arr/tests 103 | make 104 | - name: sagoma examples 105 | timeout-minutes: 2 106 | run: | 107 | cd sagoma 108 | ( cd examples/disabled ; gcc -o ./main main.c -lm ; ./main ) 109 | ( cd examples/enabled ; gcc -o ./main main.c -lm ; ./main ) 110 | - name: sagoma tests 111 | timeout-minutes: 2 112 | run: | 113 | cd sagoma/tests 114 | make 115 | debian-bookworm: 116 | needs: exhaustive-tests 117 | if: always() 118 | runs-on: ubuntu-latest 119 | container: debian:bookworm 120 | steps: 121 | - uses: actions/checkout@v4 122 | - name: install prereqs 123 | timeout-minutes: 10 124 | run: | 125 | apt-get -qq update 126 | apt-get -qq install -y build-essential git-core perl valgrind 127 | - name: dyn_dllist examples 128 | timeout-minutes: 2 129 | run: | 130 | cd dyn_dllist 131 | ( cd examples/one_file ; gcc -o ./one_file main.c ; ./one_file ) 132 | ( cd examples/two_files ; make ; ./two_files ) 133 | ( cd examples/makefile_gen ; make ; ./makefile_gen ) 134 | - name: dyn_dllist tests 135 | timeout-minutes: 2 136 | run: | 137 | cd dyn_dllist/tests 138 | make 139 | - name: dyn_kvp examples 140 | timeout-minutes: 2 141 | run: | 142 | cd dyn_kvp 143 | ( cd examples/one_file ; gcc -o ./one_file main.c ; ./one_file ) 144 | ( cd examples/two_files ; make ; ./two_files ) 145 | ( cd examples/makefile_gen ; make ; ./makefile_gen ) 146 | - name: dyn_kvp tests 147 | timeout-minutes: 2 148 | run: | 149 | cd dyn_kvp/tests 150 | make 151 | - name: dyn_arr examples 152 | timeout-minutes: 2 153 | run: | 154 | cd dyn_arr 155 | ( cd examples/one_file ; gcc -o ./one_file main.c ; ./one_file ) 156 | ( cd examples/two_files ; make ; ./two_files ) 157 | ( cd examples/makefile_gen ; make ; ./makefile_gen ) 158 | - name: dyn_arr tests 159 | timeout-minutes: 2 160 | run: | 161 | cd dyn_arr/tests 162 | make 163 | - name: sagoma examples 164 | timeout-minutes: 2 165 | run: | 166 | cd sagoma 167 | ( cd examples/disabled ; gcc -o ./main main.c -lm ; ./main ) 168 | ( cd examples/enabled ; gcc -o ./main main.c -lm ; ./main ) 169 | - name: sagoma tests 170 | timeout-minutes: 2 171 | run: | 172 | cd sagoma/tests 173 | make 174 | -------------------------------------------------------------------------------- /dyn_arr/tests/basic/basic.c: -------------------------------------------------------------------------------- 1 | #include "basic.h" 2 | #include "../../../tap/tap.h" 3 | #include 4 | 5 | struct basic_foo *basic_foo_new(int a, int b) 6 | { 7 | struct basic_foo *f = malloc(sizeof(struct basic_foo)); 8 | assert(f != NULL); 9 | f->a = a; 10 | f->b = b; 11 | return f; 12 | } 13 | void basic_foo_free(struct basic_foo *f) 14 | { 15 | assert(f != NULL); 16 | free(f); 17 | } 18 | 19 | #define DYN_ARR_VALUE_TYPE struct basic_foo 20 | #define DYN_ARR_TYPE_NAME bfoo_arr 21 | #define DYN_ARR_IMPLEMENTATION 22 | #include "../../dyn_arr.h" 23 | #undef DYN_ARR_IMPLEMENTATION 24 | #undef DYN_ARR_TYPE_NAME 25 | #undef DYN_ARR_VALUE_TYPE 26 | 27 | void test_basic_foo(void) 28 | { 29 | tap_diag("Running tests for test_basic_foo()"); 30 | struct basic_foo *f1 = basic_foo_new(1, 2); 31 | struct basic_foo *f2 = basic_foo_new(3, 4); 32 | struct basic_foo *f3 = basic_foo_new(5, 6); 33 | struct bfoo_arr *arr = bfoo_arr_new(32, 4, 2); 34 | tap_is_int(bfoo_arr_length(arr), 0, "new arr is empty (length 0)"); 35 | tap_is_int(bfoo_arr_empty(arr), 1, "new arr is empty (empty 1)"); 36 | tap_is_int(bfoo_arr_size(arr), 32, "new arr size is as given"); 37 | tap_is_voidp(bfoo_arr_front(arr), NULL, "arr front is NULL"); 38 | tap_is_voidp(bfoo_arr_back(arr), NULL, "arr back is NULL"); 39 | tap_is_int(arr->istart, 3, "arr istart is 3"); 40 | tap_is_int(arr->iend, 3, "arr iend is 3"); 41 | arr = bfoo_arr_push(arr, f1); 42 | tap_is_int(arr->istart, 3, "arr istart is still 3 after push"); 43 | tap_is_int(arr->iend, 3, "arr iend is 3 after push"); 44 | tap_is_int(bfoo_arr_length(arr), 1, "arr has length 1 after push"); 45 | tap_is_int(bfoo_arr_empty(arr), 0, "arr is not empty after push"); 46 | tap_is_int(bfoo_arr_size(arr), 32, "arr size is still 32 after push"); 47 | tap_is_voidp(bfoo_arr_front(arr), f1, "arr front is f1 after push"); 48 | tap_is_voidp(bfoo_arr_back(arr), f1, "arr back is f1 after push"); 49 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after push"); 50 | arr = bfoo_arr_push(arr, f2); 51 | tap_is_int(arr->istart, 3, "arr istart is still 3 after push"); 52 | tap_is_int(arr->iend, 4, "arr iend is 4 after push"); 53 | tap_is_int(bfoo_arr_length(arr), 2, "arr has length 2 after push"); 54 | tap_is_int(bfoo_arr_empty(arr), 0, "arr is not empty after push"); 55 | tap_is_int(bfoo_arr_size(arr), 32, "arr size is still 32 after push"); 56 | tap_is_voidp(bfoo_arr_front(arr), f1, "arr front is f1 after push"); 57 | tap_is_voidp(bfoo_arr_back(arr), f2, "arr back is f2 after push"); 58 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after push"); 59 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2 after push"); 60 | arr = bfoo_arr_push(arr, f3); 61 | tap_is_int(arr->istart, 3, "arr istart is still 3 after push"); 62 | tap_is_int(arr->iend, 5, "arr iend is 5 after push"); 63 | tap_is_int(bfoo_arr_length(arr), 3, "arr has length 3 after push"); 64 | tap_is_int(bfoo_arr_empty(arr), 0, "arr is not empty after push"); 65 | tap_is_int(bfoo_arr_size(arr), 32, "arr size is still 32 after push"); 66 | tap_is_voidp(bfoo_arr_front(arr), f1, "arr front is f1 after push"); 67 | tap_is_voidp(bfoo_arr_back(arr), f3, "arr back is f3 after push"); 68 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after push"); 69 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2 after push"); 70 | tap_is_voidp(bfoo_arr_get(arr, 2), f3, "arr get(2) is f3 after push"); 71 | { 72 | struct basic_foo *p1 = bfoo_arr_pop(arr); 73 | tap_is_voidp(p1, f3, "popped element is f3"); 74 | tap_is_int(arr->istart, 3, "arr istart is still 3 after pop"); 75 | tap_is_int(arr->iend, 4, "arr iend is now 4 after pop"); 76 | tap_is_int(bfoo_arr_length(arr), 2, "arr has length 2 after pop"); 77 | tap_is_int(bfoo_arr_empty(arr), 0, "arr is not empty after pop"); 78 | tap_is_int(bfoo_arr_size(arr), 32, "arr size is still 32 after pop"); 79 | tap_is_voidp(bfoo_arr_front(arr), f1, "arr front is f1 after pop"); 80 | tap_is_voidp(bfoo_arr_back(arr), f2, "arr back is f2 after pop"); 81 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1 after pop"); 82 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2 after pop"); 83 | } 84 | { 85 | struct basic_foo *p2 = bfoo_arr_shift(arr); 86 | tap_is_voidp(p2, f1, "shifted element is f1"); 87 | tap_is_int(bfoo_arr_length(arr), 1, "arr has length 1 after shift"); 88 | tap_is_int(bfoo_arr_empty(arr), 0, "arr is not empty after shift"); 89 | tap_is_int(bfoo_arr_size(arr), 32, "arr size is still 32 after shift"); 90 | tap_is_voidp(bfoo_arr_front(arr), f2, "arr front is f2 after shift"); 91 | tap_is_voidp(bfoo_arr_back(arr), f2, "arr back is f2 after shift"); 92 | tap_is_voidp(bfoo_arr_get(arr, 0), f2, "arr get(0) is f2 after shift"); 93 | } 94 | { 95 | bfoo_arr_clear(arr); 96 | tap_is_int(bfoo_arr_length(arr), 0, "arr has length 0 after clear"); 97 | tap_is_int(bfoo_arr_empty(arr), 1, "arr is empty after clear"); 98 | tap_is_int(bfoo_arr_size(arr), 32, "arr size is still 32 after clear"); 99 | tap_is_voidp(bfoo_arr_front(arr), NULL, "arr front is NULL after clear"); 100 | tap_is_voidp(bfoo_arr_back(arr), NULL, "arr back is NULL after clear"); 101 | } 102 | { 103 | struct bfoo_arr *orig = arr; 104 | for (int i = 0; i < 32; i++) { 105 | tap_diag("Pushing element %d", i); 106 | tap_is_int(bfoo_arr_length(arr), i, "arr length is i before push"); 107 | struct bfoo_arr *newarr = bfoo_arr_push(arr, f1); 108 | // 32 109 | // 4 leeway, then items 0..31 110 | // min_buffer 2, so when i == 27 (26+1 + 4 = 31 items, just over 111 | // min_buffer), we should double the size 112 | if (i >= 27) { // 32 total, 4 leeway either side 113 | tap_is_int(bfoo_arr_size(newarr), 64, "newarr size is doubled after push"); 114 | tap_is_int(orig != newarr, 1, "newarr is a new pointer after push"); 115 | } else { 116 | tap_is_int(bfoo_arr_size(newarr), 32, "newarr size is still 32 after push"); 117 | tap_is_int(orig == newarr, 1, "arr is the same pointer after push"); 118 | } 119 | arr = newarr; 120 | } 121 | bfoo_arr_clear(arr); 122 | tap_is_int(bfoo_arr_length(arr), 0, "arr has length 0 after clear"); 123 | tap_is_int(bfoo_arr_empty(arr), 1, "arr is empty after clear"); 124 | tap_is_int(bfoo_arr_size(arr), 64, "arr size is now 64 after clear"); 125 | tap_is_int(orig != arr, 1, "arr is a new pointer after loop"); 126 | } 127 | { 128 | arr = bfoo_arr_unshift(arr, f2); 129 | tap_is_int(bfoo_arr_length(arr), 1, "arr has length 1 after unshift"); 130 | tap_is_int(bfoo_arr_empty(arr), 0, "arr is not empty after unshift"); 131 | tap_is_int(bfoo_arr_size(arr), 64, "arr size is still 64 after unshift"); 132 | tap_is_int(arr->istart, 3, "arr istart is 3 after unshift"); 133 | tap_is_int(arr->iend, 3, "arr iend is 3 after unshift"); 134 | bfoo_arr_clear(arr); 135 | tap_is_int(bfoo_arr_length(arr), 0, "arr has length 0 after clear"); 136 | tap_is_int(bfoo_arr_empty(arr), 1, "arr is empty after clear"); 137 | tap_is_int(bfoo_arr_size(arr), 64, "arr size is now 64 after clear"); 138 | } 139 | { 140 | arr = bfoo_arr_push(arr, f1); 141 | arr = bfoo_arr_push(arr, f2); 142 | arr = bfoo_arr_push(arr, f3); 143 | tap_is_int(bfoo_arr_length(arr), 3, "arr has length 3"); 144 | tap_is_int(arr->istart, 3, "arr istart is 3"); 145 | tap_is_int(arr->iend, 5, "arr iend is 5"); 146 | tap_is_voidp(bfoo_arr_get(arr, 0), f1, "arr get(0) is f1"); 147 | tap_is_voidp(bfoo_arr_get(arr, 1), f2, "arr get(1) is f2"); 148 | tap_is_voidp(bfoo_arr_get(arr, 2), f3, "arr get(2) is f3"); 149 | struct bfoo_arr *newarr = bfoo_arr_resize(arr, 20, 6, 2); 150 | tap_is_int(bfoo_arr_length(newarr), 3, "newarr has length 3 after resize"); 151 | tap_is_voidp(bfoo_arr_get(newarr, 0), f1, "newarr get(0) is f1"); 152 | tap_is_voidp(bfoo_arr_get(newarr, 1), f2, "newarr get(1) is f2"); 153 | tap_is_voidp(bfoo_arr_get(newarr, 2), f3, "newarr get(2) is f3"); 154 | tap_is_int(newarr->istart, 5, "newarr istart is 5 after resize"); 155 | tap_is_int(newarr->iend, 7, "newarr iend is 7 after resize"); 156 | bfoo_arr_remove_all(newarr, f2); 157 | tap_is_int(bfoo_arr_length(newarr), 2, "newarr has length 2 after remove_all"); 158 | tap_is_voidp(bfoo_arr_get(newarr, 0), f1, "newarr get(0) is f1 after remove_all"); 159 | tap_is_voidp(bfoo_arr_get(newarr, 1), f3, "newarr get(1) is f3 after remove_all"); 160 | bfoo_arr_del(newarr, 0); 161 | tap_is_int(bfoo_arr_length(newarr), 1, "newarr has length 1 after del"); 162 | tap_is_voidp(bfoo_arr_get(newarr, 0), f3, "newarr get(0) is f3 after del"); 163 | bfoo_arr_free(newarr); 164 | bfoo_arr_clear(arr); 165 | } 166 | bfoo_arr_free(arr); 167 | basic_foo_free(f1); 168 | basic_foo_free(f2); 169 | basic_foo_free(f3); 170 | } 171 | -------------------------------------------------------------------------------- /sagoma/README.md: -------------------------------------------------------------------------------- 1 | # `sagoma.h` - a simple C block profiler 2 | 3 | A somewhat simple, but powerful, profiler which doesn't waste too many resources. 4 | 5 | Use it to profile what you need. As little or as much as you need. 6 | 7 | NOTA BENE: The more features you enable, the more impact the profiler _will_ have on your code! 8 | 9 | # NOTA BENE 10 | 11 | This is a work in progress. Much needs documented still. 12 | 13 | # Configuration 14 | 15 | If you'd like profiling to be "fully" performed, you'll want to `#define SAGOMA` before `#include "sagoma.h"`. In that case, you'll need to compile with `-lm`. 16 | 17 | Otherwise, it'll only print the overall run time for the `sagoma` section. You can opt out of that via `#define SAGOMA_DONT_PRINT`. 18 | 19 | Other configuration options are: 20 | 21 | - `#define SAGOMA_ASSERT` - asserts that there's as many `sagoma_block` as `sagoma_block_done`, etc. You likely want to enable this while you're setting up the profiler in your code. 22 | - `#define SAGOMA_TRACK_STATS` - use this if you'd like to keep track of the min/max time taken in each block. This doesn't come for free. 23 | - `#define SAGOMA_CLOCK_CYCLES` - use this if you'd like to look at the "real" `__rdtsc()` "time" spent for each block 24 | - `#define CPU_FREQ_ESTIMATE_WAIT_MSECS 100` how many milliseconds to spend estimating the CPU clock time. About 100ms does it. Only called once per program run, when stats are printed. 25 | 26 | # Minimal "easy" usage 27 | 28 | ## `sagoma_this` and `sagoma_this_block` 29 | 30 | The `sagoma_this(code)` outputs the total time of the `code`. You can add `sagoma_this_block("block name", code)` inside it, to separately keep track of sub-blocks. 31 | 32 |
33 | Example `sagoma_this` and `sagoma_this_block` code and output. 34 | 35 | ```c 36 | #include "sagoma.h" 37 | void a_function(void) 38 | { 39 | sagoma_this( 40 | (void)fprintf(stderr, "a_function called!\n"); 41 | ); 42 | } 43 | int main(void) 44 | { 45 | // This takes 100ms. Best done outside of any profiling: 46 | sagoma__estimate_cpu_timer_frequency(); 47 | // ... other code 48 | sagoma_this( 49 | for (int i = 0; i < 2; i++) 50 | a_function(); 51 | ); 52 | // ... other code 53 | } 54 | ``` 55 | 56 | This will output the total time taken by each `sagoma_this` call. Assuming you've saved it as `1.c` and compiled it: 57 | 58 | ``` 59 | $ ./a.out 60 | a_function called! 61 | @1.c:4: Total: 0.000023503 s = 23.5026 us 62 | a_function called! 63 | @1.c:4: Total: 0.000000392 s = 0.3919 us 64 | @1.c:13: Total: 0.000048389 s = 48.3887 us 65 | ``` 66 | 67 |
68 | 69 | If you'd like many more details, you'll have to `#define SAGOMA`. 70 | 71 |
72 | Example with `#define SAGOMA` 73 | 74 | ```c 75 | #define SAGOMA 76 | #include "sagoma.h" 77 | void a_function(void) 78 | { 79 | sagoma_this( 80 | (void)fprintf(stderr, "a_function called!\n"); 81 | ); 82 | } 83 | int main(void) 84 | { 85 | // This takes 100ms. Best done outside of any profiling: 86 | sagoma__estimate_cpu_timer_frequency(); 87 | // ... other code 88 | sagoma_this( 89 | for (int i = 0; i < 2; i++) 90 | a_function(); 91 | ); 92 | // ... other code 93 | } 94 | ``` 95 | 96 | This will output the total time taken by each `sagoma_this` call _as well as_ children time for each `sagoma_this_block`. Assuming you've saved it as `2.c` and compiled it, this time with `-lm`: 97 | 98 | ``` 99 | $ ./a.out 100 | a_function called! 101 | @2.c:5: Total: 0.000020019 s = 20.0193 us 102 | 0: @2.c:5 0.000019985 s = 19.9846 us 99.83% 103 | a_function called! 104 | @2.c:5: Total: 0.000020623 s = 20.6229 us 105 | 0: @2.c:5 2x 0.000020565 s = 20.5648 us 99.72% = 0.000010282 s = 10.2824 us/it 106 | @2.c:14: Total: 0.000069644 s = 69.6438 us 107 | 0: @2.c:14 0.000069584 s = 69.5844 us 99.91% 108 | ``` 109 | 110 |
111 | 112 |
113 | Even more details with `#define SAGOMA_CLOCK_CYCLES`: 114 | 115 | ```c 116 | #define SAGOMA 117 | #define SAGOMA_CLOCK_CYCLES 118 | #include "sagoma.h" 119 | void a_function(void) 120 | { 121 | sagoma_this( 122 | (void)fprintf(stderr, "a_function called!\n"); 123 | ); 124 | } 125 | int main(void) 126 | { 127 | // This takes 100ms. Best done outside of any profiling: 128 | sagoma__estimate_cpu_timer_frequency(); 129 | // ... other code 130 | sagoma_this( 131 | for (int i = 0; i < 2; i++) 132 | a_function(); 133 | ); 134 | // ... other code 135 | } 136 | ``` 137 | 138 | This will output something similar to the previous example, _but_ with added cycle counts. Assuming you've saved it as `3.c` and compiled it, this time with `-lm`: 139 | 140 | ``` 141 | $ ./a.out 142 | a_function called! 143 | @3.c:6: Total: 69702C 0.000023270 s = 23.2696 us 144 | 0: @3.c:6 69564C 0.000023223 s = 23.2235 us 99.80% 145 | a_function called! 146 | @3.c:6: Total: 98094C 0.000032748 s = 32.7480 us 147 | 0: @3.c:6 2x 97798C 0.000032649 s = 32.6492 us 99.70% = 48899C 0.000016325 s = 16.3246 us/it 148 | @3.c:15: Total: 284660C 0.000095032 s = 95.0319 us 149 | 0: @3.c:15 284390C 0.000094942 s = 94.9417 us 99.91% 150 | ``` 151 | 152 |
153 | 154 | The above might not look like much, and doesn't look much like C, either. 155 | 156 | Note though that the `a_function` times _properly_ captured the amount taken inside the blocks, and are unaffected by the time spent outside that function. Note also that multiple `sagoma_this` can coexist in the same file. 157 | 158 | ## `sagoma_ez` functions 159 | 160 | The `sagoma_ez`, `sagoma_ez_end`, `sagoma_ez_print`, `sagoma_ez_print_color`, `sagoma_ez_block`, `sagoma_ez_block_done`, `sagoma_ez_loop`, `sagoma_ez_print_loop` and `sagoma_ez_print_loop_color` macros offer quite a bit more customization than the previous ones (which internally use these). 161 | 162 |
163 | Overview of the `sagoma_ez*` functions: 164 | 165 | - `sagoma_ez("whole profile name")` creates a new `Sagoma` structure called `sagoma`, and starts a profile block for it. 166 | - `sagoma_ez_end()` ends the `sagoma` profile block. `sagoma_ez_print` or `sagoma_ez_print_color` will also close it for you if you need to. 167 | - `sagoma_ez_print()` and `sagoma_ez_print_color()` will output statistics without and with color, respectively. 168 | 169 | If you also want to profile blocks, you can use these _between_ `sagoma_ez` and `sagoma_ez_end`: 170 | 171 | - `sagoma_ez_block("block name")` to start a block 172 | - `sagoma_ez_block_done()` to end the block 173 | 174 | Be sure to `#define SAGOMA_ASSERT` and ensure the amount of `sagoma_ez_block_done` corresponds to the amount of `sagoma_ez_block` opened! 175 | 176 | If you're profiling the innards of a function that gets called from the outside multiple times, and you'd only like to print statistics for it under certain conditions, you'll instead want to: 177 | 178 | - use `sagoma_ez_loop("whole profile name")` _instead of_ `sagoma_ez("whole profile name")` to set up the `sagoma` profile in such a way that it's declared only once, and so that any time spent "outside" the function isn't counted as part of the profile; then 179 | - use `sagoma_ez_print_loop(when)` or `sagoma_ez_print_loop_color(when)` instead of `sagoma_ez_print` or `sagoma_ez_print_color`. You'll have to provide an _expression_, `when`, to decide _when_ the statistics should be printed. As an example, in a game loop you might want to print statistics every second, or every 10th call of the function. 180 | 181 |
182 | 183 | ## The `sagoma` functions 184 | 185 | The real workhorse is the `sagoma*` functions/macros, which allow you to slice and dice the profiler any which way you want _including_ providing a global profile, interleave blocks, etc. 186 | 187 | You can use `sagoma_init(varname, name)` to declare and initialize a Sagoma struct, and make it available as `varname` for subsequent `sagoma_*` calls, which take `varname` as their first argument. 188 | 189 | Internally, but still part of the "API", there's also: 190 | 191 | - `sagoma_declare(varname)` declares a Sagoma struct 192 | - `sagoma_init_only(varname, name)` initializes the Sagoma struct 193 | 194 | To profile a block, you can use: 195 | 196 | - `sagoma_block(varname, name)` to start the block you want to profile. The `varname` is the same as the one you used in `sagoma_init`. `name` is the name of the block you want to profile, but it's a variable name, so that different blocks can have different names and the same name can be reused in different scopes, with no warnings/errors about reusing the same underlying variable name. 197 | - `sagoma_block_done(varname)` ends the block you started with `sagoma_block`. 198 | - `sagoma_end(varname)` ends the profiling session, but that's also done by: 199 | - `sagoma_print(varname, ignore_time, ignore_index)` (and `sagoma_print_color`, for colored output) prints the profiling info for the Sagoma struct you initialized with `sagoma_init` and for the blocks you started with `sagoma_block`. 200 | 201 | To profile something (i.e. a function) that gets called _from the outside_ multiple times, you can use the verbose "loop" API: 202 | 203 | - `sagoma_init_loop(varname, name)` (use it instead of `sagoma_init`!) declares and initializes a Sagoma struct for use within a function that you want to analyze the inside bits of, but you don't want to take into consideration the time the program takes when outside of it. This internally declares a static Sagoma struct, and a static "boolean" variable, to keep track of whether the Sagoma struct has been initialized, and how much time was spent outside of the function, to ensure that statistics reflect the time spent inside the function only. 204 | - you then use `sagoma_block(...)` and `sagoma_block_done(...)`, as above 205 | - if you want to print statistics every time the function is called, you can just use `sagoma_print(...)` or `sagoma_print_color(...)`, as above. 206 | - `sagoma_print_loop(varname, when)` (and `sagoma_print_loop_color`, for colored output) instead prints the profiling info for the Sagoma struct you initialized with `sagoma_init_loop` only when `when` is true. As an example, you might only print statistics if a global boolean was toggled, or every few iterations. This makes it easier to do that. 207 | 208 | See the [tests/](tests/) and [examples/](examples/) subdirectories for more examples. 209 | 210 | # LICENSE 211 | 212 | The MIT License (MIT) 213 | 214 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 215 | 216 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 217 | 218 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 219 | 220 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 221 | -------------------------------------------------------------------------------- /dyn_arr/README.md: -------------------------------------------------------------------------------- 1 | # `dyn_arr.h` - C "dynamic" array-like header-only library 2 | 3 | This "macro-ed" header file creates definitions and implementations for a struct that contains a dynamic array of a given `DYN_ARR_VALUE_TYPE *`, and where the name for `struct`s and functions are "templated" via the given `DYN_ARR_VALUE_TYPE`, providing type safety. 4 | 5 | The system only helps manage (via `_new` and `_free` suffixed function names) the lifecycle of the structure itself, NOT of the members you put in them. That is up to your code. 6 | 7 | # Minimal usage 8 | 9 | Say you've got just one `main.c` file, containing a struct declaration for which you'd like to also define an array-like struct type for, i.e.: 10 | 11 | ```c 12 | struct foo { ... }; 13 | ``` 14 | 15 | The `DYN_ARR_VALUE_TYPE` for this will be `struct foo`, and the array-like struct will contain pointers to many of those. 16 | 17 | You'll have to pick a name for the new _struct name_ (and related _functions' prefix_). Let's pick `foo_arr` for it: the `DYN_ARR_VALUE_TYPE` you'll want to specify will be `foo_arr`. 18 | 19 | You'll have to copy the `dyn_arr.h` file to the same directory as `main.c`, and write in `main.c`: 20 | 21 | ```c 22 | #define DYN_ARR_VALUE_TYPE struct foo 23 | #define DYN_ARR_TYPE_NAME foo_arr 24 | #define DYN_ARR_IMPLEMENTATION 25 | #include "dyn_arr.h" 26 | #undef DYN_ARR_IMPLEMENTATION 27 | #undef DYN_ARR_TYPE_NAME 28 | #undef DYN_ARR_VALUE_TYPE 29 | ``` 30 | 31 | In your code, you'll then use `struct foo_arr *` to declare a variable of the newly created type, and will use functions named `foo_arr_*` to interact with it: 32 | 33 | ```c 34 | // Create one of your pre-existing structs, somehow: 35 | struct foo *val = create_a_foo(/*...*/); // or malloc(sizeof(struct foo)) or whatever 36 | // Here's what the "dyn_arr.h" gives you: 37 | // Create a pre-allocated array with 10 "members" in it. 38 | // Leave 4 "members" to either side "free", so unshifts and pushes don't trigger a resize as often 39 | // Automatically resize (doubling the "members") once either side only has 2 free slots. 40 | struct foo_arr *arr = foo_arr_new(10, 4, 2); 41 | // Append "val" to the "arr". Can return a new "arr" (not in this case, as there's ample space...) 42 | arr = foo_arr_push(arr, val); 43 | // Append another: 44 | arr = foo_arr_push(arr, val); 45 | // Do whatever else you want with it: unshift, pop, get, etc. 46 | // Free the whole struct for the array (but NOT the contents!) 47 | foo_arr_free(arr); 48 | // You'll have to clean up your "struct foo *" MEMBERS yourself! 49 | free(val); 50 | ``` 51 | 52 | See the [examples/](examples/) directory for more examples! 53 | 54 | # Definition and Implementation 55 | 56 | You can optionally put the definitions in one header file, and the implementation in a single file in your codebase: 57 | 58 | ## Definition 59 | 60 | In one header file, you can grab/create the struct definitions at compile time. This can then be used by other `.c` files, which will share the definition: 61 | 62 | ```c 63 | // Your pre-existing headers, giving access to "struct foo" definition: 64 | #include "your_other_headers.h" 65 | // What you need to add: 66 | #define DYN_ARR_VALUE_TYPE struct foo 67 | #define DYN_ARR_TYPE_NAME foo_arr 68 | #include "dyn_arr.h" 69 | #undef DYN_ARR_TYPE_NAME 70 | #undef DYN_ARR_VALUE_TYPE 71 | ``` 72 | 73 | That `#include "dyn_arr.h"` will result in something like this being inserted in the header: 74 | 75 | ```c 76 | struct foo_arr; 77 | extern struct foo_arr *foo_arr_new(size_t size, size_t buffer_size, size_t min_buffer); 78 | extern void foo_arr_free(struct foo_arr *arr); 79 | // ... 80 | ``` 81 | 82 | Multiple such `#include "dyn_arr.h"` (for **different** `DYN_ARR_VALUE_TYPE`s!) can be placed in the same header file. 83 | Remember to `#undef`, then `#define` again, properties for the new type(s). 84 | 85 | ## Implementation 86 | 87 | Similarly to the definition, you'll likely want to put the implementation in one `.c` file: 88 | 89 | ```c 90 | // Your pre-existing headers, giving access to "struct foo" definition: 91 | #include "your_other_headers.h" 92 | // What you need to add: 93 | #define DYN_ARR_VALUE_TYPE struct foo 94 | #define DYN_ARR_TYPE_NAME foo_arr 95 | #define DYN_ARR_IMPLEMENTATION 96 | #include "dyn_arr.h" 97 | #undef DYN_ARR_IMPLEMENTATION 98 | #undef DYN_ARR_TYPE_NAME 99 | #undef DYN_ARR_VALUE_TYPE 100 | ``` 101 | 102 | The fuller implementation will instead be placed in the file by the `#include "dyn_arr.h"`, i.e. something like: 103 | 104 | ```c 105 | struct foo_arr { size_t size; size_t buffer_size; /* ... */ struct foo *members[]; }; 106 | // ... 107 | ``` 108 | 109 | ## Alternative generation methods 110 | 111 | ### Generate it once 112 | 113 | You might opt to produce the definitions and/or implementations once, which might help code completion, and copy/paste them in the header file. 114 | Both `gcc` or `tcc` or other compilers can help do this, via `-E` or similar flags. Here's one example of how to do it: 115 | 116 | The definitions: 117 | 118 | ```bash 119 | $ gcc -E -P -CC -D DYN_ARR_VALUE_TYPE='struct foo' -D DYN_ARR_TYPE_NAME=foo_arr dyn_arr.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$' 120 | struct foo_arr; 121 | extern 122 | struct foo_arr *foo_arr_new(size_t size, size_t buffer_size, size_t min_buffer); 123 | # ... 124 | ``` 125 | 126 | The implementation: 127 | 128 | ```bash 129 | $ gcc -E -P -CC -D DYN_ARR_VALUE_TYPE='struct foo' -D DYN_ARR_TYPE_NAME=foo_arr -D DYN_ARR_IMPLEMENTATION dyn_arr.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$' 130 | struct foo_arr { /* ... */ }; 131 | struct foo_arr *foo_arr_new(size_t size, size_t buffer_size, size_t min_buffer); 132 | # ... 133 | ``` 134 | 135 | ### Integrate it in your build system 136 | 137 | You can instruct your build system to automatically generate the header file and the implementation file. 138 | 139 | If you use a `Makefile`, you can use this as a starting point. This will rebuild the header file and the implementation file whenever the `Makefile` itself changes (as there might be changes in the `-D ...` defines) or the `dyn_arr.h` changes (as you might get a new version, or tweak it). You might want to ignore those generated files in your build system. 140 | 141 | ```Makefile 142 | foo_arr.h: Makefile dyn_arr.h 143 | @rm -f $@ 144 | @printf '// Automatically generated from %s.\n' "$^" > $@ 145 | @printf '// Automatically generated on %s\n' "$$(date)" >> $@ 146 | @printf '#ifndef FOO_ARR_H\n#define FOO_ARR_H\n' >> $@ 147 | @gcc -E -P -CC -D DYN_ARR_VALUE_TYPE='struct foo' -D DYN_ARR_TYPE_NAME=foo_arr dyn_arr.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 148 | @printf '#endif\n' >> $@ 149 | @chmod -w $@ 150 | 151 | foo_arr.c: Makefile dyn_arr.h foo_arr.h 152 | @rm -f $@ 153 | @printf '// Automatically generated from %s.\n' "$^" > $@ 154 | @printf '// Automatically generated on %s\n' "$$(date)" >> $@ 155 | @printf '#include "your_project_other_includes.h"\n' >> $@ 156 | @printf '#include "foo_arr.h"\n' >> $@ 157 | @gcc -E -P -CC -D DYN_ARR_VALUE_TYPE=struct foo' -D DYN_ARR_TYPE_NAME=foo_arr -D DYN_ARR_IMPLEMENTATION dyn_arr.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 158 | @chmod -w $@ 159 | ``` 160 | 161 | # Functions 162 | 163 | The header also declares, and makes available, functions to interact with the array-like struct: push, pop, etc. 164 | 165 | All functions here are mentioned by suffix. Your `-D DYN_ARR_TYPE_NAME=foo_arr` will create functions whose name _starts_ with the value you picked `foo_arr`, but whose suffix is constant, i.e. the `_new` will be available to you as `foo_arr_new`. 166 | 167 | ## `_new` & `_free` 168 | 169 | `_new` creates a new array-like struct, ready to accept (via `_push`, etc) `DYN_ARR_VALUE_TYPE` items in it. 170 | 171 | You'll have to specify: 172 | 173 | - a `size_t size` for how many _member pointers_ you'd like preallocated 174 | - a `size_t buffer_size` for how many _member pointers_ should be left "untouched" to start with, at either end 175 | - a `size_t min_buffer` to provide a minimum trigger at which point the array will be reallocated with twice the `size` to ensure more members can fit in it. 176 | 177 | Once done with the struct, remember to use the `_free` function on it. 178 | 179 | Note that the `_free` function will **not** free the `DYN_ARR_VALUE_TYPE` values which you might have placed in it. You'll have to do that yourself, separately! 180 | 181 | ## `_empty`, `_length`, and `_size` 182 | 183 | - `_empty` returns whether the array has any items in it or not. 184 | - `_length` returns the amount of **actually filled** "member slots" in the array. 185 | - `_size` returns the amount of actual "member slots" in it. 186 | 187 | Neither needs to iterate the array to provide its answer. 188 | 189 | ## `_push` and `_unshift` 190 | 191 | These operations insert a new member at the end of, and at the start of, the array, respectively. 192 | 193 | v-- unshift adds here 194 | ...[ ] [1] [2] [3] [ ]... 195 | ^--- push adds here 196 | 197 | If the operation would cause the amount of "buffer" at either end to go at or below the initial `min_buffer`, the array will be resized to twice the size, and a new one returned. 198 | 199 | ## `_get` 200 | 201 | This operation returns the member item at the given `size_t idx`, or `NULL` if the `idx` is out of bounds (`0`..`_length`). 202 | 203 | This does _not_ remove the member from the array. It merely _gets its value_. 204 | 205 | ## `_set` 206 | 207 | This operation sets the member item at the given `size_t idx`. It _does nothing_ if the `idx` is out of bounds (`0`..`_length`). 208 | 209 | You can only `_set` an `idx` if it previously contained something. Else, see `_push` or `_unshift`. 210 | 211 | This does _not_ free the previous member from the array. That's up to you to do. 212 | 213 | ## `_del` 214 | 215 | This operation removes the member at the given `size_t idx` from the array, and shifts the contents to the left as a result, to fill the gap. 216 | 217 | This does _not_ free the now-gone member from the array. That's up to you to do. 218 | 219 | ## `_front` and `_back` 220 | 221 | These operations return the member item at the start of, and at the end of, the array, respectively. 222 | 223 | v-- front returns this 224 | ...[ ] [1] [2] [3] [ ]... 225 | ^--- back returns this 226 | 227 | ## `_shift` and `_pop` 228 | 229 | These operations _remove_ **and** _return_ a member item from the end of, or the start of, the array, respectively. 230 | 231 | v-- shift removes and returns this 232 | ...[ ] [1] [2] [3] [ ]... 233 | ^--- pop removes and returns this 234 | 235 | The array's `_length` is modified as a byproduct of this operation. 236 | 237 | ## `_unique` 238 | 239 | This somewhat useful function removes, _shifting left_ the remaining members as required, any duplicate member item in the array. 240 | 241 | ## `_fill` 242 | 243 | This somewhat useful function fills, _appending_ if the array is not empty, `size` amount of `value`s to the array. Uses `_push` internally. 244 | The array's `_length` is modified as a byproduct of this operation. 245 | 246 | As per `_push`, if the operation would cause the amount of "buffer" at either end to go at or below the initial `min_buffer`, the array will be resized to twice the size, and a new one returned. 247 | 248 | ## `_clear` 249 | 250 | This function removes all elements from the array. The array's `_length` is modified as a byproduct of this operation. 251 | The member items are **not** freed by this operation. You'll have to do that yourself. 252 | 253 | ## `_remove_all` 254 | 255 | This somewhat useful function removes, _shifting left_ the remaining members as required, any member item which matches the given `value` in the array. 256 | 257 | ## `_iterate_ftl` and `_iterate_ltf` 258 | 259 | Iterates the array, calling a given "func" (with user-provided parameter "param") for each item "val" in the list, **stopping** if the "func" returns non-zero. The array is **not** modified by the iteration. Variations: 260 | 261 | - `_iterate_ftl` iterates the array from first to last. 262 | - `_iterate_ltf` iterates the array from last to first. 263 | 264 | ## `_status` 265 | 266 | This not-so-useful function prints out the "status" of the array to STDOUT, with an overview and one line per either empty or filled member. Useful for debugging the innards of this header file. 267 | 268 | # LICENSE 269 | 270 | The MIT License (MIT) 271 | 272 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 273 | 274 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 275 | 276 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 277 | 278 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 279 | -------------------------------------------------------------------------------- /dyn_kvp/README.md: -------------------------------------------------------------------------------- 1 | # `dyn_kvp.h` - C "dynamic" key/value "hash" library 2 | 3 | This "macro-ed" header file creates definitions and implementations for a key/value "hash" that has a default `unsigned int` key (overridable via `DYN_KVP_KEY_TYPE`), a given `DYN_KVP_VALUE_TYPE *` as "value", and where the name for `struct`s and functions are "templated" via the given `DYN_KVP_TYPE_NAME`, providing type safety. 4 | 5 | The system only helps manage (via `_new` and `_free` suffixed function names) the lifecycle of the "hash" itself, NOT of the members you put in them. That is up to your code. 6 | 7 | # Minimal usage 8 | 9 | Say you've got just one `main.c` file, containing a struct declaration for which you'd like to also define a key/value type for, i.e.: 10 | 11 | ```c 12 | struct foo { ... }; 13 | ``` 14 | 15 | The `DYN_KVP_VALUE_TYPE` for this will be `struct foo`, and the hash will contain a pointer to one. 16 | 17 | You'll have to pick a name for the new _struct name_ (and related _functions' prefix_). Let's pick `foo_kvp` for it: the `DYN_KVP_TYPE_NAME` you'll want to specify will be `foo_kvp`. 18 | 19 | You'll have to copy the `dyn_kvp.h` file to the same directory as `main.c`, and write in `main.c`: 20 | 21 | ```c 22 | // Struct definitions: 23 | #define DYN_KVP_VALUE_TYPE struct foo 24 | #define DYN_KVP_TYPE_NAME foo_kvp 25 | #include "dyn_kvp.h" 26 | // Actual implementation: 27 | #define DYN_KVP_IMPLEMENTATION 28 | #include "dyn_kvp.h" 29 | #undef DYN_KVP_IMPLEMENTATION 30 | #undef DYN_KVP_TYPE_NAME 31 | #undef DYN_KVP_VALUE_TYPE 32 | ``` 33 | 34 | In your code, you'll then use `struct foo_kvp *` to declare a variable of the newly created type, and will use functions named `foo_kvp_*` (or `foo_kvp__member_*`, for the "explosion" of it) to interact with it: 35 | 36 | ```c 37 | // Create one of your pre-existing structs, somehow: 38 | struct foo *val = create_a_foo(/*...*/); // or malloc(sizeof(struct foo)) or whatever 39 | // Here's what the "dyn_kvp.h" gives you. Create a hash with 8 buckets: 40 | struct foo_kvp *hash = foo_kvp_new(8); 41 | // Set "1234" to that value: 42 | foo_kvp_set(1234, val); 43 | // Do stuff with it 44 | // Remove "val" from the hash: 45 | foo_kvp_del(1234); 46 | // Free the whole hash (but NOT the contents!) 47 | foo_kvp_free(hash); 48 | // You'll have to clean up your "struct foo *" MEMBERS yourself! 49 | free(val); 50 | ``` 51 | 52 | See the `examples/` directory for more examples! 53 | 54 | # Definition and Implementation 55 | 56 | You can optionally put the definitions in one header file, and the implementation in a single file in your codebase: 57 | 58 | ## Definition 59 | 60 | In one header file, you can grab/create the struct definitions at compile time. This can then be used by other `.c` files, which will share the definition: 61 | 62 | ```c 63 | // Your pre-existing headers, giving access to "struct foo" definition: 64 | #include "your_other_headers.h" 65 | // What you need to add: 66 | #define DYN_KVP_VALUE_TYPE struct foo 67 | #define DYN_KVP_TYPE_NAME foo_kvp 68 | #include "dyn_kvp.h" 69 | #undef DYN_KVP_TYPE_NAME 70 | #undef DYN_KVP_VALUE_TYPE 71 | ``` 72 | 73 | That `#include "dyn_kvp.h"` will result in something like this being inserted in the header: 74 | 75 | ```c 76 | struct foo_kvp; 77 | extern struct foo_kvp *foo_kvp_new(void); 78 | extern void foo_kvp_free(struct foo_kvp *hash); 79 | // ... 80 | ``` 81 | 82 | Multiple such `#include "dyn_kvp.h"` (for **different** `DYN_KVP_VALUE_TYPE`s!) can be placed in the same header file. 83 | Remember to `#undef`, then `#define` again, properties for the new type(s). 84 | 85 | ## Implementation 86 | 87 | Similarly to the definition, you'll likely want to put the implementation in one `.c` file: 88 | 89 | ```c 90 | // Your pre-existing headers, giving access to "struct foo" definition: 91 | #include "your_other_headers.h" 92 | // Your "kvp" header, which gives you access to the foo_kvp struct declaration: 93 | #include "foo_kvp.h" 94 | // What you need to add: 95 | #define DYN_KVP_VALUE_TYPE struct foo 96 | #define DYN_KVP_TYPE_NAME foo_kvp 97 | #define DYN_KVP_IMPLEMENTATION 98 | #include "dyn_kvp.h" 99 | #undef DYN_KVP_IMPLEMENTATION 100 | #undef DYN_KVP_TYPE_NAME 101 | #undef DYN_KVP_VALUE_TYPE 102 | ``` 103 | 104 | The fuller implementation will instead be placed in the file by the `#include "dyn_kvp.h"`, i.e. something like: 105 | 106 | ```c 107 | // ... 108 | struct foo_kvp *foo_kvp_new(size_t size); 109 | // ... 110 | ``` 111 | 112 | ## Alternative generation methods 113 | 114 | ### Generate it once 115 | 116 | You might opt to produce the definitions and/or implementations once, which might help code completion, and copy/paste them in the header file. 117 | Both `gcc` or `tcc` or other compilers can help do this, via `-E` or similar flags. Here's one example of how to do it: 118 | 119 | The definitions: 120 | 121 | ```bash 122 | $ gcc -E -P -CC -D DYN_KVP_VALUE_TYPE='struct foo' -D DYN_KVP_TYPE_NAME=foo_kvp dyn_kvp.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$' 123 | # ... 124 | extern 125 | struct foo_kvp *foo_kvp_new(size_t size); 126 | # ... 127 | ``` 128 | 129 | The implementation: 130 | 131 | ```bash 132 | $ gcc -E -P -CC -D DYN_KVP_VALUE_TYPE='struct foo' -D DYN_KVP_TYPE_NAME=foo_kvp -D DYN_KVP_IMPLEMENTATION dyn_kvp.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$' 133 | # ... 134 | struct foo_kvp *foo_kvp_new(void) 135 | { 136 | # ... 137 | } 138 | # ... 139 | ``` 140 | 141 | ### Integrate it in your build system 142 | 143 | You can instruct your build system to automatically generate the header file and the implementation file. 144 | 145 | If you use a `Makefile`, you can use this as a starting point. This will rebuild the header file and the implementation file whenever the `Makefile` itself changes (as there might be changes in the `-D ...` defines) or the `dyn_kvp.h` changes (as you might get a new version, or tweak it). You might want to ignore those generated files in your build system. 146 | 147 | ```Makefile 148 | foo_kvp.h: Makefile dyn_kvp.h 149 | @rm -f $@ 150 | @printf '// Automatically generated from %s.\n' "$^" > $@ 151 | @printf '// Automatically generated on %s\n' "$$(date)" >> $@ 152 | @printf '#ifndef FOO_KVP_H\n#define FOO_KVP_H\n' >> $@ 153 | @gcc -E -P -CC -D DYN_KVP_VALUE_TYPE='struct foo' -D DYN_KVP_TYPE_NAME=foo_kvp dyn_kvp.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 154 | @printf '#endif\n' >> $@ 155 | @chmod -w $@ 156 | 157 | foo_kvp.c: Makefile dyn_kvp.h foo_kvp.h 158 | @rm -f $@ 159 | @printf '// Automatically generated from %s.\n' "$^" > $@ 160 | @printf '// Automatically generated on %s\n' "$$(date)" >> $@ 161 | @printf '#include "your_project_other_includes.h"\n' >> $@ 162 | @printf '#include "foo_kvp.h"\n' >> $@ 163 | @gcc -E -P -CC -D DYN_KVP_VALUE_TYPE=struct foo' -D DYN_KVP_TYPE_NAME=foo_kvp -D DYN_KVP_IMPLEMENTATION dyn_kvp.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 164 | @chmod -w $@ 165 | ``` 166 | 167 | # Hash Functions 168 | 169 | The header also declares, and makes available, functions to interact with the key/value hash: set, del, get, etc. 170 | 171 | All functions here are mentioned by suffix. Your `-D DYN_KVP_TYPE_NAME=foo_kvp` will create functions whose name _starts_ with the value you picked `foo_kvp`, but whose suffix is constant, i.e. the `_new` will be available to you as `foo_kvp_new`. 172 | 173 | ## `_new` & `_free` 174 | 175 | `_new` creates a new key/value hash, ready to accept (via `_set`, etc) `DYN_KVP_VALUE_TYPE` items in it. 176 | 177 | Once done with the hash, remember to use the `_free` function on it. 178 | 179 | Note that the `_free` function will **not** free the `DYN_KVP_VALUE_TYPE` values which you might have placed in the hash. You'll have to do that yourself, separately! 180 | 181 | ## `_set`, `_get` and `_del` 182 | 183 | The backbone of a hash. 184 | 185 | - `_set` lets you set (or override, if already present) a given "key" with a new "value". 186 | - `_get` returns the (possibly NULL, if not in the hash) "value" corresponding to a given "key". 187 | - `_del` removes the (possibly not found, but it doesn't matter) "value" corresponding to a given "key". It doesn't "free" the value. That's up to you! 188 | 189 | ## `_exists` 190 | 191 | Returns `0` if the "key" isn't found in the hash, or non-`0` if it is found. 192 | 193 | ## `_clear` 194 | 195 | Clears the hash of any key/values in it. 196 | 197 | ## `_copy` 198 | 199 | Returns a separate (new) copy of the given hash, with the given "new_size". Remember to `_free` them! 200 | 201 | This might also be handy to "resize" a given hash (albeit not in-line!), if the `_stats` make it look like its "load_factor" or the "avg_chain_len" are too high and a better "size" for it might be more appropriate: 202 | 203 | ```c 204 | // Somewhere in your code... 205 | { 206 | struct foo_kvp_stats stats = foo_kvp_stats(old_hash); 207 | // Pick a better method than this example: 208 | if (stats.load_factor > 0.8 || stats.avg_chain_len > 1.8) 209 | { 210 | size_t new_size = stats.size * 3; // Pick a better method than this... 211 | struct foo_kvp *new_hash = foo_kvp_copy(old_hash, new_size); 212 | foo_kvp_free(old_hash); 213 | old_hash = new_hash; 214 | } 215 | } 216 | // Done, continue using "old_hash", but with a "better" size if required. 217 | ``` 218 | 219 | ## `_merge` 220 | 221 | Returns a new hash with keys and values from the first one, _then_ keys and values from the second one, and the given "new_size". Remember to `_free` them! 222 | 223 | If a key is found in both hashes, the value from the _second_ one will be put in the one returned. Order matters! 224 | 225 | ## `_remove` 226 | 227 | Returns a new hash with keys and values from the first one, _without_ keys/values from the second one, and the given "new_size". Remember to `_free` them! 228 | 229 | If a key is found in both hashes, the copy won't have it. 230 | 231 | ## `_intersect` 232 | 233 | Returns a new hash with keys common between the two hashes, the given "new_size", and values from the first one. Remember to `_free` them! 234 | If you need values from the other hash, swap the arguments! 235 | 236 | If a key isn't found in both hashes, the copy won't have it. 237 | 238 | # Member Functions 239 | 240 | It's often useful to "get" all key/values from a hash, to then do whatever one wants. 241 | 242 | A `struct foo_kvp_kv` is provided, which contains the "key", "value", and a useless (to you) "next" pointer. 243 | 244 | You can `_kv_get` to get a `struct foo_kvp_kv **` which contains all the key/values in the hash, ended by a `NULL` pointer. 245 | 246 | Once done with it, you can `_kv_free` it. 247 | 248 | # Statistics 249 | 250 | Some statistics can be printed to STDOUT with the `_stats_printf` function. 251 | 252 | A `_stats` struct is avilable if you need more programmatic insights into a hash's statistics. It get populated by the `_stats` function: 253 | 254 | ```c 255 | struct foo_kvp_stats hash_stats = foo_kvp_stats(hash); 256 | ``` 257 | 258 | You can then look at the struct's contents to find information such as: 259 | 260 | - `size`: number of buckets in the hash table (the parameter given to `_new`) 261 | - `nkeys`: how many total keys are currently in the hash 262 | - `nempty`: how many buckets are empty (out of `size`) 263 | - `nused`: how many buckets are non-empty (out of `size`) 264 | - `ncollisions`: how many buckets hold more than one value 265 | - `ninchainmin`: minimum chain length among non-empty buckets (1 is good, much larger is not) 266 | - `ninchainmax`: maximum chain length among non-empty buckets (1 is good, much larger is not) 267 | - `avg_chain_len`: average chain length among non-empty buckets (1.0 is good, much larger is not) 268 | - `load_factor`: how many buckets are used compared to its size (`nused`/`size`) 269 | 270 | # Key type override 271 | 272 | By default, this library uses an `unsigned int` key type. 273 | 274 | You can override that by defining: 275 | 276 | - a `DYN_KVP_KEY_TYPE` (i.e. `#define DYN_KVP_KEY_TYPE const char *`) 277 | - a `DYN_KVP_HASH_FUNCTION` (i.e. `#define DYN_KVP_HASH_FUNCTION my_custom_hash`), which returns an `unsigned int` in the range `0..size` and takes as parameters: 278 | - a `size_t size` of how many slots exist in the hash (as passed to `_new`) 279 | - a `DYN_KVP_KEY_TYPE key` representing the key that the custom function should produce a hash for 280 | - a `DYN_KVP_COMPARE_FUNCTION` (default is `#define DYN_KVP_COMPARE_FUNCTION(a, b) ((a) == (b))`, you might want to use something like `#define DYN_KVP_COMPARE_FUNCTION(a, b) ((a)[0] == (b)[0] && !strcmp(a, b))` for a string key), which returns `1` if the two keys are equal, and `0` otherwise, and takes as parameters: 281 | - a `DYN_KVP_KEY_TYPE a` representing the item key to compare 282 | - a `DYN_KVP_KEY_TYPE b` representing the wanted item key to compare it to 283 | 284 | See `dyn_kvp/tests/charp_key` for an example of this. 285 | 286 | # LICENSE 287 | 288 | The MIT License (MIT) 289 | 290 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 291 | 292 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 293 | 294 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 295 | 296 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 297 | -------------------------------------------------------------------------------- /dyn_dllist/README.md: -------------------------------------------------------------------------------- 1 | # `dyn_dllist.h` - C "dynamic" doubly-linked-list header-only library 2 | 3 | This "macro-ed" header file creates definitions and implementations for a doubly-linked list that has a given `DYN_DLLIST_VALUE_TYPE *` as "value", and where the name for `struct`s and functions are "templated" via the given `DYN_DLLIST_TYPE_NAME`, providing type safety. 4 | 5 | The system only helps manage (via `_new` and `_free` suffixed function names) the lifecycle of the list itself, NOT of the members you put in them. That is up to your code. 6 | 7 | # Minimal usage 8 | 9 | Say you've got just one `main.c` file, containing a struct declaration for which you'd like to also define a doubly-linked list type for, i.e.: 10 | 11 | ```c 12 | struct foo { ... }; 13 | ``` 14 | 15 | The `DYN_DLLIST_VALUE_TYPE` for this will be `struct foo`, and the doubly-linked list will contain a pointer to one. 16 | 17 | You'll have to pick a name for the new _struct name_ (and related _functions' prefix_). Let's pick `foo_list` for it: the `DYN_DLLIST_TYPE_NAME` you'll want to specify will be `foo_list`. 18 | 19 | You'll have to copy the `dyn_dllist.h` file to the same directory as `main.c`, and write in `main.c`: 20 | 21 | ```c 22 | #define DYN_DLLIST_VALUE_TYPE struct foo 23 | #define DYN_DLLIST_TYPE_NAME foo_list 24 | #define DYN_DLLIST_IMPLEMENTATION 25 | #include "dyn_dllist.h" 26 | #undef DYN_DLLIST_IMPLEMENTATION 27 | #undef DYN_DLLIST_TYPE_NAME 28 | #undef DYN_DLLIST_VALUE_TYPE 29 | ``` 30 | 31 | In your code, you'll then use `struct foo_list *` to declare a variable of the newly created type, and will use functions named `foo_list_*` to interact with it: 32 | 33 | ```c 34 | // Create one of your pre-existing structs, somehow: 35 | struct foo *val = create_a_foo(/*...*/); // or malloc(sizeof(struct foo)) or whatever 36 | // Here's what the "dyn_dllist.h" gives you: 37 | struct foo_list *list = foo_list_new(); 38 | // Append "val" to the list: 39 | foo_list_append(list, val); 40 | // Do stuff with it 41 | // Remove "val" from the list, wherever it is and however many there are in it: 42 | foo_list_remove_all(list, val); 43 | // Free the whole list (but NOT the contents!) 44 | foo_list_free(list); 45 | // You'll have to clean up your "struct foo *" MEMBERS yourself! 46 | free(val); 47 | ``` 48 | 49 | See the `examples/` directory for more examples! 50 | 51 | # Definition and Implementation 52 | 53 | You can optionally put the definitions in one header file, and the implementation in a single file in your codebase: 54 | 55 | ## Definition 56 | 57 | In one header file, you can grab/create the struct definitions at compile time. This can then be used by other `.c` files, which will share the definition: 58 | 59 | ```c 60 | // Your pre-existing headers, giving access to "struct foo" definition: 61 | #include "your_other_headers.h" 62 | // What you need to add: 63 | #define DYN_DLLIST_VALUE_TYPE struct foo 64 | #define DYN_DLLIST_TYPE_NAME foo_list 65 | #include "dyn_dllist.h" 66 | #undef DYN_DLLIST_TYPE_NAME 67 | #undef DYN_DLLIST_VALUE_TYPE 68 | ``` 69 | 70 | That `#include "dyn_dllist.h"` will result in something like this being inserted in the header: 71 | 72 | ```c 73 | struct foo_list; 74 | extern struct foo_list *foo_list_new(void); 75 | extern void foo_list_free(struct foo_list *list); 76 | // ... 77 | ``` 78 | 79 | Multiple such `#include "dyn_dllist.h"` (for **different** `DYN_DLLIST_VALUE_TYPE`s!) can be placed in the same header file. 80 | Remember to `#undef`, then `#define` again, properties for the new type(s). 81 | 82 | ## Implementation 83 | 84 | Similarly to the definition, you'll likely want to put the implementation in one `.c` file: 85 | 86 | ```c 87 | // Your pre-existing headers, giving access to "struct foo" definition: 88 | #include "your_other_headers.h" 89 | // What you need to add: 90 | #define DYN_DLLIST_VALUE_TYPE struct foo 91 | #define DYN_DLLIST_TYPE_NAME foo_list 92 | #define DYN_DLLIST_IMPLEMENTATION 93 | #include "dyn_dllist.h" 94 | #undef DYN_DLLIST_IMPLEMENTATION 95 | #undef DYN_DLLIST_TYPE_NAME 96 | #undef DYN_DLLIST_VALUE_TYPE 97 | ``` 98 | 99 | The fuller implementation will instead be placed in the file by the `#include "dyn_dllist.h"`, i.e. something like: 100 | 101 | ```c 102 | struct foo_list__member { foo *p; struct foo_list__member *next; struct foo_list__member *prev; }; 103 | typedef struct foo_list { struct foo_list__member *first; struct foo_list__member *last; } foo_list; 104 | struct foo_list *foo_list_new(void); 105 | // ... 106 | ``` 107 | 108 | ## Alternative generation methods 109 | 110 | ### Generate it once 111 | 112 | You might opt to produce the definitions and/or implementations once, which might help code completion, and copy/paste them in the header file. 113 | Both `gcc` or `tcc` or other compilers can help do this, via `-E` or similar flags. Here's one example of how to do it: 114 | 115 | The definitions: 116 | 117 | ```bash 118 | $ gcc -E -P -CC -D DYN_DLLIST_VALUE_TYPE='struct foo' -D DYN_DLLIST_TYPE_NAME=foo_list dyn_dllist.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$' 119 | struct foo_list; 120 | extern 121 | struct foo_list *foo_list_new(void); 122 | # ... 123 | ``` 124 | 125 | The implementation: 126 | 127 | ```bash 128 | $ gcc -E -P -CC -D DYN_DLLIST_VALUE_TYPE='struct foo' -D DYN_DLLIST_TYPE_NAME=foo_list -D DYN_DLLIST_IMPLEMENTATION dyn_dllist.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$' 129 | struct foo_list { /* ... */ }; 130 | struct foo_list *foo_list_new(void); 131 | # ... 132 | ``` 133 | 134 | ### Integrate it in your build system 135 | 136 | You can instruct your build system to automatically generate the header file and the implementation file. 137 | 138 | If you use a `Makefile`, you can use this as a starting point. This will rebuild the header file and the implementation file whenever the `Makefile` itself changes (as there might be changes in the `-D ...` defines) or the `dyn_dllist.h` changes (as you might get a new version, or tweak it). You might want to ignore those generated files in your build system. 139 | 140 | ```Makefile 141 | foo_list.h: Makefile dyn_dllist.h 142 | @rm -f $@ 143 | @printf '// Automatically generated from %s.\n' "$^" > $@ 144 | @printf '// Automatically generated on %s\n' "$$(date)" >> $@ 145 | @printf '#ifndef FOO_LIST_H\n#define FOO_LIST_H\n' >> $@ 146 | @gcc -E -P -CC -D DYN_DLLIST_VALUE_TYPE='struct foo' -D DYN_DLLIST_TYPE_NAME=foo_list dyn_dllist.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 147 | @printf '#endif\n' >> $@ 148 | @chmod -w $@ 149 | 150 | foo_list.c: Makefile dyn_dllist.h foo_list.h 151 | @rm -f $@ 152 | @printf '// Automatically generated from %s.\n' "$^" > $@ 153 | @printf '// Automatically generated on %s\n' "$$(date)" >> $@ 154 | @printf '#include "your_project_other_includes.h"\n' >> $@ 155 | @printf '#include "char_data_dllist.h"\n' >> $@ 156 | @gcc -E -P -CC -D DYN_DLLIST_VALUE_TYPE=struct foo' -D DYN_DLLIST_TYPE_NAME=foo_list -D DYN_DLLIST_IMPLEMENTATION dyn_dllist.h | perl -0777 -pe's,/[*].*?[*]/,,gs' | grep -v -e '^\s*$$' >> $@ 157 | @chmod -w $@ 158 | ``` 159 | 160 | # Functions 161 | 162 | The header also declares, and makes available, functions to interact with the doubly-linked list: add, remove, etc. 163 | 164 | All functions here are mentioned by suffix. Your `-D DYN_DLLIST_TYPE_NAME=foo_list` will create functions whose name _starts_ with the value you picked `foo_list`, but whose suffix is constant, i.e. the `_new` will be available to you as `foo_list_new`. 165 | 166 | ## `_new` & `_free` 167 | 168 | `_new` creates a new doubly-linked list, ready to accept (via `_append`, etc) `DYN_DLLIST_VALUE_TYPE` items in it. 169 | 170 | Once done with the list, remember to use the `_free` function on it. 171 | 172 | Note that the `_free` function will **not** free the `DYN_DLLIST_VALUE_TYPE` values which you might have placed in the list. You'll have to do that yourself, separately! 173 | 174 | ## `_size` 175 | 176 | Returns the number of items in the list, without having to iterate the list. Upkeep performed at every insertion and deletion. 177 | 178 | ## `_peek_first`, `_peek_last` 179 | 180 | Returns the item in the list's "first" or "last" position, respectively. 181 | The item is **not** removed from the list. See `_shift` and `_pop` for that. 182 | 183 | ## `_contains`, `_contains_ftl`, `contains_ltf` 184 | 185 | Returns `0` if the given item cannot be found on the list, or non-`0` if it can. There are variations: 186 | 187 | - `_contains_ftl` iterates from `first` to `last`. Use it if the item is likely to exist, and to be found towards the front. 188 | - `_contains_ltf` iterates from `last` to `first`. Use it if the item is likely to exist, and to be found towards the back. 189 | - `_contains` uses `_contains_ftl`. Note it does one more function call than the other, more specific, versions. 190 | 191 | If it's unlikely that the item is in the list, it doesn't matter which one you pick: the list has to be fully traversed to find out if it's in it. 192 | 193 | ## `_append`, `_append1_ftl`, `_append1_ltf` 194 | 195 | Appends the given element at the **end** of the list. Variations: 196 | 197 | - `_append` always appends the item, even if it's already on the list. If you use this, the list might contain the same value multiple times. 198 | - `_append1_ftl` only appends the item if it's **not** already in the list. Uses `_contains_ftl` to find it. Use it if it's likely the item can be found towards the start of the list. 199 | - `_append1_ltf` only appends the item if it's **not** already in the list. Uses `_contains_ltf` to find it. Use it if it's likely the item can be found towards the end of the list. 200 | 201 | Use either `_append1_*` variation if you don't think the item is in the list, as the list has to be fully traversed to find out if it's in it. 202 | 203 | ## `_prepend`, `_prepend1_ftl`, `_prepend1_ltf` 204 | 205 | Prepends the given element at the **start** of the list. Variations: 206 | 207 | - `_prepend` always prepends the item, even if it's already on the list. If you use this, the list might contain the same value multiple times. 208 | - `_prepend1_ftl` only prepends the item if it's **not** already in the list. Uses `_contains_ftl` to find it. Use it if it's likely the item can be found towards the start of the list. 209 | - `_prepend1_ltf` only prepends the item if it's **not** already in the list. Uses `_contains_ltf` to find it. Use it if it's likely the item can be found towards the end of the list. 210 | 211 | Use either `_prepend1_*` variation if you don't think the item is in the list, as the list has to be fully traversed to find out if it's in it. 212 | 213 | ## `_remove_all`, `_removenth_ftl`, `_removenth_ltf` 214 | 215 | Removes a given element from the list. Variations: 216 | 217 | - `_remove_all` removes **all** elements which match the given one from the list. 218 | - `_removenth_ftl` removes the `nth` element which matches the given one from the list, iterating it first to last. 219 | - `_removenth_ltf` removes the `nth` element which matches the given one from the list, iterating it last to first. 220 | 221 | ## `_shift`, `_pop` 222 | 223 | Removes, **and returns**, a given element from the list: 224 | 225 | - `_shift` removes **and returns** the **first** item in the list 226 | - `_pop` removes **and returns** the **last** item in the list 227 | 228 | ## `_iterate_ftl` and `_iterate_ltf` 229 | 230 | Iterates the list, calling a given "func" (with user-provided parameter "param") for each item "val" found, **stopping** if the "func" returns non-zero. The list is **not** modified by the iteration. Variations: 231 | 232 | - `_iterate_ftl` iterates the list from first to last. 233 | - `_iterate_ltf` iterates the list from last to first. 234 | 235 | Note that within the "func" it should be safe to remove the given "val" from the list, if you wish to. 236 | 237 | ## `_countif` 238 | 239 | Fully iterates the list, and returns the **count** of items "val" for which the given "func" (with user-provided parameter "param") returned non-zero. 240 | 241 | ## `_grep` 242 | 243 | Returns a **new dll** created by iterating the original list from **from first to last**, calling the given "func" (with user-provided parameter "param") for each item "val" found, and `_append`ing "val" to the new list IFF the "func" returns non-zero. 244 | 245 | As such, the new dll will contain matching items while retaining the order in which they were present in the original dllist. 246 | 247 | Note that the original list is **not** modified, unless the "func" modifies it. It's also safe to remove the given "val" from the list, if you wish to. 248 | 249 | ## `_nth_ftl`, `_nth_ltf`, `_first`, `_last` 250 | 251 | Returns the Nth item "val" from the list for which the given "func" (with user-provided parameter "param") returns non-zero. The list is **not** modified. Variations: 252 | 253 | - `_nth_ftl` iterates the list first to last, returning the nth matching item 254 | - `_nth_ltf` iterates the list last to first, returning the nth matching item 255 | - `_first` uses `_nth_ftl`, returning the first matching item from first to last. Note it does one more function call than the other, more specific, version. 256 | - `_last` uses `_nth_ltf`, returning the 'first' matching item from last to first, that is, the "last" matching item. Note it does one more function call than the other, more specific, version. 257 | 258 | # LICENSE 259 | 260 | The MIT License (MIT) 261 | 262 | Copyright (c) 2024 Marco Fontani, MFONTANI@cpan.org 263 | 264 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 265 | 266 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 267 | 268 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 269 | -------------------------------------------------------------------------------- /dyn_kvp/tests/basic/basic.c: -------------------------------------------------------------------------------- 1 | #include "basic.h" 2 | #include "../../../tap/tap.h" 3 | #include 4 | #include 5 | 6 | struct basic_foo *basic_foo_new(int a, int b) 7 | { 8 | struct basic_foo *f = malloc(sizeof(struct basic_foo)); 9 | assert(f != NULL); 10 | f->a = a; 11 | f->b = b; 12 | return f; 13 | } 14 | void basic_foo_free(struct basic_foo *f) 15 | { 16 | assert(f != NULL); 17 | free(f); 18 | } 19 | 20 | #define DYN_KVP_VALUE_TYPE struct basic_foo 21 | #define DYN_KVP_TYPE_NAME bfoo_kvp 22 | #define DYN_KVP_IMPLEMENTATION 23 | #include "../../dyn_kvp.h" 24 | #undef DYN_KVP_IMPLEMENTATION 25 | #undef DYN_KVP_TYPE_NAME 26 | #undef DYN_KVP_VALUE_TYPE 27 | 28 | void test_basic_foo(void) 29 | { 30 | tap_diag("Running tests for test_basic_foo()"); 31 | { 32 | struct bfoo_kvp *hash = bfoo_kvp_new(8); 33 | tap_is_int(bfoo_kvp_nkeys(hash), 0, "bfoo_kvp_nkeys() returns 0 after bfoo_kvp_new()"); 34 | tap_is_int(bfoo_kvp_exists(hash, 1), 0, "bfoo_kvp_exists(1) returns 0 after bfoo_kvp_new()"); 35 | bfoo_kvp_del(hash, 1); // no-op 36 | tap_is_int(bfoo_kvp_exists(hash, 1), 0, "bfoo_kvp_exists(1) returns 0 after bfoo_kvp_del()"); 37 | tap_is_int(bfoo_kvp_nkeys(hash), 0, "bfoo_kvp_nkeys() returns 0 after _del()"); 38 | bfoo_kvp_free(hash); 39 | } 40 | { 41 | struct bfoo_kvp *hash = bfoo_kvp_new(8); 42 | struct basic_foo *f1 = basic_foo_new(1, 2); 43 | tap_is_int(bfoo_kvp_exists(hash, 1), 0, "bfoo_kvp_exists(1) returns 0 after bfoo_kvp_new()"); 44 | bfoo_kvp_set(hash, 1, f1); 45 | tap_is_int(bfoo_kvp_exists(hash, 1), 1, "bfoo_kvp_exists(1) returns 1 after bfoo_kvp_set()"); 46 | tap_is_int(bfoo_kvp_nkeys(hash), 1, "bfoo_kvp_nkeys() returns 1 after bfoo_kvp_set()"); 47 | tap_is_voidp(bfoo_kvp_get(hash, 1), f1, 48 | "bfoo_kvp_get(1) returns the same pointer that was passed to bfoo_kvp_set()"); 49 | bfoo_kvp_del(hash, 1); 50 | tap_is_int(bfoo_kvp_nkeys(hash), 0, "bfoo_kvp_nkeys() returns 0 after bfoo_kvp_del()"); 51 | tap_is_voidp(bfoo_kvp_get(hash, 1), NULL, 52 | "bfoo_kvp_get(1) returns NULL after bfoo_kvp_del(1)"); 53 | bfoo_kvp_del(hash, 1); // no-op 54 | tap_is_int(bfoo_kvp_nkeys(hash), 0, "bfoo_kvp_nkeys() returns 0 after second bfoo_kvp_del()"); 55 | tap_is_voidp(bfoo_kvp_get(hash, 1), NULL, 56 | "bfoo_kvp_get(1) returns NULL after second bfoo_kvp_del(1)"); 57 | bfoo_kvp_free(hash); 58 | basic_foo_free(f1); 59 | } 60 | struct basic_foo *f1 = basic_foo_new(1, 2); 61 | struct basic_foo *f2 = basic_foo_new(3, 4); 62 | struct basic_foo *f3 = basic_foo_new(5, 6); 63 | struct bfoo_kvp *hash = bfoo_kvp_new(8); 64 | bfoo_kvp_set(hash, 1, f1); 65 | bfoo_kvp_set(hash, 3, f2); 66 | bfoo_kvp_set(hash, 5, f3); 67 | tap_is_int(bfoo_kvp_exists(hash, 1), 1, "bfoo_kvp_exists(1) returns 1 after bfoo_kvp_set()"); 68 | tap_is_int(bfoo_kvp_exists(hash, 3), 1, "bfoo_kvp_exists(3) returns 1 after bfoo_kvp_set()"); 69 | tap_is_int(bfoo_kvp_exists(hash, 5), 1, "bfoo_kvp_exists(5) returns 1 after bfoo_kvp_set()"); 70 | tap_is_int(bfoo_kvp_exists(hash, 2), 0, "bfoo_kvp_exists(2) returns 0 after bfoo_kvp_set()"); 71 | tap_is_int(bfoo_kvp_nkeys(hash), 3, "bfoo_kvp_nkeys() returns 3 after 3 bfoo_kvp_set()s"); 72 | tap_is_voidp(bfoo_kvp_get(hash, 1), f1, 73 | "bfoo_kvp_get(1) returns the same pointer that was passed to bfoo_kvp_set()"); 74 | tap_is_voidp(bfoo_kvp_get(hash, 3), f2, 75 | "bfoo_kvp_get(3) returns the same pointer that was passed to bfoo_kvp_set()"); 76 | tap_is_voidp(bfoo_kvp_get(hash, 5), f3, 77 | "bfoo_kvp_get(5) returns the same pointer that was passed to bfoo_kvp_set()"); 78 | { 79 | tap_is_int(bfoo_kvp_nkeys(hash), 3, "bfoo_kvp_nkeys() returns 3 after 3 bfoo_kvp_set()s"); 80 | assert(bfoo_kvp_nkeys(hash) == 3); 81 | struct bfoo_kvp_kv **exploded = bfoo_kvp_kv_new(hash); 82 | assert(exploded != NULL); 83 | assert(exploded[0] != NULL); 84 | // The order of these depends on the hash used! 85 | tap_is_int(exploded[0]->key, 1, "exploded[0]->key == 1"); 86 | tap_is_voidp(exploded[0]->value, f1, "exploded[0]->value == f1"); 87 | assert(exploded[1] != NULL); 88 | tap_is_int(exploded[1]->key, 3, "exploded[1]->key == 3"); 89 | tap_is_voidp(exploded[1]->value, f2, "exploded[1]->value == f2"); 90 | assert(exploded[2] != NULL); 91 | tap_is_int(exploded[2]->key, 5, "exploded[2]->key == 5"); 92 | tap_is_voidp(exploded[2]->value, f3, "exploded[2]->value == f3"); 93 | tap_is_voidp(exploded[3], NULL, "exploded[3] == NULL"); 94 | bfoo_kvp_kv_free(exploded); 95 | } 96 | bfoo_kvp_free(hash); 97 | { 98 | hash = bfoo_kvp_new(1); 99 | bfoo_kvp_set(hash, 1, f1); 100 | bfoo_kvp_set(hash, 2, f2); 101 | bfoo_kvp_set(hash, 3, f3); 102 | struct bfoo_kvp_kv **exploded = bfoo_kvp_kv_new(hash); 103 | assert(exploded != NULL); 104 | assert(exploded[0] != NULL); 105 | // All have the same slot in the hash, so they get prepended. 106 | tap_is_int(exploded[0]->key, 3, "exploded[0]->key == 3"); 107 | tap_is_voidp(exploded[0]->value, f3, "exploded[0]->value == f3"); 108 | assert(exploded[1] != NULL); 109 | tap_is_int(exploded[1]->key, 2, "exploded[1]->key == 2"); 110 | tap_is_voidp(exploded[1]->value, f2, "exploded[1]->value == f2"); 111 | assert(exploded[2] != NULL); 112 | tap_is_int(exploded[2]->key, 1, "exploded[2]->key == 1"); 113 | tap_is_voidp(exploded[2]->value, f1, "exploded[2]->value == f1"); 114 | tap_is_voidp(exploded[3], NULL, "exploded[3] == NULL"); 115 | bfoo_kvp_kv_free(exploded); 116 | } 117 | tap_is_int(bfoo_kvp_nkeys(hash), 3, "bfoo_kvp_nkeys() returns 3 after 3 bfoo_kvp_set()s"); 118 | { 119 | struct bfoo_kvp *copy = bfoo_kvp_copy(hash, 4); 120 | assert(copy != NULL); 121 | tap_is_int(bfoo_kvp_nkeys(copy), 3, "bfoo_kvp_nkeys() returns 3 after bfoo_kvp_copy()"); 122 | tap_is_voidp(bfoo_kvp_get(copy, 1), f1, 123 | "bfoo_kvp_get(1) returns the same pointer that was passed to bfoo_kvp_set()"); 124 | tap_is_voidp(bfoo_kvp_get(copy, 2), f2, 125 | "bfoo_kvp_get(3) returns the same pointer that was passed to bfoo_kvp_set()"); 126 | tap_is_voidp(bfoo_kvp_get(copy, 3), f3, 127 | "bfoo_kvp_get(5) returns the same pointer that was passed to bfoo_kvp_set()"); 128 | { 129 | struct bfoo_kvp_stats stats = bfoo_kvp_stats(copy); 130 | tap_is_int(stats.nkeys, 3, "bfoo_kvp_stats.nkeys == 3"); 131 | tap_is_int(stats.size, 4, "bfoo_kvp_stats.size == 4"); 132 | tap_is_int(stats.nempty, 1, "bfoo_kvp_stats.nempty == 1"); 133 | tap_is_int(stats.nused, 3, "bfoo_kvp_stats.nused == 3"); 134 | tap_is_int(stats.ncollisions, 0, "bfoo_kvp_stats.ncollisions == 0"); 135 | tap_is_int(stats.ninchainmin, 1, "bfoo_kvp_stats.ninchainmin == 1"); 136 | tap_is_int(stats.ninchainmax, 1, "bfoo_kvp_stats.ninchainmax == 1"); 137 | } 138 | bfoo_kvp_clear(copy); 139 | tap_is_int(bfoo_kvp_nkeys(copy), 0, "bfoo_kvp_nkeys() returns 0 after bfoo_kvp_clear()"); 140 | tap_is_int(bfoo_kvp_nkeys(hash), 3, "bfoo_kvp_nkeys() returns 3 after bfoo_kvp_clear(copy)"); 141 | bfoo_kvp_free(copy); 142 | } 143 | tap_is_int(bfoo_kvp_nkeys(hash), 3, "bfoo_kvp_nkeys() returns 3 after 3 bfoo_kvp_copy()s"); 144 | bfoo_kvp_clear(hash); 145 | tap_is_int(bfoo_kvp_nkeys(hash), 0, "bfoo_kvp_nkeys() returns 0 after bfoo_kvp_clear()"); 146 | { 147 | struct bfoo_kvp *hash1 = bfoo_kvp_new(8); 148 | struct bfoo_kvp *hash2 = bfoo_kvp_new(8); 149 | bfoo_kvp_set(hash1, 1, f1); 150 | bfoo_kvp_set(hash1, 2, f2); 151 | bfoo_kvp_set(hash1, 3, f3); 152 | bfoo_kvp_set(hash2, 1, f3); 153 | bfoo_kvp_set(hash2, 10, f1); 154 | bfoo_kvp_set(hash2, 20, f2); 155 | bfoo_kvp_set(hash2, 30, f3); 156 | { 157 | struct bfoo_kvp *merged_12 = bfoo_kvp_merge(hash1, hash2, 4); 158 | assert(merged_12 != NULL); 159 | tap_is_int(bfoo_kvp_nkeys(merged_12), 6, "bfoo_kvp_nkeys() returns 6 after bfoo_kvp_merge()[1, 2]"); 160 | tap_is_voidp(bfoo_kvp_get(merged_12, 1), f3, "bfoo_kvp_get(1) returns the override (f3) from hash2"); 161 | tap_is_voidp(bfoo_kvp_get(merged_12, 2), f2, "bfoo_kvp_get(2) returns the original (f2) from hash1"); 162 | tap_is_voidp(bfoo_kvp_get(merged_12, 3), f3, "bfoo_kvp_get(3) returns the original (f3) from hash1"); 163 | tap_is_voidp(bfoo_kvp_get(merged_12, 10), f1, "bfoo_kvp_get(10) returns the original (f1) from hash2"); 164 | tap_is_voidp(bfoo_kvp_get(merged_12, 20), f2, "bfoo_kvp_get(20) returns the original (f2) from hash2"); 165 | tap_is_voidp(bfoo_kvp_get(merged_12, 30), f3, "bfoo_kvp_get(30) returns the original (f3) from hash2"); 166 | bfoo_kvp_free(merged_12); 167 | } 168 | { 169 | // NOLINTNEXTLINE(readability-suspicious-call-argument) 170 | struct bfoo_kvp *merged_21 = bfoo_kvp_merge(hash2, hash1, 4); 171 | assert(merged_21 != NULL); 172 | tap_is_int(bfoo_kvp_nkeys(merged_21), 6, "bfoo_kvp_nkeys() returns 6 after bfoo_kvp_merge()[2, 1]"); 173 | tap_is_voidp(bfoo_kvp_get(merged_21, 1), f1, "bfoo_kvp_get(1) returns the override (f1) from hash1"); 174 | tap_is_voidp(bfoo_kvp_get(merged_21, 2), f2, "bfoo_kvp_get(2) returns the original (f2) from hash1"); 175 | tap_is_voidp(bfoo_kvp_get(merged_21, 3), f3, "bfoo_kvp_get(3) returns the original (f3) from hash1"); 176 | tap_is_voidp(bfoo_kvp_get(merged_21, 10), f1, "bfoo_kvp_get(10) returns the original (f1) from hash2"); 177 | tap_is_voidp(bfoo_kvp_get(merged_21, 20), f2, "bfoo_kvp_get(20) returns the original (f2) from hash2"); 178 | tap_is_voidp(bfoo_kvp_get(merged_21, 30), f3, "bfoo_kvp_get(30) returns the original (f3) from hash2"); 179 | bfoo_kvp_free(merged_21); 180 | } 181 | bfoo_kvp_free(hash2); 182 | bfoo_kvp_free(hash1); 183 | } 184 | { 185 | struct bfoo_kvp *copy = bfoo_kvp_copy(hash, 2); 186 | assert(copy != NULL); 187 | tap_is_int(bfoo_kvp_nkeys(copy), 0, "bfoo_kvp_nkeys() returns 0 after bfoo_kvp_copy()"); 188 | tap_is_voidp(bfoo_kvp_get(copy, 1), NULL, "bfoo_kvp_get(1) returns NULL after bfoo_kvp_copy()"); 189 | bfoo_kvp_set(copy, 1, f1); 190 | bfoo_kvp_set(copy, 2, f2); 191 | bfoo_kvp_set(copy, 5, f3); 192 | tap_is_int(bfoo_kvp_nkeys(copy), 3, "bfoo_kvp_nkeys() returns 3 after 3 bfoo_kvp_set()s"); 193 | tap_is_voidp(bfoo_kvp_get(copy, 1), f1, 194 | "bfoo_kvp_get(1) returns the same pointer that was passed to bfoo_kvp_set()"); 195 | tap_is_voidp(bfoo_kvp_get(copy, 2), f2, 196 | "bfoo_kvp_get(2) returns the same pointer that was passed to bfoo_kvp_set()"); 197 | tap_is_voidp(bfoo_kvp_get(copy, 5), f3, 198 | "bfoo_kvp_get(5) returns the same pointer that was passed to bfoo_kvp_set()"); 199 | struct bfoo_kvp_stats stats = bfoo_kvp_stats(copy); 200 | tap_is_int(stats.nkeys, 3, "bfoo_kvp_stats.nkeys == 3"); 201 | tap_is_int(stats.size, 2, "bfoo_kvp_stats.size == 2"); 202 | tap_is_int(stats.nempty, 0, "bfoo_kvp_stats.nempty == 0"); 203 | tap_is_int(stats.nused, 2, "bfoo_kvp_stats.nused == 2"); 204 | tap_is_int(stats.ncollisions, 1, "bfoo_kvp_stats.ncollisions == 1"); 205 | tap_is_int(stats.ninchainmin, 1, "bfoo_kvp_stats.ninchainmin == 1"); 206 | tap_is_int(stats.ninchainmax, 2, "bfoo_kvp_stats.ninchainmax == 2"); 207 | // TODO: once I have tap_is_double or similar. 208 | tap_diag("stats.avg_chain_len = %Lf", stats.avg_chain_len); // 1.5 209 | tap_diag("stats.load_factor = %f", stats.load_factor); // 1.0 210 | bfoo_kvp_free(copy); 211 | } 212 | { 213 | bfoo_kvp_clear(hash); 214 | bfoo_kvp_set(hash, 1, f1); 215 | bfoo_kvp_set(hash, 2, f1); 216 | bfoo_kvp_set(hash, 5, f1); 217 | struct bfoo_kvp *hash2 = bfoo_kvp_new(8); 218 | bfoo_kvp_set(hash2, 1, f2); 219 | bfoo_kvp_set(hash2, 2, f3); 220 | struct bfoo_kvp *removed = bfoo_kvp_remove(hash, hash2, 8); 221 | assert(removed != NULL); 222 | tap_is_int(bfoo_kvp_nkeys(removed), 1, "bfoo_kvp_nkeys(removed) returns 1 after bfoo_kvp_remove(hash, hash2)"); 223 | tap_is_voidp(bfoo_kvp_get(removed, 1), NULL, 224 | "bfoo_kvp_get(removed, 1) returns NULL as hash had it, and hash2 had it too"); 225 | tap_is_voidp(bfoo_kvp_get(removed, 2), NULL, 226 | "bfoo_kvp_get(removed, 2) returns NULL as hash had it, and hash2 had it too"); 227 | tap_is_voidp(bfoo_kvp_get(removed, 5), f1, 228 | "bfoo_kvp_get(removed, 5) returns f1 as hash had it, but hash2 did not have it"); 229 | bfoo_kvp_free(removed); 230 | bfoo_kvp_free(hash2); 231 | } 232 | { 233 | bfoo_kvp_clear(hash); 234 | bfoo_kvp_set(hash, 1, f1); 235 | bfoo_kvp_set(hash, 2, f1); 236 | bfoo_kvp_set(hash, 5, f1); 237 | struct bfoo_kvp *hash2 = bfoo_kvp_new(8); 238 | bfoo_kvp_set(hash2, 1, f2); 239 | bfoo_kvp_set(hash2, 2, f3); 240 | { 241 | struct bfoo_kvp *intersected = bfoo_kvp_intersect(hash, hash2, 8); 242 | assert(intersected != NULL); 243 | tap_is_int(bfoo_kvp_nkeys(intersected), 2, "bfoo_kvp_nkeys(intersected) returns 2 after bfoo_kvp_intersect(hash, hash2)"); 244 | tap_is_voidp(bfoo_kvp_get(intersected, 1), f1, 245 | "bfoo_kvp_get(intersected, 1) returns f1 as hash had it as f1, and hash2 had it too"); 246 | tap_is_voidp(bfoo_kvp_get(intersected, 2), f1, 247 | "bfoo_kvp_get(intersected, 2) returns f1 as hash had it as f1, and hash2 had it too"); 248 | tap_is_voidp(bfoo_kvp_get(intersected, 5), NULL, 249 | "bfoo_kvp_get(intersected, 5) returns NULL as hash had it, but hash2 did not have it"); 250 | bfoo_kvp_free(intersected); 251 | } 252 | // Swap order of arguments to "get" values from the other hash. 253 | { 254 | // NOLINTNEXTLINE(readability-suspicious-call-argument) 255 | struct bfoo_kvp *intersected = bfoo_kvp_intersect(hash2, hash, 8); 256 | assert(intersected != NULL); 257 | tap_is_int(bfoo_kvp_nkeys(intersected), 2, "bfoo_kvp_nkeys(intersected) returns 2 after bfoo_kvp_intersect(hash2, hash)"); 258 | tap_is_voidp(bfoo_kvp_get(intersected, 1), f2, 259 | "bfoo_kvp_get(intersected, 1) returns f2 as hash had it, and hash2 had it too as f2"); 260 | tap_is_voidp(bfoo_kvp_get(intersected, 2), f3, 261 | "bfoo_kvp_get(intersected, 2) returns f3 as hash had it, and hash2 had it too as f3"); 262 | tap_is_voidp(bfoo_kvp_get(intersected, 5), NULL, 263 | "bfoo_kvp_get(intersected, 5) returns NULL as hash had it, but hash2 did not have it"); 264 | bfoo_kvp_free(intersected); 265 | } 266 | bfoo_kvp_free(hash2); 267 | } 268 | bfoo_kvp_free(hash); 269 | basic_foo_free(f1); 270 | basic_foo_free(f2); 271 | basic_foo_free(f3); 272 | } 273 | -------------------------------------------------------------------------------- /dyn_dllist/tests/basic/basic.c: -------------------------------------------------------------------------------- 1 | #include "basic.h" 2 | #include "../../../tap/tap.h" 3 | #include 4 | 5 | struct basic_foo *basic_foo_new(int a, int b) 6 | { 7 | struct basic_foo *f = malloc(sizeof(struct basic_foo)); 8 | assert(f != NULL); 9 | f->a = a; 10 | f->b = b; 11 | return f; 12 | } 13 | void basic_foo_free(struct basic_foo *f) 14 | { 15 | assert(f != NULL); 16 | free(f); 17 | } 18 | 19 | #define DYN_DLLIST_VALUE_TYPE struct basic_foo 20 | #define DYN_DLLIST_TYPE_NAME bfoo_list 21 | #define DYN_DLLIST_IMPLEMENTATION 22 | #include "../../dyn_dllist.h" 23 | #undef DYN_DLLIST_IMPLEMENTATION 24 | #undef DYN_DLLIST_TYPE_NAME 25 | #undef DYN_DLLIST_VALUE_TYPE 26 | 27 | int list_voidp_equals(struct bfoo_list *list, struct basic_foo *val, void *param) 28 | { 29 | return val == param; 30 | } 31 | 32 | int list_voidp_not_equals(struct bfoo_list *list, struct basic_foo *val, void *param) 33 | { 34 | return val != param; 35 | } 36 | 37 | int list_voidp_remove_if_equals(struct bfoo_list *list, struct basic_foo *val, void *param) 38 | { 39 | if (val == param) 40 | { 41 | bfoo_list_remove_all(list, val); 42 | return 1; // Stops. 43 | } 44 | return 0; 45 | } 46 | 47 | int list_voidp_remove_if_not_equals(struct bfoo_list *list, struct basic_foo *val, void *param) 48 | { 49 | if (val != param) 50 | { 51 | bfoo_list_remove_all(list, val); 52 | return 1; // Stops. 53 | } 54 | return 0; 55 | } 56 | 57 | void test_basic_foo(void) 58 | { 59 | tap_diag("Running tests for test_basic_foo()"); 60 | struct basic_foo *f1 = basic_foo_new(1, 2); 61 | struct basic_foo *f2 = basic_foo_new(3, 4); 62 | struct basic_foo *f3 = basic_foo_new(5, 6); 63 | struct bfoo_list *list = bfoo_list_new(); 64 | tap_is_int(bfoo_list_size(list), 0, "new list is empty"); 65 | tap_is_int(bfoo_list_contains(list, f1), 0, "list does not contain f1"); 66 | bfoo_list_append1_ftl(list, f1); 67 | tap_is_int(bfoo_list_size(list), 1, "list has size 1 after one append"); 68 | tap_is_int(bfoo_list_contains(list, f1), 1, "list contains f1 after append"); 69 | tap_is_int(bfoo_list_countif(list, list_voidp_equals, f1), 1, "list contains f1 once"); 70 | bfoo_list_append1_ftl(list, f2); 71 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after two appends"); 72 | tap_is_int(bfoo_list_contains(list, f1), 1, "list still contains f1 after append"); 73 | tap_is_int(bfoo_list_countif(list, list_voidp_equals, f1), 1, "list contains f1 once"); 74 | tap_is_int(bfoo_list_contains(list, f2), 1, "list contains f2 after append"); 75 | tap_is_int(bfoo_list_countif(list, list_voidp_equals, f2), 1, "list contains f2 once"); 76 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1"); 77 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2"); 78 | bfoo_list_append1_ftl(list, f2); 79 | tap_is_int(bfoo_list_size(list), 2, "list still has size 2 after append1_ftl(f2)"); 80 | { 81 | struct basic_foo *f = bfoo_list_shift(list); 82 | tap_is_voidp(f, f1, "shifted element is f1"); 83 | tap_is_int(bfoo_list_size(list), 1, "list has size 1 after shift"); 84 | tap_is_voidp(bfoo_list_peek_first(list), f2, "first element is f2 after shift"); 85 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after shift"); 86 | } 87 | bfoo_list_prepend1_ftl(list, f3); 88 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after prepend1_ftl(f3)"); 89 | { 90 | struct basic_foo *f = bfoo_list_pop(list); 91 | tap_is_voidp(f, f2, "popped element is f2"); 92 | tap_is_int(bfoo_list_size(list), 1, "list has size 1 after pop"); 93 | tap_is_voidp(bfoo_list_peek_first(list), f3, "first element is f3 after pop"); 94 | tap_is_voidp(bfoo_list_peek_last(list), f3, "last element is f3 after pop"); 95 | } 96 | bfoo_list_prepend1_ftl(list, f1); 97 | bfoo_list_prepend1_ftl(list, f2); 98 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after prepend1_ftl(f2)"); 99 | { 100 | struct basic_foo *f = bfoo_list_pop(list); 101 | tap_is_voidp(f, f3, "popped element is f3"); 102 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after pop"); 103 | tap_is_voidp(bfoo_list_peek_first(list), f2, "first element is f2 after pop"); 104 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after pop"); 105 | } 106 | { 107 | struct basic_foo *f = bfoo_list_shift(list); 108 | tap_is_voidp(f, f2, "shifted element is f2"); 109 | tap_is_int(bfoo_list_size(list), 1, "list has size 1 after shift"); 110 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after shift"); 111 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after shift"); 112 | } 113 | bfoo_list_append(list, f2); 114 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after append(f2)"); 115 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after append(f2)"); 116 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after append(f2)"); 117 | bfoo_list_append(list, f2); 118 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after append(f2)"); 119 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after append(f2)"); 120 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after append(f2)"); 121 | tap_is_int(bfoo_list_countif(list, list_voidp_equals, f2), 2, "list contains f2 twice"); 122 | { 123 | struct bfoo_list *f2s = bfoo_list_grep(list, list_voidp_equals, f2); 124 | tap_is_int(bfoo_list_size(f2s), 2, "f2s has size 2"); 125 | tap_is_voidp(bfoo_list_peek_first(f2s), f2, "first element of f2s is f2"); 126 | tap_is_voidp(bfoo_list_peek_last(f2s), f2, "last element of f2s is f2"); 127 | bfoo_list_free(f2s); 128 | } 129 | { 130 | struct basic_foo *f = bfoo_list_pop(list); 131 | tap_is_voidp(f, f2, "popped element is f2"); 132 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after pop"); 133 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after pop"); 134 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after pop"); 135 | } 136 | { 137 | struct basic_foo *f = bfoo_list_shift(list); 138 | tap_is_voidp(f, f1, "shifted element is f1"); 139 | tap_is_int(bfoo_list_size(list), 1, "list has size 1 after shift"); 140 | tap_is_voidp(bfoo_list_peek_first(list), f2, "first element is f2 after shift"); 141 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after shift"); 142 | } 143 | { 144 | struct basic_foo *f = bfoo_list_pop(list); 145 | tap_is_voidp(f, f2, "popped element is f2"); 146 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after pop"); 147 | } 148 | bfoo_list_append(list, f2); 149 | bfoo_list_append(list, f1); 150 | bfoo_list_append(list, f3); 151 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after append(f1)"); 152 | tap_is_int(bfoo_list_contains_ftl(list, f1), 1, "list contains [ltf] f1 after append(f1)"); 153 | tap_is_int(bfoo_list_contains_ltf(list, f1), 1, "list contains [ftl] f1 after append(f1)"); 154 | tap_is_int(bfoo_list_contains_ftl(list, f2), 1, "list contains [ltf] f2 after append(f1)"); 155 | tap_is_int(bfoo_list_contains_ltf(list, f2), 1, "list contains [ftl] f2 after append(f1)"); 156 | tap_is_int(bfoo_list_contains_ftl(list, f3), 1, "list contains [ltf] f3 after append(f1)"); 157 | tap_is_int(bfoo_list_contains_ltf(list, f3), 1, "list contains [ftl] f3 after append(f1)"); 158 | (void)bfoo_list_pop(list); 159 | (void)bfoo_list_shift(list); 160 | tap_is_int(bfoo_list_size(list), 1, "list has size 1 after pop and shift"); 161 | tap_is_int(bfoo_list_contains_ftl(list, f2), 0, "list does not contain [ltf] f2 after pop and shift"); 162 | tap_is_int(bfoo_list_contains_ltf(list, f2), 0, "list does not contain [ftl] f2 after pop and shift"); 163 | tap_is_int(bfoo_list_contains_ftl(list, f3), 0, "list does not contain [ltf] f3 after pop and shift"); 164 | tap_is_int(bfoo_list_contains_ltf(list, f3), 0, "list does not contain [ftl] f3 after pop and shift"); 165 | tap_is_int(bfoo_list_contains_ftl(list, f1), 1, "list contains [ltf] f1 after pop and shift"); 166 | tap_is_int(bfoo_list_contains_ltf(list, f1), 1, "list contains [ftl] f1 after pop and shift"); 167 | bfoo_list_append1_ftl(list, f2); 168 | bfoo_list_append1_ftl(list, f3); 169 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after append1_ftl(f2) and append1_ftl(f3)"); 170 | bfoo_list_append1_ftl(list, f1); 171 | tap_is_int(bfoo_list_size(list), 3, "list has size 4 after append1_ftl(f1)"); 172 | tap_is_int(bfoo_list_contains_ftl(list, f1), 1, "list contains [ltf] f1 after append1_ftl(f1)"); 173 | tap_is_voidp(bfoo_list_peek_last(list), f3, "last element is f3 after append1_ftl(f1)"); 174 | (void)bfoo_list_pop(list); 175 | (void)bfoo_list_pop(list); 176 | (void)bfoo_list_pop(list); 177 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after 3 pops"); 178 | bfoo_list_append1_ltf(list, f1); 179 | bfoo_list_append1_ltf(list, f2); 180 | bfoo_list_append1_ltf(list, f3); 181 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after append1_ltf(f1), append1_ltf(f2), append1_ltf(f3)"); 182 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after append1_ltf(f1), append1_ltf(f2), append1_ltf(f3)"); 183 | tap_is_voidp(bfoo_list_peek_last(list), f3, "last element is f3 after append1_ltf(f1), append1_ltf(f2), append1_ltf(f3)"); 184 | (void)bfoo_list_pop(list); 185 | (void)bfoo_list_pop(list); 186 | (void)bfoo_list_pop(list); 187 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after 3 pops"); 188 | bfoo_list_prepend1_ltf(list, f1); 189 | bfoo_list_prepend1_ltf(list, f2); 190 | bfoo_list_prepend1_ltf(list, f3); 191 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after prepend1_ltf(f1), prepend1_ltf(f2), prepend1_ltf(f3)"); 192 | tap_is_voidp(bfoo_list_peek_first(list), f3, "first element is f3 after prepend1_ltf(f1), prepend1_ltf(f2), prepend1_ltf(f3)"); 193 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after prepend1_ltf(f1), prepend1_ltf(f2), prepend1_ltf(f3)"); 194 | bfoo_list_remove_all(list, f1); 195 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after remove_all(list, f1)"); 196 | tap_is_voidp(bfoo_list_peek_first(list), f3, "first element is f3 after remove_all(list, f1)"); 197 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after remove_all(list, f1)"); 198 | (void)bfoo_list_pop(list); 199 | (void)bfoo_list_pop(list); 200 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after 2 pops"); 201 | bfoo_list_append(list, f1); 202 | bfoo_list_append(list, f2); 203 | bfoo_list_append(list, f3); 204 | bfoo_list_append(list, f1); 205 | tap_is_int(bfoo_list_size(list), 4, "list has size 4 after append(f1), append(f2), append(f3), append(f1)"); 206 | bfoo_list_removenth_ftl(list, f1, 0); 207 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after removenth_ftl(list, f1, 0)"); 208 | tap_is_voidp(bfoo_list_peek_first(list), f2, "first element is f2 after removenth_ftl(list, f1, 0)"); 209 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after removenth_ftl(list, f1, 0)"); 210 | bfoo_list_prepend(list, f1); 211 | tap_is_int(bfoo_list_size(list), 4, "list has size 4 after prepend(f1)"); 212 | bfoo_list_removenth_ltf(list, f1, 0); 213 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after removenth_ltf(list, f1, 0)"); 214 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after removenth_ltf(list, f1, 0)"); 215 | tap_is_voidp(bfoo_list_peek_last(list), f3, "last element is f3 after removenth_ltf(list, f1, 0)"); 216 | bfoo_list_append(list, f1); 217 | tap_is_int(bfoo_list_size(list), 4, "list has size 4 after append(f1)"); 218 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after append(f1)"); 219 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after append(f1)"); 220 | { 221 | struct basic_foo *f = bfoo_list_nth_ftl(list, list_voidp_equals, f1, 0); 222 | tap_is_voidp(f, f1, "nth_ftl(list, list_voidp_equals, f1, 0) is f1"); 223 | } 224 | { 225 | struct basic_foo *f = bfoo_list_nth_ftl(list, list_voidp_not_equals, f1, 0); 226 | tap_is_voidp(f, f2, "nth_ftl(list, list_voidp_not_equals, f1, 0) is f2"); 227 | } 228 | { 229 | struct basic_foo *f = bfoo_list_nth_ltf(list, list_voidp_equals, f1, 0); 230 | tap_is_voidp(f, f1, "nth_ltf(list, list_voidp_equals, f1, 0) is f1"); 231 | } 232 | { 233 | struct basic_foo *f = bfoo_list_nth_ltf(list, list_voidp_not_equals, f1, 0); 234 | tap_is_voidp(f, f3, "nth_ltf(list, list_voidp_not_equals, f1, 0) is f3"); 235 | } 236 | // F1 F2 F3 F1 237 | tap_is_int(bfoo_list_size(list), 4, "list has still size 4"); 238 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1"); 239 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1"); 240 | { 241 | // F1 F2 F3 F1 -> F1 F3 F1, as it should stop at the first one. 242 | bfoo_list_iterate_ftl(list, list_voidp_remove_if_not_equals, f1); 243 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after iterate_ftl(list, list_voidp_remove_if_not_equals, f1)"); 244 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after iterate_ftl(list, list_voidp_remove_if_not_equals, f1)"); 245 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after iterate_ftl(list, list_voidp_remove_if_not_equals, f1)"); 246 | // F1 F3 F1 -> F3 F1, as it should stop at the first one. 247 | (void)bfoo_list_shift(list); 248 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after shift"); 249 | tap_is_voidp(bfoo_list_peek_first(list), f3, "first element is f3 after shift"); 250 | (void)bfoo_list_shift(list); 251 | (void)bfoo_list_shift(list); 252 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after 2 shifts"); 253 | } 254 | bfoo_list_append(list, f1); 255 | bfoo_list_append(list, f2); 256 | bfoo_list_append(list, f3); 257 | bfoo_list_append(list, f1); 258 | tap_is_int(bfoo_list_size(list), 4, "list has size 4 after append(f1), append(f2), append(f3), append(f1)"); 259 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after append(f1), append(f2), append(f3), append(f1)"); 260 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after append(f1), append(f2), append(f3), append(f1)"); 261 | { 262 | // F1 F2 F3 F1 -> F1 F2 F1, as it should stop at the first one from the RIGHT. 263 | bfoo_list_iterate_ltf(list, list_voidp_remove_if_not_equals, f1); 264 | tap_is_int(bfoo_list_size(list), 3, "list has size 3 after iterate_ltf(list, list_voidp_remove_if_not_equals, f1)"); 265 | tap_is_voidp(bfoo_list_peek_first(list), f1, "first element is f1 after iterate_ltf(list, list_voidp_remove_if_not_equals, f1)"); 266 | tap_is_voidp(bfoo_list_peek_last(list), f1, "last element is f1 after iterate_ltf(list, list_voidp_remove_if_not_equals, f1)"); 267 | // F1 F2 F1 -> F1 F2, as it should stop at the first one from the RIGHT. 268 | (void)bfoo_list_pop(list); 269 | tap_is_int(bfoo_list_size(list), 2, "list has size 2 after pop"); 270 | tap_is_voidp(bfoo_list_peek_last(list), f2, "last element is f2 after pop"); 271 | (void)bfoo_list_pop(list); 272 | (void)bfoo_list_pop(list); 273 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after 2 pops"); 274 | } 275 | (void)bfoo_list_pop(list); 276 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after spurious pop"); 277 | (void)bfoo_list_shift(list); 278 | tap_is_int(bfoo_list_size(list), 0, "list has size 0 after spurious shift"); 279 | bfoo_list_free(list); 280 | basic_foo_free(f1); 281 | basic_foo_free(f2); 282 | basic_foo_free(f3); 283 | } 284 | -------------------------------------------------------------------------------- /dyn_arr/dyn_arr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * `dyn_arr.h` - C "dynamic" array-like header-only library 3 | * Copyright (c) 2024 Marco Fontani - MFONTANI@cpan.org 4 | */ 5 | 6 | #ifndef DYN_ARR_VALUE_TYPE 7 | #error "DYN_ARR_VALUE_TYPE must be defined before including dyn_arr.h" 8 | #endif 9 | 10 | #ifndef DYN_ARR_TYPE_NAME 11 | #error "DYN_ARR_TYPE_NAME must be defined before including dyn_arr.h" 12 | #endif 13 | 14 | #ifdef DYN_ARR_IMPLEMENTATION 15 | # define DYN_ARR_EXTERN 16 | #else 17 | # define DYN_ARR_EXTERN extern 18 | #endif 19 | 20 | #ifdef DYN_ARR__ 21 | #error "dyn_arr.h uses DYN_ARR__ as a macro, but it's already defined" 22 | #endif 23 | 24 | #define DYN_ARR__(x,y) x##y 25 | 26 | #ifdef DYN_ARR_ 27 | #error "dyn_arr.h uses DYN_ARR_ as a macro, but it's already defined" 28 | #endif 29 | 30 | #define DYN_ARR_(x,y) DYN_ARR__(x,y) 31 | 32 | #ifdef DYN_ARR_F 33 | #error "dyn_arr.h uses DYN_ARR_F as a macro, but it's already defined" 34 | #endif 35 | 36 | #define DYN_ARR_F(x, y) DYN_ARR_(x, y) 37 | 38 | #ifdef DYN_ARR_IMPLEMENTATION 39 | 40 | struct DYN_ARR_TYPE_NAME { 41 | // Setup data: 42 | size_t size; // Allocated size for "members" 43 | size_t buffer_size; // Size of the left/right initial "buffer" 44 | size_t min_buffer; // Minimum size of the left/right "buffer" to trigger a resize 45 | // Implementation data: 46 | size_t length; // Used size for "members" 47 | size_t istart; // Index of the first element 48 | size_t iend; // Index of the last element 49 | DYN_ARR_VALUE_TYPE *members[]; // of size "size" 50 | }; 51 | 52 | #else 53 | 54 | struct DYN_ARR_TYPE_NAME; 55 | 56 | #endif 57 | 58 | DYN_ARR_EXTERN 59 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _new)(size_t size, size_t buffer_size, size_t min_buffer); 60 | DYN_ARR_EXTERN 61 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _free)(struct DYN_ARR_TYPE_NAME *arr); 62 | DYN_ARR_EXTERN 63 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _resize)(struct DYN_ARR_TYPE_NAME *arr, size_t new_size, size_t new_buffer_size, size_t new_min_buffer); 64 | DYN_ARR_EXTERN 65 | size_t DYN_ARR_F(DYN_ARR_TYPE_NAME, _length)(struct DYN_ARR_TYPE_NAME *arr); 66 | DYN_ARR_EXTERN 67 | size_t DYN_ARR_F(DYN_ARR_TYPE_NAME, _size)(struct DYN_ARR_TYPE_NAME *arr); 68 | DYN_ARR_EXTERN 69 | int DYN_ARR_F(DYN_ARR_TYPE_NAME, _empty)(struct DYN_ARR_TYPE_NAME *arr); 70 | DYN_ARR_EXTERN 71 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _push)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *value); 72 | DYN_ARR_EXTERN 73 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _unshift)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *value); 74 | DYN_ARR_EXTERN 75 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _front)(struct DYN_ARR_TYPE_NAME *arr); 76 | DYN_ARR_EXTERN 77 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _back)(struct DYN_ARR_TYPE_NAME *arr); 78 | DYN_ARR_EXTERN 79 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _get)(struct DYN_ARR_TYPE_NAME *arr, size_t idx); 80 | DYN_ARR_EXTERN 81 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _set)(struct DYN_ARR_TYPE_NAME *arr, size_t idx, DYN_ARR_VALUE_TYPE *value); 82 | DYN_ARR_EXTERN 83 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _del)(struct DYN_ARR_TYPE_NAME *arr, size_t idx); 84 | DYN_ARR_EXTERN 85 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _shift)(struct DYN_ARR_TYPE_NAME *arr); 86 | DYN_ARR_EXTERN 87 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _pop)(struct DYN_ARR_TYPE_NAME *arr); 88 | 89 | DYN_ARR_EXTERN 90 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _fill)(struct DYN_ARR_TYPE_NAME *arr, size_t size, DYN_ARR_VALUE_TYPE *value); 91 | 92 | DYN_ARR_EXTERN 93 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _remove_all)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *value); 94 | 95 | DYN_ARR_EXTERN 96 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _unique)(struct DYN_ARR_TYPE_NAME *arr); 97 | 98 | DYN_ARR_EXTERN 99 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _clear)(struct DYN_ARR_TYPE_NAME *arr); 100 | 101 | DYN_ARR_EXTERN 102 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _iterate_ftl)(struct DYN_ARR_TYPE_NAME *arr, int (*func)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *val, void *param), void *param); 103 | DYN_ARR_EXTERN 104 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _iterate_ltf)(struct DYN_ARR_TYPE_NAME *arr, int (*func)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *val, void *param), void *param); 105 | 106 | DYN_ARR_EXTERN 107 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _status)(struct DYN_ARR_TYPE_NAME *arr); 108 | 109 | #undef DYN_ARR_EXTERN 110 | 111 | #ifdef DYN_ARR_IMPLEMENTATION 112 | 113 | // Create a new "array" of this type. Remember to _free() it! 114 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _new)(size_t size, size_t buffer_size, size_t min_buffer) 115 | { 116 | struct DYN_ARR_TYPE_NAME *arr = malloc( 117 | sizeof(struct DYN_ARR_TYPE_NAME) + 118 | sizeof(DYN_ARR_VALUE_TYPE *) * size + 119 | 0 120 | ); 121 | if (!arr) 122 | { 123 | perror("_new: malloc[arr]"); 124 | exit(EXIT_FAILURE); 125 | } 126 | if (min_buffer == 0) 127 | min_buffer = 1; 128 | if (buffer_size == 0) 129 | buffer_size = 1; 130 | if (buffer_size < min_buffer) 131 | buffer_size = min_buffer + 2; 132 | if (size < buffer_size) 133 | size = buffer_size * 2; 134 | arr->size = size; 135 | arr->buffer_size = buffer_size; 136 | arr->min_buffer = min_buffer; 137 | arr->length = 0; 138 | arr->istart = buffer_size - 1; 139 | arr->iend = arr->istart; 140 | for (size_t i = 0; i < size; i++) 141 | arr->members[i] = NULL; 142 | return arr; 143 | } 144 | 145 | // Free an "array" of this type, including inner members but NOT including the 146 | // values in them. That's up to the caller! 147 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _free)(struct DYN_ARR_TYPE_NAME *arr) 148 | { 149 | if (!arr) 150 | return; 151 | free(arr); 152 | } 153 | 154 | // Create a new array with the given parameters, copying the values from the 155 | // old one. Remember to _free() it! 156 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _resize)(struct DYN_ARR_TYPE_NAME *arr, size_t new_size, size_t new_buffer_size, size_t new_min_buffer) 157 | { 158 | struct DYN_ARR_TYPE_NAME *new_arr = DYN_ARR_F(DYN_ARR_TYPE_NAME, _new)(new_size, new_buffer_size, new_min_buffer); 159 | size_t j = new_arr->istart; 160 | for (size_t i = arr->istart; i <= arr->iend; i++, j++) 161 | new_arr->members[j] = arr->members[i]; 162 | new_arr->iend = new_arr->istart + arr->length - 1; 163 | new_arr->length = arr->length; 164 | return new_arr; 165 | } 166 | 167 | // Returns the amount of actually filled "member slots" in the array. 168 | // Doesn't need to iterate it. 169 | size_t DYN_ARR_F(DYN_ARR_TYPE_NAME, _length)(struct DYN_ARR_TYPE_NAME *arr) 170 | { 171 | if (!arr) 172 | return 0; 173 | return arr->length; 174 | } 175 | 176 | // Returns whether the array is empty. 177 | // Doesn't need to iterate it. 178 | int DYN_ARR_F(DYN_ARR_TYPE_NAME, _empty)(struct DYN_ARR_TYPE_NAME *arr) 179 | { 180 | if (!arr) 181 | return 0; 182 | return !arr->length; 183 | } 184 | 185 | // Returns the amount of possible "member slots" in the array. 186 | // Doesn't need to iterate it. 187 | size_t DYN_ARR_F(DYN_ARR_TYPE_NAME, _size)(struct DYN_ARR_TYPE_NAME *arr) 188 | { 189 | if (!arr) 190 | return 0; 191 | return arr->size; 192 | } 193 | 194 | // Push a value at the end of the array. 195 | // Note this could return a NEW array! 196 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _push)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *value) 197 | { 198 | if (!arr) 199 | return NULL; 200 | if (arr->iend + 1 >= arr->size - arr->min_buffer) 201 | { 202 | struct DYN_ARR_TYPE_NAME *new_arr = DYN_ARR_F(DYN_ARR_TYPE_NAME, _resize)(arr, arr->size * 2, arr->buffer_size, arr->min_buffer); 203 | DYN_ARR_F(DYN_ARR_TYPE_NAME, _free)(arr); 204 | arr = new_arr; 205 | } 206 | if (arr->length) 207 | arr->iend++; 208 | arr->members[arr->iend] = value; 209 | arr->length++; 210 | return arr; 211 | } 212 | 213 | // Prepend (unshift) a value at the beginning of the array. 214 | // Note this could return a NEW array! 215 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _unshift)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *value) 216 | { 217 | if (!arr) 218 | return NULL; 219 | if (arr->istart <= arr->min_buffer - 1) 220 | { 221 | struct DYN_ARR_TYPE_NAME *new_arr = DYN_ARR_F(DYN_ARR_TYPE_NAME, _resize)(arr, arr->size * 2, arr->buffer_size, arr->min_buffer); 222 | DYN_ARR_F(DYN_ARR_TYPE_NAME, _free)(arr); 223 | arr = new_arr; 224 | } 225 | if (arr->length) 226 | arr->istart--; 227 | arr->members[arr->istart] = value; 228 | arr->length++; 229 | return arr; 230 | } 231 | 232 | // Get the _first_ value from the array. 233 | // Could be NULL if the index is out of bounds. 234 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _front)(struct DYN_ARR_TYPE_NAME *arr) 235 | { 236 | if (!arr || !arr->length) 237 | return NULL; 238 | return arr->members[arr->istart]; 239 | } 240 | 241 | // Get the _last_ value from the array. 242 | // Could be NULL if the index is out of bounds. 243 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _back)(struct DYN_ARR_TYPE_NAME *arr) 244 | { 245 | if (!arr || !arr->length) 246 | return NULL; 247 | return arr->members[arr->istart + arr->length - 1]; 248 | } 249 | 250 | // Get the value at index "idx" from the array. 251 | // Could be NULL if the index is out of bounds. 252 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _get)(struct DYN_ARR_TYPE_NAME *arr, size_t idx) 253 | { 254 | if (!arr) 255 | return NULL; 256 | if (idx >= arr->length) 257 | return NULL; 258 | return arr->members[arr->istart + idx]; 259 | } 260 | 261 | // Set the value at index "idx" from the array (assumes "idx" is valid). 262 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _set)(struct DYN_ARR_TYPE_NAME *arr, size_t idx, DYN_ARR_VALUE_TYPE *value) 263 | { 264 | if (!arr) 265 | return; 266 | if (idx >= arr->length) 267 | return; 268 | arr->members[arr->istart + idx] = value; 269 | } 270 | 271 | // Delete the value at index "idx" from the array (assumes "idx" is valid). 272 | // The array is shifted to the left as a result, to fill the gap. 273 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _del)(struct DYN_ARR_TYPE_NAME *arr, size_t idx) 274 | { 275 | if (!arr) 276 | return; 277 | if (idx >= arr->length) 278 | return; 279 | // Shift the array 280 | size_t j = arr->istart + idx + 1; 281 | while (j <= arr->iend) 282 | { 283 | arr->members[j - 1] = arr->members[j]; 284 | j++; 285 | } 286 | arr->members[arr->iend] = NULL; 287 | arr->iend--; 288 | arr->length--; 289 | } 290 | 291 | // Shift the array to the left, removing (and returning!) the first element. 292 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _shift)(struct DYN_ARR_TYPE_NAME *arr) 293 | { 294 | if (!arr) 295 | return NULL; 296 | if (!arr->length) 297 | return NULL; 298 | DYN_ARR_VALUE_TYPE *value = arr->members[arr->istart]; 299 | arr->members[arr->istart] = NULL; 300 | arr->length--; 301 | if (arr->length) 302 | arr->istart++; 303 | return value; 304 | } 305 | 306 | // Remove, and return, the last element from the array. 307 | DYN_ARR_VALUE_TYPE *DYN_ARR_F(DYN_ARR_TYPE_NAME, _pop)(struct DYN_ARR_TYPE_NAME *arr) 308 | { 309 | if (!arr) 310 | return NULL; 311 | if (!arr->length) 312 | return NULL; 313 | DYN_ARR_VALUE_TYPE *value = arr->members[arr->iend]; 314 | arr->members[arr->iend] = NULL; 315 | arr->length--; 316 | if (arr->length) 317 | arr->iend--; 318 | return value; 319 | } 320 | 321 | // Iterate the array from first to last, calling "func" for each element, and 322 | // passing "param" to "func" for each iteration. 323 | // If "func" returns a non-zero value, the iteration is stopped. 324 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _iterate_ftl)(struct DYN_ARR_TYPE_NAME *arr, int (*func)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *val, void *param), void *param) 325 | { 326 | if (!arr) 327 | return; 328 | if (!func) 329 | return; 330 | if (!arr->length) 331 | return; 332 | for (size_t i = arr->istart; i <= arr->iend; i++) 333 | if (func(arr, arr->members[i], param)) 334 | return; 335 | } 336 | 337 | // Iterate the array from last to first, calling "func" for each element, and 338 | // passing "param" to "func" for each iteration. 339 | // If "func" returns a non-zero value, the iteration is stopped. 340 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _iterate_ltf)(struct DYN_ARR_TYPE_NAME *arr, int (*func)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *val, void *param), void *param) 341 | { 342 | if (!arr) 343 | return; 344 | if (!func) 345 | return; 346 | if (!arr->length) 347 | return; 348 | for (size_t i = arr->iend; i >= arr->istart; i--) 349 | if (func(arr, arr->members[i], param)) 350 | return; 351 | } 352 | 353 | // Pre-fills the array with "size" elements, all set to "value". 354 | // If the array already has elements, they are pushed at the end. 355 | // Shortcut for readability. Uses _push internally. 356 | struct DYN_ARR_TYPE_NAME *DYN_ARR_F(DYN_ARR_TYPE_NAME, _fill)(struct DYN_ARR_TYPE_NAME *arr, size_t size, DYN_ARR_VALUE_TYPE *value) 357 | { 358 | if (!arr) 359 | return NULL; 360 | for (size_t i = 0; i < size; i++) 361 | arr = DYN_ARR_F(DYN_ARR_TYPE_NAME, _push)(arr, value); 362 | return arr; 363 | } 364 | 365 | // Remove all the elements from the array that match the given value. 366 | // The array is shifted to the left after every removal, to fill the gap. 367 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _remove_all)(struct DYN_ARR_TYPE_NAME *arr, DYN_ARR_VALUE_TYPE *value) 368 | { 369 | if (!arr) 370 | return; 371 | if (!arr->length) 372 | return; 373 | size_t i = arr->istart; 374 | while (i <= arr->iend) 375 | { 376 | if (arr->members[i] == value) 377 | { 378 | // Remove this value 379 | arr->members[i] = NULL; 380 | // Shift the array 381 | size_t j = i + 1; 382 | while (j <= arr->iend) 383 | { 384 | arr->members[j - 1] = arr->members[j]; 385 | j++; 386 | } 387 | arr->iend--; 388 | arr->length--; 389 | } 390 | else 391 | { 392 | i++; 393 | } 394 | } 395 | } 396 | 397 | // Remove all the duplicate elements from the array. 398 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _unique)(struct DYN_ARR_TYPE_NAME *arr) 399 | { 400 | if (!arr) 401 | return; 402 | if (!arr->length) 403 | return; 404 | size_t i = arr->istart; 405 | while (i <= arr->iend) 406 | { 407 | size_t j = i + 1; 408 | while (j <= arr->iend) 409 | { 410 | if (arr->members[i] == arr->members[j]) 411 | { 412 | // Remove this value 413 | arr->members[j] = NULL; 414 | // Shift the array 415 | size_t k = j + 1; 416 | while (k <= arr->iend) 417 | { 418 | arr->members[k - 1] = arr->members[k]; 419 | k++; 420 | } 421 | arr->iend--; 422 | arr->length--; 423 | } 424 | else 425 | { 426 | j++; 427 | } 428 | } 429 | i++; 430 | } 431 | } 432 | 433 | // Remove all the elements from the array. 434 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _clear)(struct DYN_ARR_TYPE_NAME *arr) 435 | { 436 | if (!arr) 437 | return; 438 | if (!arr->length) 439 | return; 440 | for (size_t i = arr->istart; i <= arr->iend; i++) 441 | arr->members[i] = NULL; 442 | arr->length = 0; 443 | arr->istart = arr->buffer_size - 1; 444 | arr->iend = arr->istart; 445 | } 446 | 447 | // Print the status/dump of the array to stdout. 448 | void DYN_ARR_F(DYN_ARR_TYPE_NAME, _status)(struct DYN_ARR_TYPE_NAME *arr) 449 | { 450 | if (!arr) 451 | { 452 | printf("Array is NULL\n"); 453 | return; 454 | } 455 | printf("Array: %p, length/size %zu/%zu, min_buffer/buffer_size %zu/%zu istart %zu iend %zu:\n", 456 | (void *)arr, arr->length, arr->size, arr->min_buffer, arr->buffer_size, arr->istart, arr->iend); 457 | for (size_t i = 0; i < arr->size; i++) 458 | { 459 | printf(" %3zu", i); 460 | if (i == arr->istart) 461 | printf(" [ "); 462 | else 463 | printf(" "); 464 | if (i == arr->iend) 465 | printf("]"); 466 | else 467 | printf(" "); 468 | printf(" %p", (void *)arr->members[i]); 469 | if (i == arr->istart) 470 | printf(" <--- istart"); 471 | if (i == arr->iend) 472 | printf(" <--- iend"); 473 | printf("\n"); 474 | } 475 | } 476 | 477 | #endif 478 | 479 | #undef DYN_ARR_F 480 | #undef DYN_ARR_ 481 | #undef DYN_ARR__ 482 | -------------------------------------------------------------------------------- /dyn_kvp/dyn_kvp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * `dyn_kvp.h` - C "dynamic" key/value "hash" library 3 | * Copyright (c) 2024 Marco Fontani - MFONTANI@cpan.org 4 | */ 5 | 6 | #ifndef DYN_KVP_VALUE_TYPE 7 | # error "DYN_KVP_VALUE_TYPE must be defined before including dyn_kvp.h" 8 | #endif 9 | 10 | #ifndef DYN_KVP_TYPE_NAME 11 | # error "DYN_KVP_TYPE_NAME must be defined before including dyn_kvp.h" 12 | #endif 13 | 14 | #ifdef DYN_KVP_HASH_FUNCTION_DEFINED 15 | # error "dyn_kvp.h uses DYN_KVP_HASH_FUNCTION_DEFINED as a macro, but it's already defined" 16 | #endif 17 | 18 | #ifdef DYN_KVP_IMPLEMENTATION 19 | # define DYN_KVP_EXTERN 20 | # ifndef DYN_KVP_HASH_FUNCTION 21 | # ifndef DYN_KVP_KEY_TYPE 22 | # define DYN_KVP_HASH_FUNCTION(size, key) ((key) % (size)) 23 | # define DYN_KVP_HASH_FUNCTION_DEFINED 24 | # else 25 | # error "DYN_KVP_HASH_FUNCTION must be defined before including dyn_kvp.h, if using a custom DYN_KVP_KEY_TYPE" 26 | # endif 27 | # endif 28 | #else 29 | # define DYN_KVP_EXTERN extern 30 | #endif 31 | 32 | #ifdef DYN_KVP_KEY_TYPE_DEFINED 33 | # error "dyn_kvp.h uses DYN_KVP_KEY_TYPE_DEFINED as a macro, but it's already defined" 34 | #endif 35 | 36 | #ifndef DYN_KVP_KEY_TYPE 37 | # define DYN_KVP_KEY_TYPE unsigned long 38 | # define DYN_KVP_KEY_TYPE_DEFINED 39 | #endif 40 | 41 | #ifdef DYN_KVP__ 42 | # error "dyn_kvp.h uses DYN_KVP__ as a macro, but it's already defined" 43 | #endif 44 | 45 | #define DYN_KVP__(x,y) x##y 46 | 47 | #ifdef DYN_KVP_ 48 | # error "dyn_kvp.h uses DYN_KVP_ as a macro, but it's already defined" 49 | #endif 50 | 51 | #define DYN_KVP_(x,y) DYN_KVP__(x,y) 52 | 53 | #ifdef DYN_KVP_F 54 | # error "dyn_kvp.h uses DYN_KVP_F as a macro, but it's already defined" 55 | #endif 56 | 57 | #define DYN_KVP_F(x, y) DYN_KVP_(x, y) 58 | 59 | #ifdef DYN_KVP_MEMBER_NAME 60 | # error "dyn_kvp.h uses DYN_KVP_MEMBER_NAME as a macro, but it's already defined" 61 | #endif 62 | #define DYN_KVP_MEMBER_NAME DYN_KVP_F(DYN_KVP_TYPE_NAME, _kv) 63 | 64 | #ifndef DYN_KVP_IMPLEMENTATION 65 | struct DYN_KVP_F(DYN_KVP_TYPE_NAME, _kv) { 66 | DYN_KVP_KEY_TYPE key; 67 | DYN_KVP_VALUE_TYPE *value; 68 | struct DYN_KVP_F(DYN_KVP_TYPE_NAME, _kv) *next; 69 | }; 70 | #endif 71 | 72 | #ifndef DYN_KVP_IMPLEMENTATION 73 | // Struct to hold hash table statistics 74 | struct DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats) { 75 | size_t nkeys; // Number of keys in the hash table 76 | size_t size; // Number of buckets in the hash table 77 | size_t nused; // Number of non-empty buckets 78 | size_t nempty; // Number of empty buckets 79 | size_t ncollisions; // Number of buckets with more than one element 80 | size_t ninchainmin; // Minimum chain length among non-empty buckets 81 | size_t ninchainmax; // Maximum chain length among non-empty buckets 82 | long double avg_chain_len; // Average chain length 83 | double load_factor; // Load factor 84 | }; 85 | #endif 86 | 87 | #ifdef DYN_KVP_IMPLEMENTATION 88 | 89 | struct DYN_KVP_TYPE_NAME { 90 | size_t size; 91 | size_t nkeys; 92 | struct DYN_KVP_MEMBER_NAME *hash[]; 93 | }; 94 | 95 | #else 96 | 97 | struct DYN_KVP_TYPE_NAME; 98 | 99 | #endif 100 | 101 | DYN_KVP_EXTERN 102 | struct DYN_KVP_MEMBER_NAME **DYN_KVP_F(DYN_KVP_MEMBER_NAME, _new)(struct DYN_KVP_TYPE_NAME *hash); 103 | DYN_KVP_EXTERN 104 | void DYN_KVP_F(DYN_KVP_MEMBER_NAME, _free)(struct DYN_KVP_MEMBER_NAME **hash); 105 | DYN_KVP_EXTERN 106 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _new)(size_t size); 107 | DYN_KVP_EXTERN 108 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _free)(struct DYN_KVP_TYPE_NAME *hash); 109 | DYN_KVP_EXTERN 110 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _clear)(struct DYN_KVP_TYPE_NAME *hash); 111 | DYN_KVP_EXTERN 112 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key, DYN_KVP_VALUE_TYPE *val); 113 | DYN_KVP_EXTERN 114 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _copy)(struct DYN_KVP_TYPE_NAME *hash, size_t new_size); 115 | DYN_KVP_EXTERN 116 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _merge)(struct DYN_KVP_TYPE_NAME *hash1, struct DYN_KVP_TYPE_NAME *hash2, size_t new_size); 117 | DYN_KVP_EXTERN 118 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _remove)(struct DYN_KVP_TYPE_NAME *hash1, struct DYN_KVP_TYPE_NAME *hash2, size_t new_size); 119 | DYN_KVP_EXTERN 120 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _intersect)(struct DYN_KVP_TYPE_NAME *hash1, struct DYN_KVP_TYPE_NAME *hash2, size_t new_size); 121 | DYN_KVP_EXTERN 122 | int DYN_KVP_F(DYN_KVP_TYPE_NAME, _exists)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key); 123 | DYN_KVP_EXTERN 124 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _del)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key); 125 | DYN_KVP_EXTERN 126 | DYN_KVP_VALUE_TYPE *DYN_KVP_F(DYN_KVP_TYPE_NAME, _get)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key); 127 | DYN_KVP_EXTERN 128 | size_t DYN_KVP_F(DYN_KVP_TYPE_NAME, _nkeys)(struct DYN_KVP_TYPE_NAME *hash); 129 | DYN_KVP_EXTERN 130 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats_printf)(struct DYN_KVP_TYPE_NAME *hash); 131 | DYN_KVP_EXTERN 132 | struct DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats) DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats)(struct DYN_KVP_TYPE_NAME *hash); 133 | 134 | #undef DYN_KVP_EXTERN 135 | 136 | #ifdef DYN_KVP_IMPLEMENTATION 137 | 138 | // Free an "exploded" NULL-terminated array from a KVP of this type: 139 | void DYN_KVP_F(DYN_KVP_MEMBER_NAME, _free)(struct DYN_KVP_MEMBER_NAME **hash) 140 | { 141 | if (!hash) 142 | return; 143 | for (size_t i = 0; hash[i] != NULL; i++) 144 | free(hash[i]); 145 | free(hash); 146 | } 147 | 148 | // Create a NULL-terminated array from a KVP of this type. 149 | // Remember to _free() it! 150 | struct DYN_KVP_MEMBER_NAME **DYN_KVP_F(DYN_KVP_MEMBER_NAME, _new)(struct DYN_KVP_TYPE_NAME *hash) 151 | { 152 | struct DYN_KVP_MEMBER_NAME **new_hash = NULL; 153 | new_hash = malloc(sizeof(struct DYN_KVP_MEMBER_NAME *) * (hash->nkeys + 1)); 154 | if (!new_hash) 155 | { 156 | perror("malloc failure"); 157 | (void)fprintf(stderr, "Malloc failure at %s:%d\n", __FILE__, __LINE__); 158 | abort(); 159 | } 160 | for (size_t i = 0; i <= hash->nkeys; i++) 161 | new_hash[i] = NULL; 162 | int inew = 0; 163 | for (size_t i = 0; i < hash->size; i++) 164 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[i]; p; p = p->next) 165 | { 166 | // assert(inew < hash->nkeys); 167 | new_hash[inew] = malloc(sizeof(struct DYN_KVP_MEMBER_NAME)); 168 | if (!new_hash[inew]) 169 | { 170 | perror("malloc failure"); 171 | (void)fprintf(stderr, "Malloc failure at %s:%d\n", __FILE__, __LINE__); 172 | abort(); 173 | } 174 | new_hash[inew]->key = p->key; 175 | new_hash[inew]->value = p->value; 176 | new_hash[inew]->next = NULL; 177 | inew++; 178 | } 179 | return new_hash; 180 | } 181 | 182 | // Create a new KVP of this type, hashed over "size" slots. 183 | // Remember to _free() it! 184 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _new)(size_t size) 185 | { 186 | struct DYN_KVP_TYPE_NAME *hash = NULL; 187 | hash = malloc(sizeof(struct DYN_KVP_TYPE_NAME) + size * sizeof(void *)); 188 | if (!hash) 189 | { 190 | perror("malloc failure"); 191 | (void)fprintf(stderr, "Malloc failure at %s:%d\n", __FILE__, __LINE__); 192 | abort(); 193 | } 194 | hash->size = size; 195 | hash->nkeys = 0; 196 | for (size_t i = 0; i < size; i++) 197 | hash->hash[i] = NULL; 198 | return hash; 199 | } 200 | 201 | // Free a KVP of this type. 202 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _free)(struct DYN_KVP_TYPE_NAME *hash) 203 | { 204 | if (!hash) 205 | return; 206 | for (size_t i = 0; i < hash->size; i++) 207 | { 208 | struct DYN_KVP_MEMBER_NAME *pnext = NULL; 209 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[i]; p; p = pnext) 210 | { 211 | pnext = p->next; 212 | free(p); 213 | } 214 | } 215 | free(hash); 216 | } 217 | 218 | // Clear a KVP of this type. 219 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _clear)(struct DYN_KVP_TYPE_NAME *hash) 220 | { 221 | if (!hash || !hash->nkeys) 222 | return; 223 | for (size_t i = 0; i < hash->size; i++) 224 | { 225 | struct DYN_KVP_MEMBER_NAME *pnext = NULL; 226 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[i]; p; p = pnext) 227 | { 228 | pnext = p->next; 229 | free(p); 230 | } 231 | hash->hash[i] = NULL; 232 | } 233 | hash->nkeys = 0; 234 | } 235 | 236 | #ifndef DYN_KVP_COMPARE_FUNCTION 237 | # define DYN_KVP_COMPARE_FUNCTION(x, y) ((x) == (y)) 238 | #endif 239 | 240 | // Set a "key" to "val" in a KVP of this type. 241 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key, DYN_KVP_VALUE_TYPE *val) 242 | { 243 | if (!hash) 244 | return; 245 | unsigned int hash_index = DYN_KVP_HASH_FUNCTION(hash->size, key); 246 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[hash_index]; p; p = p->next) 247 | if (DYN_KVP_COMPARE_FUNCTION(p->key, key)) 248 | { 249 | p->value = val; 250 | return; 251 | } 252 | struct DYN_KVP_MEMBER_NAME *p = malloc(sizeof(struct DYN_KVP_MEMBER_NAME)); 253 | if (!p) 254 | { 255 | perror("malloc"); 256 | abort(); 257 | } 258 | p->key = key; 259 | p->value = val; 260 | p->next = hash->hash[hash_index]; 261 | hash->nkeys++; 262 | hash->hash[hash_index] = p; 263 | return; 264 | } 265 | 266 | // Copy a hash to a new hash, which contains the same key/values ('tho maybe not in the same order) 267 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _copy)(struct DYN_KVP_TYPE_NAME *hash, size_t new_size) 268 | { 269 | struct DYN_KVP_TYPE_NAME *new_hash = DYN_KVP_F(DYN_KVP_TYPE_NAME, _new)(new_size); 270 | if (hash->nkeys) 271 | for (size_t i = 0; i < hash->size; i++) 272 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[i]; p; p = p->next) 273 | DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(new_hash, p->key, p->value); 274 | return new_hash; 275 | } 276 | 277 | // Merge two hashes in a new hash, which contains the same key/values ('tho 278 | // maybe not in the same order) as the two hashes, with a given "size". 279 | // Values from the second hash override values in the first! 280 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _merge)(struct DYN_KVP_TYPE_NAME *hash1, struct DYN_KVP_TYPE_NAME *hash2, size_t new_size) 281 | { 282 | struct DYN_KVP_TYPE_NAME *new_hash = DYN_KVP_F(DYN_KVP_TYPE_NAME, _new)(new_size); 283 | if (hash1->nkeys) 284 | for (size_t i = 0; i < hash1->size; i++) 285 | for (struct DYN_KVP_MEMBER_NAME *p = hash1->hash[i]; p; p = p->next) 286 | DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(new_hash, p->key, p->value); 287 | if (hash2->nkeys) 288 | for (size_t i = 0; i < hash2->size; i++) 289 | for (struct DYN_KVP_MEMBER_NAME *p = hash2->hash[i]; p; p = p->next) 290 | DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(new_hash, p->key, p->value); 291 | return new_hash; 292 | } 293 | 294 | // Create a new hash containing keys in the first hash which are not present in 295 | // the second hash, with a given "size". 296 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _remove)(struct DYN_KVP_TYPE_NAME *hash1, struct DYN_KVP_TYPE_NAME *hash2, size_t new_size) 297 | { 298 | struct DYN_KVP_TYPE_NAME *new_hash = DYN_KVP_F(DYN_KVP_TYPE_NAME, _new)(new_size); 299 | if (!hash1->nkeys) 300 | return new_hash; 301 | size_t hash2_nkeys = hash2->nkeys; 302 | if (hash1->nkeys) 303 | for (size_t i = 0; i < hash1->size; i++) 304 | for (struct DYN_KVP_MEMBER_NAME *p = hash1->hash[i]; p; p = p->next) 305 | if (hash2_nkeys && !DYN_KVP_F(DYN_KVP_TYPE_NAME, _exists)(hash2, p->key)) 306 | DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(new_hash, p->key, p->value); 307 | return new_hash; 308 | } 309 | 310 | // Create a new hash containing keys in the first hash which are also present 311 | // in the second hash, with values from the first hash and with a given "size". 312 | // If you want values from the second one, swap the parameters! 313 | struct DYN_KVP_TYPE_NAME *DYN_KVP_F(DYN_KVP_TYPE_NAME, _intersect)(struct DYN_KVP_TYPE_NAME *hash1, struct DYN_KVP_TYPE_NAME *hash2, size_t new_size) 314 | { 315 | struct DYN_KVP_TYPE_NAME *new_hash = DYN_KVP_F(DYN_KVP_TYPE_NAME, _new)(new_size); 316 | if (!hash1->nkeys) 317 | return new_hash; 318 | size_t hash2_nkeys = hash2->nkeys; 319 | if (hash1->nkeys) 320 | for (size_t i = 0; i < hash1->size; i++) 321 | for (struct DYN_KVP_MEMBER_NAME *p = hash1->hash[i]; p; p = p->next) 322 | if (hash2_nkeys && DYN_KVP_F(DYN_KVP_TYPE_NAME, _exists)(hash2, p->key)) 323 | DYN_KVP_F(DYN_KVP_TYPE_NAME, _set)(new_hash, p->key, p->value); 324 | return new_hash; 325 | } 326 | 327 | // Does "key" exist in a KVP of this type? 328 | int DYN_KVP_F(DYN_KVP_TYPE_NAME, _exists)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key) 329 | { 330 | if (!hash || !hash->nkeys) 331 | return 0; 332 | unsigned int hash_index = DYN_KVP_HASH_FUNCTION(hash->size, key); 333 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[hash_index]; p; p = p->next) 334 | if (DYN_KVP_COMPARE_FUNCTION(p->key, key)) 335 | return 1; 336 | return 0; 337 | } 338 | 339 | // Delete a "key", if found, from a KVP of this type. 340 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _del)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key) 341 | { 342 | if (!hash || !hash->nkeys) 343 | return; 344 | unsigned int hash_index = DYN_KVP_HASH_FUNCTION(hash->size, key); 345 | struct DYN_KVP_MEMBER_NAME *prev = NULL; 346 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[hash_index]; p; p = p->next) 347 | { 348 | if (DYN_KVP_COMPARE_FUNCTION(p->key, key)) 349 | { 350 | if (prev) 351 | prev->next = p->next; 352 | else 353 | hash->hash[hash_index] = p->next; 354 | free(p); 355 | hash->nkeys--; 356 | return; 357 | } 358 | prev = p; 359 | } 360 | } 361 | 362 | // Get a "key", if found, from a KVP of this type. 363 | // Returns NULL if not found. 364 | DYN_KVP_VALUE_TYPE *DYN_KVP_F(DYN_KVP_TYPE_NAME, _get)(struct DYN_KVP_TYPE_NAME *hash, DYN_KVP_KEY_TYPE key) 365 | { 366 | if (!hash || !hash->nkeys) 367 | return NULL; 368 | unsigned int hash_index = DYN_KVP_HASH_FUNCTION(hash->size, key); 369 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[hash_index]; p; p = p->next) 370 | if (DYN_KVP_COMPARE_FUNCTION(p->key, key)) 371 | return p->value; 372 | return NULL; 373 | } 374 | 375 | #ifdef DYN_KVP_HASH_FUNCTION_DEFINED 376 | # undef DYN_KVP_HASH_FUNCTION_DEFINED 377 | # undef DYN_KVP_HASH_FUNCTION 378 | #endif 379 | 380 | // How many keys are in a KVP of this type? 381 | size_t DYN_KVP_F(DYN_KVP_TYPE_NAME, _nkeys)(struct DYN_KVP_TYPE_NAME *hash) 382 | { 383 | if (!hash) 384 | return 0; 385 | return hash->nkeys; 386 | } 387 | 388 | // Print stats to STDOUT for a KVP of this type. 389 | void DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats_printf)(struct DYN_KVP_TYPE_NAME *hash) 390 | { 391 | if (!hash) 392 | { 393 | printf("No hash passed!\n"); 394 | return; 395 | } 396 | size_t bytes_used = sizeof(struct DYN_KVP_TYPE_NAME) + hash->size * sizeof(void *); 397 | size_t total_bytes_used = 0UL; 398 | // unsigned long nempty = 0UL; 399 | size_t nused = 0UL; 400 | size_t ninchain = 0UL; 401 | size_t ninchainmin = ULONG_MAX; 402 | size_t ninchainmax = 0UL; 403 | int foundMin = 0; 404 | for (size_t i = 0; i < hash->size; i++) 405 | { 406 | if (hash->hash[i] == NULL) 407 | { 408 | // nempty++; 409 | continue; 410 | } 411 | nused++; 412 | unsigned long inchain = 0UL; 413 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[i]; p; p = p->next) 414 | { 415 | inchain++; 416 | bytes_used += sizeof(struct DYN_KVP_MEMBER_NAME); 417 | total_bytes_used += sizeof(DYN_KVP_VALUE_TYPE); 418 | #ifndef DYN_KVP_KEY_TYPE_DEFINED 419 | total_bytes_used += sizeof(DYN_KVP_KEY_TYPE); 420 | #endif 421 | } 422 | ninchain += inchain; 423 | if (inchain > ninchainmax) 424 | ninchainmax = inchain; 425 | if (inchain < ninchainmin) 426 | { 427 | ninchainmin = inchain; 428 | foundMin = 1; 429 | } 430 | } 431 | if (!foundMin) 432 | ninchainmin = ninchainmax; 433 | long double pct_slots_used = nused; 434 | pct_slots_used /= hash->size; 435 | long double avg_chain_len = 0.0L; 436 | if (nused) 437 | { 438 | avg_chain_len = ninchain; 439 | avg_chain_len /= nused; 440 | } 441 | printf("%s(%p)[nkeys=%zu]: %zu[+%zu=%zu] bytes used, %3zu/%3zu (%6.2Lf%%) slots used. Min %zu, Max %zu, Avg %.2Lf.\r\n", 442 | __func__, (void *)hash, hash->nkeys, 443 | bytes_used, total_bytes_used, bytes_used + total_bytes_used, 444 | nused, hash->size, pct_slots_used, 445 | ninchainmin, ninchainmax, avg_chain_len); 446 | } 447 | 448 | // Function to get hash table statistics 449 | struct DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats) DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats)(struct DYN_KVP_TYPE_NAME *hash) 450 | { 451 | struct DYN_KVP_F(DYN_KVP_TYPE_NAME, _stats) stats = {0}; 452 | if (!hash) 453 | return stats; 454 | stats.nkeys = hash->nkeys; 455 | stats.size = hash->size; 456 | size_t ninchain = 0; 457 | for (size_t i = 0; i < hash->size; i++) 458 | { 459 | if (hash->hash[i] == NULL) 460 | { 461 | stats.nempty++; 462 | continue; 463 | } 464 | stats.nused++; 465 | unsigned long inchain = 0; 466 | for (struct DYN_KVP_MEMBER_NAME *p = hash->hash[i]; p; p = p->next) 467 | inchain++; 468 | if (inchain > 1) 469 | stats.ncollisions++; 470 | ninchain += inchain; 471 | if (inchain > stats.ninchainmax) 472 | stats.ninchainmax = inchain; 473 | if (inchain < stats.ninchainmin || !stats.ninchainmin) 474 | stats.ninchainmin = inchain; 475 | } 476 | if (stats.nused) 477 | { 478 | stats.avg_chain_len = ninchain; 479 | stats.avg_chain_len /= stats.nused; 480 | } 481 | stats.load_factor = (double)stats.nused / (double)hash->size; 482 | return stats; 483 | } 484 | 485 | #undef DYN_KVP_MEMBER_NAME 486 | #endif 487 | 488 | #undef DYN_KVP_F 489 | #undef DYN_KVP_ 490 | #undef DYN_KVP__ 491 | #ifdef DYN_KVP_KEY_TYPE_DEFINED 492 | # undef DYN_KVP_KEY_TYPE_DEFINED 493 | # undef DYN_KVP_KEY_TYPE 494 | #endif 495 | #undef DYN_KVP_MEMBER_NAME 496 | -------------------------------------------------------------------------------- /dyn_dllist/dyn_dllist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * `dyn_dllist.h` - C "dynamic" doubly-linked-list header-only library 3 | * Copyright (c) 2024 Marco Fontani - MFONTANI@cpan.org 4 | */ 5 | 6 | #ifndef DYN_DLLIST_VALUE_TYPE 7 | #error "DYN_DLLIST_VALUE_TYPE must be defined before including dyn_dllist.h" 8 | #endif 9 | 10 | #ifndef DYN_DLLIST_TYPE_NAME 11 | #error "DYN_DLLIST_TYPE_NAME must be defined before including dyn_dllist.h" 12 | #endif 13 | 14 | #ifdef DYN_DLLIST_IMPLEMENTATION 15 | # define DYN_DLLIST_EXTERN 16 | #else 17 | # define DYN_DLLIST_EXTERN extern 18 | #endif 19 | 20 | #ifdef DYN_DLLIST__ 21 | #error "dyn_dllist.h uses DYN_DLLIST__ as a macro, but it's already defined" 22 | #endif 23 | 24 | #define DYN_DLLIST__(x,y) x##y 25 | 26 | #ifdef DYN_DLLIST_ 27 | #error "dyn_dllist.h uses DYN_DLLIST_ as a macro, but it's already defined" 28 | #endif 29 | 30 | #define DYN_DLLIST_(x,y) DYN_DLLIST__(x,y) 31 | 32 | #ifdef DYN_DLLIST_F 33 | #error "dyn_dllist.h uses DYN_DLLIST_F as a macro, but it's already defined" 34 | #endif 35 | 36 | #define DYN_DLLIST_F(x, y) DYN_DLLIST_(x, y) 37 | 38 | #ifdef DYN_DLLIST_IMPLEMENTATION 39 | 40 | #ifdef DYN_DLLIST_MEMBER_NAME 41 | # error "dyn_dllist.h uses DYN_DLLIST_MEMBER_NAME as a macro, but it's already defined" 42 | #endif 43 | #define DYN_DLLIST_MEMBER_NAME DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, __member) 44 | struct DYN_DLLIST_MEMBER_NAME { 45 | DYN_DLLIST_VALUE_TYPE *p; 46 | struct DYN_DLLIST_MEMBER_NAME *next; 47 | struct DYN_DLLIST_MEMBER_NAME *prev; 48 | }; 49 | 50 | struct DYN_DLLIST_TYPE_NAME { 51 | struct DYN_DLLIST_MEMBER_NAME *first; 52 | struct DYN_DLLIST_MEMBER_NAME *last; 53 | size_t size; 54 | }; 55 | 56 | #else 57 | 58 | struct DYN_DLLIST_TYPE_NAME; 59 | 60 | #endif 61 | 62 | DYN_DLLIST_EXTERN 63 | struct DYN_DLLIST_TYPE_NAME *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _new)(void); 64 | DYN_DLLIST_EXTERN 65 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _free)(struct DYN_DLLIST_TYPE_NAME *list); 66 | DYN_DLLIST_EXTERN 67 | size_t DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _size)(struct DYN_DLLIST_TYPE_NAME *list); 68 | DYN_DLLIST_EXTERN 69 | int DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 70 | DYN_DLLIST_EXTERN 71 | int DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 72 | DYN_DLLIST_EXTERN 73 | int DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 74 | DYN_DLLIST_EXTERN 75 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 76 | DYN_DLLIST_EXTERN 77 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append1_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 78 | DYN_DLLIST_EXTERN 79 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append1_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 80 | DYN_DLLIST_EXTERN 81 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 82 | DYN_DLLIST_EXTERN 83 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend1_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 84 | DYN_DLLIST_EXTERN 85 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend1_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 86 | DYN_DLLIST_EXTERN 87 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _remove_all)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val); 88 | DYN_DLLIST_EXTERN 89 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _removenth_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, size_t n); 90 | DYN_DLLIST_EXTERN 91 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _removenth_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, size_t n); 92 | DYN_DLLIST_EXTERN 93 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _shift)(struct DYN_DLLIST_TYPE_NAME *list); 94 | DYN_DLLIST_EXTERN 95 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _pop)(struct DYN_DLLIST_TYPE_NAME *list); 96 | DYN_DLLIST_EXTERN 97 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _iterate_ftl)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param); 98 | DYN_DLLIST_EXTERN 99 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _iterate_ltf)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param); 100 | DYN_DLLIST_EXTERN 101 | struct DYN_DLLIST_TYPE_NAME *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _grep)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param); 102 | DYN_DLLIST_EXTERN 103 | size_t DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _countif)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param); 104 | DYN_DLLIST_EXTERN 105 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _nth_ftl)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param, size_t n); 106 | DYN_DLLIST_EXTERN 107 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _nth_ltf)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param, size_t n); 108 | DYN_DLLIST_EXTERN 109 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _first)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param); 110 | DYN_DLLIST_EXTERN 111 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _last)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param); 112 | DYN_DLLIST_EXTERN 113 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _peek_first)(struct DYN_DLLIST_TYPE_NAME *list); 114 | DYN_DLLIST_EXTERN 115 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _peek_last)(struct DYN_DLLIST_TYPE_NAME *list); 116 | 117 | #undef DYN_DLLIST_EXTERN 118 | 119 | #ifdef DYN_DLLIST_IMPLEMENTATION 120 | 121 | // Create a new doubly-linked list of this type. Remember to _free() it! 122 | struct DYN_DLLIST_TYPE_NAME *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _new)(void) 123 | { 124 | struct DYN_DLLIST_TYPE_NAME *list = malloc(sizeof(struct DYN_DLLIST_TYPE_NAME)); 125 | if (!list) 126 | { 127 | perror("_new: malloc"); 128 | exit(EXIT_FAILURE); 129 | } 130 | list->first = NULL; 131 | list->last = NULL; 132 | list->size = 0; 133 | return list; 134 | } 135 | 136 | // Free a doubly-linked list of this type, including inner members but NOT 137 | // including the values in them. That's up to the caller! 138 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _free)(struct DYN_DLLIST_TYPE_NAME *list) 139 | { 140 | if (!list) 141 | return; 142 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 143 | while (cur) 144 | { 145 | struct DYN_DLLIST_MEMBER_NAME *next = cur->next; 146 | free(cur); 147 | cur = next; 148 | } 149 | free(list); 150 | } 151 | 152 | // Returns the number of elements in the list. Doesn't need to iterate it. 153 | size_t DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _size)(struct DYN_DLLIST_TYPE_NAME *list) 154 | { 155 | if (!list) 156 | return 0; 157 | return list->size; 158 | } 159 | 160 | // Returns non-zero if val is in the list. Iterates first to last. 161 | int DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 162 | { 163 | if (!list || !list->size) 164 | return 0; 165 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 166 | while (cur) 167 | { 168 | if (cur->p == val) 169 | return 1; 170 | cur = cur->next; 171 | } 172 | return 0; 173 | } 174 | 175 | // Returns non-zero if val is in the list. Iterates last to first. 176 | int DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 177 | { 178 | if (!list || !list->size) 179 | return 0; 180 | struct DYN_DLLIST_MEMBER_NAME *cur = list->last; 181 | while (cur) 182 | { 183 | if (cur->p == val) 184 | return 1; 185 | cur = cur->prev; 186 | } 187 | return 0; 188 | } 189 | 190 | // Returns non-zero if val is in the list. Iterates first to last. 191 | int DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 192 | { 193 | if (!list || !list->size) 194 | return 0; 195 | return DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ftl)(list, val); 196 | } 197 | 198 | // Appends val at the end of the list. Might already be in the list! 199 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 200 | { 201 | if (!list) 202 | return; 203 | struct DYN_DLLIST_MEMBER_NAME *new = malloc(sizeof(struct DYN_DLLIST_MEMBER_NAME)); 204 | if (!new) 205 | { 206 | perror("_append: malloc"); 207 | exit(EXIT_FAILURE); 208 | } 209 | new->p = val; 210 | new->next = NULL; 211 | new->prev = NULL; 212 | list->size++; 213 | if (!list->first) 214 | { 215 | list->first = new; 216 | list->last = new; 217 | return; 218 | } 219 | list->last->next = new; 220 | new->prev = list->last; 221 | list->last = new; 222 | } 223 | 224 | // Appends val at the end of the list, but only if it's not already in it. 225 | // Uses contains_ftl() to check its presence. 226 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append1_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 227 | { 228 | if (!list) 229 | return; 230 | if (DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ftl)(list, val)) 231 | return; 232 | DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append)(list, val); 233 | } 234 | 235 | // Appends val at the end of the list, but only if it's not already in it. 236 | // Uses contains_ltf() to check its presence. 237 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append1_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 238 | { 239 | if (!list) 240 | return; 241 | if (DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ltf)(list, val)) 242 | return; 243 | DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append)(list, val); 244 | } 245 | 246 | // Prepends val at the start of the list. Might already be in the list! 247 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 248 | { 249 | if (!list) 250 | return; 251 | struct DYN_DLLIST_MEMBER_NAME *new = malloc(sizeof(struct DYN_DLLIST_MEMBER_NAME)); 252 | if (!new) 253 | { 254 | perror("_append: malloc"); 255 | exit(EXIT_FAILURE); 256 | } 257 | new->p = val; 258 | new->next = NULL; 259 | new->prev = NULL; 260 | list->size++; 261 | if (!list->first) 262 | { 263 | list->first = new; 264 | list->last = new; 265 | return; 266 | } 267 | list->first->prev = new; 268 | new->next = list->first; 269 | list->first = new; 270 | } 271 | 272 | // Prepends val at the start of the list, but only if it's not already in it. 273 | // Uses contains_ftl() to check its presence. 274 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend1_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 275 | { 276 | if (!list) 277 | return; 278 | if (DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ftl)(list, val)) 279 | return; 280 | DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend)(list, val); 281 | } 282 | 283 | // Prepends val at the start of the list, but only if it's not already in it. 284 | // Uses contains_ltf() to check its presence. 285 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend1_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 286 | { 287 | if (!list) 288 | return; 289 | if (DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _contains_ltf)(list, val)) 290 | return; 291 | DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _prepend)(list, val); 292 | } 293 | 294 | // Removes all instances of val from the list. 295 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _remove_all)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val) 296 | { 297 | if (!list || !list->size) 298 | return; 299 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 300 | while (cur) 301 | { 302 | struct DYN_DLLIST_MEMBER_NAME *cur_next = cur->next; 303 | if (cur->p == val) 304 | { 305 | if (cur->prev) 306 | cur->prev->next = cur->next; 307 | else 308 | list->first = cur->next; 309 | if (cur->next) 310 | cur->next->prev = cur->prev; 311 | else 312 | list->last = cur->prev; 313 | free(cur); 314 | list->size--; 315 | } 316 | cur = cur_next; 317 | } 318 | } 319 | 320 | // Removes the nth instance of val from the list, iterating it first to last. 321 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _removenth_ftl)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, size_t n) 322 | { 323 | if (!list || !list->size || n >= list->size) 324 | return; 325 | size_t count = 0; 326 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 327 | while (cur) 328 | { 329 | if (cur->p == val) 330 | { 331 | if (count == n) 332 | { 333 | if (cur->prev) 334 | cur->prev->next = cur->next; 335 | else 336 | list->first = cur->next; 337 | if (cur->next) 338 | cur->next->prev = cur->prev; 339 | else 340 | list->last = cur->prev; 341 | free(cur); 342 | list->size--; 343 | return; 344 | } 345 | count++; 346 | } 347 | cur = cur->next; 348 | } 349 | } 350 | 351 | // Removes the nth instance of val from the list, iterating it last to first 352 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _removenth_ltf)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, size_t n) 353 | { 354 | if (!list || !list->size || n >= list->size) 355 | return; 356 | size_t count = 0; 357 | struct DYN_DLLIST_MEMBER_NAME *cur = list->last; 358 | while (cur) 359 | { 360 | if (cur->p == val) 361 | { 362 | if (count == n) 363 | { 364 | if (cur->prev) 365 | cur->prev->next = cur->next; 366 | else 367 | list->first = cur->next; 368 | if (cur->next) 369 | cur->next->prev = cur->prev; 370 | else 371 | list->last = cur->prev; 372 | free(cur); 373 | list->size--; 374 | return; 375 | } 376 | count++; 377 | } 378 | cur = cur->prev; 379 | } 380 | } 381 | 382 | // Removes, returning it, the FIRST element of the list. 383 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _shift)(struct DYN_DLLIST_TYPE_NAME *list) 384 | { 385 | if (!list || !list->size) 386 | return NULL; 387 | struct DYN_DLLIST_MEMBER_NAME *ret = list->first; 388 | if (list->first) 389 | { 390 | list->first = list->first->next; 391 | if (list->first) 392 | list->first->prev = NULL; 393 | else 394 | list->last = NULL; 395 | } 396 | if (ret) 397 | { 398 | DYN_DLLIST_VALUE_TYPE *val = ret->p; 399 | free(ret); 400 | list->size--; 401 | return val; 402 | } 403 | return NULL; 404 | } 405 | 406 | // Removes, returning it, the LAST element of the list. 407 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _pop)(struct DYN_DLLIST_TYPE_NAME *list) 408 | { 409 | if (!list || !list->size) 410 | return NULL; 411 | struct DYN_DLLIST_MEMBER_NAME *ret = list->last; 412 | if (list->last) 413 | { 414 | list->last = list->last->prev; 415 | if (list->last) 416 | list->last->next = NULL; 417 | else 418 | list->first = NULL; 419 | } 420 | if (ret) 421 | { 422 | DYN_DLLIST_VALUE_TYPE *val = ret->p; 423 | free(ret); 424 | list->size--; 425 | return val; 426 | } 427 | return NULL; 428 | } 429 | 430 | // Iterate the list from first to last, calling func(list, val, param) for each val in the list. 431 | // It's SAFE to remove "val" from "list". 432 | // If func returns non-zero, the iteration is stopped. 433 | // "param" is passed to func as-is. 434 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _iterate_ftl)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param) 435 | { 436 | if (!list || !func || !list->size) 437 | return; 438 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 439 | while (cur) 440 | { 441 | struct DYN_DLLIST_MEMBER_NAME *cur_next = cur->next; 442 | if (func(list, cur->p, param)) 443 | return; 444 | cur = cur_next; 445 | } 446 | } 447 | 448 | // Iterate the list from last to first, calling func(list, val, param) for each val in the list. 449 | // It's SAFE to remove "val" from "list". 450 | // If func returns non-zero, the iteration is stopped. 451 | // "param" is passed to func as-is. 452 | void DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _iterate_ltf)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param) 453 | { 454 | if (!list || !func || !list->size) 455 | return; 456 | struct DYN_DLLIST_MEMBER_NAME *cur = list->last; 457 | while (cur) 458 | { 459 | struct DYN_DLLIST_MEMBER_NAME *cur_prev = cur->prev; 460 | if (func(list, cur->p, param)) 461 | return; 462 | cur = cur_prev; 463 | } 464 | } 465 | 466 | 467 | // Returns a new list containing all the elements of "list" for which the 468 | // function "func" returns non-zero. 469 | // "param" is passed to func as-is. 470 | struct DYN_DLLIST_TYPE_NAME *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _grep)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param) 471 | { 472 | struct DYN_DLLIST_TYPE_NAME *new_list = DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _new)(); 473 | if (!list || !func || !list->size) 474 | return new_list; 475 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 476 | while (cur) 477 | { 478 | struct DYN_DLLIST_MEMBER_NAME *cur_next = cur->next; 479 | DYN_DLLIST_VALUE_TYPE *elem = cur->p; 480 | if (func(list, elem, param)) 481 | DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _append)(new_list, elem); 482 | cur = cur_next; 483 | } 484 | return new_list; 485 | } 486 | 487 | // Returns the number of elements of "list" for which the function "func" 488 | // returns non-zero. 489 | // "param" is passed to func as-is. 490 | size_t DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _countif)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param) 491 | { 492 | size_t count = 0; 493 | if (!list || !func || !list->size) 494 | return count; 495 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 496 | while (cur) 497 | { 498 | struct DYN_DLLIST_MEMBER_NAME *cur_next = cur->next; 499 | if (func(list, cur->p, param)) 500 | count++; 501 | cur = cur_next; 502 | } 503 | return count; 504 | } 505 | 506 | // Returns the "n"th element of "list", iterated first to last, for which the 507 | // function "func" returns non-zero. 508 | // "param" is passed to func as-is. 509 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _nth_ftl)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param, size_t n) 510 | { 511 | if (!list || !func || !list->size || n >= list->size) 512 | return NULL; 513 | struct DYN_DLLIST_MEMBER_NAME *cur = list->first; 514 | size_t count = 0; 515 | while (cur) 516 | { 517 | struct DYN_DLLIST_MEMBER_NAME *cur_next = cur->next; 518 | DYN_DLLIST_VALUE_TYPE *elem = cur->p; 519 | if (func(list, elem, param)) 520 | { 521 | if (count == n) 522 | return elem; 523 | count++; 524 | } 525 | cur = cur_next; 526 | } 527 | return NULL; 528 | } 529 | 530 | // Returns the "n"th element of "list", iterated last to first, for which the 531 | // function "func" returns non-zero. 532 | // "param" is passed to func as-is. 533 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _nth_ltf)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param, size_t n) 534 | { 535 | if (!list || !func || !list->size || n >= list->size) 536 | return NULL; 537 | struct DYN_DLLIST_MEMBER_NAME *cur = list->last; 538 | size_t count = 0; 539 | while (cur) 540 | { 541 | struct DYN_DLLIST_MEMBER_NAME *cur_prev = cur->prev; 542 | DYN_DLLIST_VALUE_TYPE *elem = cur->p; 543 | if (func(list, elem, param)) 544 | { 545 | if (count == n) 546 | return elem; 547 | count++; 548 | } 549 | cur = cur_prev; 550 | } 551 | return NULL; 552 | } 553 | 554 | // Returns the first element of "list" (iterated first to last) for which the 555 | // function "func" returns non-zero. 556 | // "param" is passed to func as-is. 557 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _first)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param) 558 | { 559 | if (!list || !func || !list->size) 560 | return NULL; 561 | return DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _nth_ftl)(list, func, param, 0); 562 | } 563 | 564 | // Returns the first element of "list" (iterated last to first) for which the 565 | // function "func" returns non-zero. 566 | // "param" is passed to func as-is. 567 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _last)(struct DYN_DLLIST_TYPE_NAME *list, int (*func)(struct DYN_DLLIST_TYPE_NAME *list, DYN_DLLIST_VALUE_TYPE *val, void *param), void *param) 568 | { 569 | if (!list || !func || !list->size) 570 | return NULL; 571 | return DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _nth_ltf)(list, func, param, 0); 572 | } 573 | 574 | // Returns the "first" element of "list". 575 | // Does not remove it from the list. 576 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _peek_first)(struct DYN_DLLIST_TYPE_NAME *list) 577 | { 578 | if (!list || !list->size || !list->first) 579 | return NULL; 580 | return list->first->p; 581 | } 582 | 583 | // Returns the "last" element of "list". 584 | // Does not remove it from the list. 585 | DYN_DLLIST_VALUE_TYPE *DYN_DLLIST_F(DYN_DLLIST_TYPE_NAME, _peek_last)(struct DYN_DLLIST_TYPE_NAME *list) 586 | { 587 | if (!list || !list->size || !list->last) 588 | return NULL; 589 | return list->last->p; 590 | } 591 | 592 | #undef DYN_DLLIST_MEMBER_NAME 593 | #endif 594 | 595 | #undef DYN_DLLIST_F 596 | #undef DYN_DLLIST_ 597 | #undef DYN_DLLIST__ 598 | --------------------------------------------------------------------------------