diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..26dba3ad --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +RM=rm -f + +all: + @echo "Nothing to be done." + +check: + cd tests && make check + cd example && make examples + +doc: README.md + markdown < README.md > README.html + +clean: + cd tests && make clean + cd example && make clean + $(RM) -f README.html + + diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 00000000..b8b02c8c --- /dev/null +++ b/example/Makefile @@ -0,0 +1,20 @@ +CC=gcc -std=c99 +CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function +CPPFLAGS=-I.. +RM=rm -f +EXE=./a.exe +EXAMPLES=ex01b.c ex01.c ex02.c ex03.c ex04.c ex05.c ex06.c ex07.c + +all: + @echo "Nothing to be done." + +check: examples + +examples: + for f in $(EXAMPLES) ; do $(CC) $(CFLAGS) $(CPPFLAGS) -g $$f -o $(EXE) -pthread ; done + +clean: + $(RM) $(EXE) *.s *~ *.o ./a.dat + $(RM) -r coverage *.gcda *.gcno all.info test.tar README.html + + diff --git a/example/ex01.c b/example/ex01.c new file mode 100644 index 00000000..4e0d6110 --- /dev/null +++ b/example/ex01.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "m-array.h" + +ARRAY_DEF (charp, char *) + +int main(void) +{ + // Create an array list + array_charp_t al; + array_charp_init(al); + + // add elements to the array list + array_charp_push_back (al,"C"); + array_charp_push_back (al,"A"); + array_charp_push_back (al,"E"); + array_charp_push_back (al,"B"); + array_charp_push_back (al,"D"); + array_charp_push_back (al,"F"); + + // Use iterator to display contents of al + printf("Original contents of al: "); + array_it_charp_t itr; + for (array_charp_it(itr, al) ; !array_charp_end_p (itr); array_charp_next(itr)) { + char *element = *array_charp_ref(itr); + printf ("%s ", element); + } + printf("\n"); + + // Now, display the list backwards + printf("List backwards: "); + for (array_charp_previous (itr) ; !array_charp_end_p (itr); array_charp_previous(itr)) { + char *element = *array_charp_ref(itr); + printf ("%s ", element); + } + printf("\n"); + + array_charp_clear(al); + return 0; +} diff --git a/example/ex01b.c b/example/ex01b.c new file mode 100644 index 00000000..7e332afa --- /dev/null +++ b/example/ex01b.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "m-array.h" + +ARRAY_DEF (charp, char *) + +int main(void) +{ + // Create an array list + M_LET (al, ARRAY_OPLIST(charp)) { + // add elements to the array list + array_charp_push_back (al,"C"); + array_charp_push_back (al,"A"); + array_charp_push_back (al,"E"); + array_charp_push_back (al,"B"); + array_charp_push_back (al,"D"); + array_charp_push_back (al,"F"); + + // Display contents of al + printf("Original contents of al: "); + for M_EACH(element, al, ARRAY_OPLIST(charp)) { + printf ("%s ", *element); + } + printf("\n"); + } + return 0; +} diff --git a/example/ex02.c b/example/ex02.c new file mode 100644 index 00000000..074181a6 --- /dev/null +++ b/example/ex02.c @@ -0,0 +1,27 @@ +#include +#include "m-array.h" + +ARRAY_DEF(int, int) + +const int n = 100000; + +int main(void) +{ + array_int_t x; + array_int_init(x); + + for (int i = 0; i < n; i++) { + array_int_push_back(x, rand()); + } + + for (int i = 0; i < n; i++) { + array_int_push_at (x, rand() % n, rand()); + } + + for (int i = 0; i < n; i++) { + array_int_pop_at (NULL, x, rand() % n); + } + + array_int_clear(x); + return 0; +} diff --git a/example/ex03.c b/example/ex03.c new file mode 100644 index 00000000..ce51a0f6 --- /dev/null +++ b/example/ex03.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include "m-dict.h" +#include "m-string.h" + +DICT_DEF2(str, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST) + +int main(void) +{ + dict_str_t h; + int max = 1; + string_t key; + + dict_str_init(h); + string_init (key); + while (!feof(stdin)) + { + string_fgets (key, stdin, STRING_READ_PURE_LINE); + int *v = dict_str_get (h, key); + if (v != NULL) { + (*v)++; + max = M_MAX(*v, max); + } else { + dict_str_set_at(h, key, 1); + } + } + string_clear(key); + dict_str_clear(h); + return 0; +} diff --git a/example/ex04.c b/example/ex04.c new file mode 100644 index 00000000..545b219d --- /dev/null +++ b/example/ex04.c @@ -0,0 +1,164 @@ +#include "m-buffer.h" +#include "m-shared.h" +#include "m-mutex.h" + +/**************************************************************/ + +/* Define a random big data structure */ + +#define BIGDATA_SIZE 100000 +#define BIGDATA_NUM 10 + +typedef struct bigdata_s { + char bigdata[BIGDATA_SIZE]; + char *ptr; +} bigdata_t[1]; + +static void bigdata_init(bigdata_t d) +{ + memset(d->bigdata, 0, BIGDATA_SIZE); + d->ptr = malloc (BIGDATA_SIZE * 10); +} + +static void bigdata_clear(bigdata_t d) +{ + free(d->ptr); +} + +static bigdata_t *bigdata_new (void) +{ + bigdata_t *n = malloc (sizeof (bigdata_t)); + bigdata_init(*n); + return n; +} + +static void bigdata_free(bigdata_t *d) +{ + bigdata_clear(*d); + free(d); +} + +/**************************************************************/ + +/* Perform some works on this big data */ + +static void perform_acquisition(bigdata_t *p) +{ + // TODO +} +static void perform_computation2(bigdata_t *p) +{ + // TODO +} +static void perform_computation3(bigdata_t *p) +{ + // TODO +} +static void perform_computation4(bigdata_t *p) +{ + // TODO +} + +/**************************************************************/ + +/* Let's do the thread scheduling */ + +#define MY_QUEUE_SIZE 10 +SHARED_PTR_DEF(bigdata, bigdata_t, (CLEAR(bigdata_clear), FREE(free))) +BUFFER_DEF(bigdata, shared_bigdata_t, MY_QUEUE_SIZE, + BUFFER_QUEUE|BUFFER_PUSH_INIT_POP_MOVE, SHARED_PTR_OPLIST(bigdata)) + +/* Thread synchros: + * Thread1 ---> Thread 2 + * +-> Thread 3 --> Thread 4 + */ +buffer_bigdata_t buf_t1tot2; +buffer_bigdata_t buf_t1tot3; +buffer_bigdata_t buf_t3tot4; +bool continue_threading; +M_THREAD_T t1, t2, t3, t4; + +static void initbuf(void) +{ + buffer_bigdata_init(buf_t1tot2, MY_QUEUE_SIZE); + buffer_bigdata_init(buf_t1tot3, MY_QUEUE_SIZE); + buffer_bigdata_init(buf_t3tot4, MY_QUEUE_SIZE); +} + +static void clearbuf(void) +{ + buffer_bigdata_clear(buf_t1tot2); + buffer_bigdata_clear(buf_t1tot3); + buffer_bigdata_clear(buf_t3tot4); +} + +static void thread1 (void *arg) +{ + while (continue_threading) { + shared_bigdata_t ptr; + shared_bigdata_init2 (ptr, bigdata_new()); + perform_acquisition(shared_bigdata_ref(ptr)); + buffer_bigdata_push(buf_t1tot2, ptr); + buffer_bigdata_push(buf_t1tot3, ptr); + shared_bigdata_clear(ptr); + } +} +static void thread2 (void *arg) +{ + while (continue_threading) { + shared_bigdata_t ptr; + shared_bigdata_init (ptr); + buffer_bigdata_pop(&ptr, buf_t1tot2); + perform_computation2(shared_bigdata_ref(ptr)); + shared_bigdata_clear(ptr); + } +} +static void thread3 (void *arg) +{ + while (continue_threading) { + shared_bigdata_t ptr; + shared_bigdata_init (ptr); + buffer_bigdata_pop(&ptr, buf_t1tot3); + perform_computation3(shared_bigdata_ref(ptr)); + buffer_bigdata_push(buf_t3tot4, ptr); + shared_bigdata_clear(ptr); + } +} +static void thread4 (void *arg) +{ + while (continue_threading) { + shared_bigdata_t ptr; + shared_bigdata_init (ptr); + buffer_bigdata_pop(&ptr, buf_t3tot4); + perform_computation4(shared_bigdata_ref(ptr)); + shared_bigdata_clear(ptr); + } +} + + +static void init(void) +{ + initbuf(); + continue_threading = true; + M_THREAD_CREATE(t1, thread1, NULL); + M_THREAD_CREATE(t2, thread2, NULL); + M_THREAD_CREATE(t3, thread3, NULL); + M_THREAD_CREATE(t3, thread4, NULL); +} + +static void clear(void) +{ + continue_threading = false; + M_THREAD_JOIN(t1); + M_THREAD_JOIN(t2); + M_THREAD_JOIN(t3); + M_THREAD_JOIN(t4); + clearbuf(); +} + +int main(void) +{ + init(); + // + clear(); +} diff --git a/example/ex05.c b/example/ex05.c new file mode 100644 index 00000000..8a74f908 --- /dev/null +++ b/example/ex05.c @@ -0,0 +1,120 @@ +/* Based on: + https://github.com/kostya/benchmarks/blob/master/brainfuck/brainfuck.cpp */ +#include +#include "m-array.h" +#include "m-string.h" + +ARRAY_DEF(int, int) + +typedef struct { + int pos; + array_int_t tape; +} tape_t[1]; + +static void tape_init(tape_t tape) { + tape->pos =0; + array_int_init(tape->tape); + array_int_push_back(tape->tape, 0); +} +static void tape_clear(tape_t tape) { array_int_clear(tape->tape); } +static int tape_get(tape_t tape) { return *array_int_cget(tape->tape, tape->pos); } +static void tape_inc(tape_t tape) { (*array_int_get(tape->tape, tape->pos)) ++; } +static void tape_dec(tape_t tape) { (*array_int_get(tape->tape, tape->pos)) --; } +static void tape_advance(tape_t tape) { tape->pos++; if (array_int_size(tape->tape) <= (unsigned int) tape->pos) array_int_push_back(tape->tape, 0); } +static void tape_devance(tape_t tape) { if (tape->pos > 0) tape->pos--; } + +typedef struct { + string_t code; + array_int_t bracket_map; // Should use map when possible. +} program_t[1]; + +static void program_init(program_t p, string_t text) { + string_init (p->code); + array_int_init (p->bracket_map); + array_int_t leftstack; + string_t symbols; + array_int_init(leftstack); + string_init_set_str(symbols, "[]<>+-,."); + int pc = 0; + for (size_t i = 0; i < string_get_length(text); i++) { + char c = string_get_char(text, i); + + if (string_search_char(symbols, c) == STRING_FAILURE) continue; + + if (c == '[') array_int_push_back (leftstack, pc); + else + if (c == ']' && array_int_size (leftstack) != 0) { + int left; + array_int_pop_back(&left, leftstack); + int right = pc; + array_int_set_at2 (p->bracket_map, left, right); + array_int_set_at2 (p->bracket_map, right, left); + } + + pc++; + string_push_back(p->code, c); + } + array_int_clear(leftstack); + string_clear(symbols); +} +static void program_clear(program_t p) { + string_clear(p->code); + array_int_clear(p->bracket_map); +} +static void program_run(program_t p){ + tape_t tape; + tape_init(tape); + for(size_t pc = 0; pc < string_get_length(p->code); pc++) { + switch (string_get_char(p->code, pc)) { + case '+': + tape_inc(tape); + break; + case '-': + tape_dec(tape); + break; + case '>': + tape_advance(tape); + break; + case '<': + tape_devance(tape); + break; + case '[': + if (tape_get(tape) == 0) pc = *array_int_cget(p->bracket_map, pc); + break; + case ']': + if (tape_get(tape) != 0) pc = *array_int_cget(p->bracket_map, pc); + break; + case '.': + printf("%c", tape_get(tape)); + fflush(stdout); + break; + default: + abort(); + break; + } + } + tape_clear(tape); +} + +static void read_file(string_t str, const string_t filename) { + FILE *f = fopen(string_get_cstr(filename), "rt"); + if (!f) abort(); + string_fgets (str, f, STRING_READ_FILE); + fclose(f); +} + +int main(int argc, const char*argv[]) +{ + string_t filename, text; + assert (argc >= 2); + string_init_set_str(filename, argv[1]); + string_init(text); + read_file(text, filename); + program_t p; + program_init(p, text); + program_run(p); + program_clear(p); + string_clear(text); + string_clear(filename); + return 0; +} diff --git a/example/ex06.c b/example/ex06.c new file mode 100644 index 00000000..023d7e85 --- /dev/null +++ b/example/ex06.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +#include "m-list.h" +#include "m-string.h" +#include "m-algo.h" + +LIST_DEF(string, string_t, STRING_OPLIST) +#define ASTR_OPLIST LIST_OPLIST(string, STRING_OPLIST) +ALGO_DEF(astring, ASTR_OPLIST) + +int main(void) +{ + // Let's create a variable named 'grow' which is a list of string_t + M_LET (grow, ASTR_OPLIST) + M_LET (tmpstr, STRING_OPLIST) { // And let's create a string_t + + // add string elements to the array + list_string_push_back (grow, STRING_CTE("AB")); + string_printf(tmpstr, "%d", 12); + list_string_push_back (grow, tmpstr); + list_string_push_back (grow, STRING_CTE("CD")); + + // get the chunk as a big string + list_string_get_str (tmpstr, grow, false); + + // print out + printf("Number of elements = %lu\n", list_string_size(grow)); + printf("Final string = %s\n", string_get_cstr(tmpstr)); + + // Another way to print the list + for M_EACH (item, grow, ASTR_OPLIST) { + printf ("Item = "); + string_fputs (stdout, *item); + printf ("\n"); + } + + // Get the min and the max of the list + string_t *p = astring_min(grow); + printf ("Min string is %s\n", string_get_cstr(*p)); + p = astring_max(grow); + printf ("Max string is %s\n", string_get_cstr(*p)); + + // We can even sort the list! + astring_sort(grow); + for M_EACH (item, grow, ASTR_OPLIST) { + printf ("Sort Item = %s\n", string_get_cstr(*item) ); + } + + // Split a string into a list. + string_set_str(tmpstr, "HELLO;JOHN;HOW;ARE;YOU"); + astring_split(grow, tmpstr, ';'); + for M_EACH (item, grow, ASTR_OPLIST) { + printf ("Split Item = %s\n", string_get_cstr(*item) ); + } + + } // Everything is cleared after this point. + + return 0; +} diff --git a/example/ex07.c b/example/ex07.c new file mode 100644 index 00000000..9d77e765 --- /dev/null +++ b/example/ex07.c @@ -0,0 +1,44 @@ +#if 1 // Non macro version. + +#include "m-dict.h" +DICT_DEF2(m32, unsigned int, M_DEFAULT_OPLIST, char, M_DEFAULT_OPLIST) + +int main(void) { + dict_m32_t h; + dict_m32_init(h); // h is init. + dict_m32_set_at (h, 5, 10); // h[5] = 10 + char *k = dict_m32_get(h, 10); // k == NULL + int is_missing = (k != NULL); // true + assert (is_missing); + dict_m32_remove(h, 5); // h is now empty + dict_it_m32_t it; // iterate over all dictionnary + for (dict_m32_it (it, h) ; !dict_m32_end_p (it); dict_m32_next(it)) { + dict_pair_m32_t *item = dict_m32_ref(it); + (*item)->value = 1; // Set its value to 1 + } + dict_m32_clear(h); // h is cleared + return 0; +} + +#else // Use of M_LET & M_FOR macros (otherwise equivalent) + +#include "m-dict.h" +DICT_DEF2(m32, unsigned int, M_DEFAULT_OPLIST, char, M_DEFAULT_OPLIST) +#define M32_OPLIST DICT_OPLIST(m32, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) +int main(void) { + M_LET(h, M32_OPLIST) { // h is init + dict_m32_set_at (h, 5, 10); // h[5] = 10 + char *k = dict_m32_get(h, 10); // k == NULL + int is_missing = (k != NULL); // true + assert (is_missing); + dict_m32_remove(h, 5); // h is now empty + for M_EACH(item, h, M32_OPLIST) { // traverse each item + (*item)->value = 1; // Set its value to 1 + } + } // h is cleared + return 0; +} + +#endif + + diff --git a/m-algo.h b/m-algo.h new file mode 100644 index 00000000..1e6e7c5e --- /dev/null +++ b/m-algo.h @@ -0,0 +1,217 @@ +/* + * MLIB - ALGO module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_ALGO_H +#define __M_ALGO_H + +#include +#include +#include +#include + +#include "m-core.h" + +#define ALGO_DEF(name, cont_oplist) \ + ALGOI_DEF(name, M_GET_TYPE cont_oplist, cont_oplist, \ + M_GET_SUBTYPE cont_oplist, M_GET_OPLIST cont_oplist, M_GET_IT_TYPE cont_oplist) + +#define ALGO_MAP(container, cont_oplist, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (ALGOI_MAP(container, cont_oplist, __VA_ARGS__), \ + ALGOI_MAP_ARG(name, cont_oplist, __VA_ARGS__ )) + +/********************************** INTERNAL ************************************/ + +#define ALGOI_DEF(name, container_t, cont_oplist, type_t, type_oplist, it_t) \ + \ + static inline void CAT(name, _find) (it_t it, container_t l, const type_t data) \ + { \ + for (M_GET_IT_FIRST cont_oplist (it, l); \ + !M_GET_IT_END_P cont_oplist (it) ; \ + M_GET_IT_NEXT cont_oplist (it)) { \ + if (M_GET_EQUAL type_oplist (*M_GET_IT_CREF cont_oplist (it), data)) \ + return ; \ + } \ + } \ + \ + static inline bool CAT(name, _contains) (container_t l, const type_t data) \ + { \ + it_t it; \ + CAT(name,_find)(it, l, data); \ + return !M_GET_IT_END_P cont_oplist (it); \ + } \ + \ + static inline size_t CAT(name, _count) (container_t l, const type_t data) \ + { \ + it_t it; \ + size_t count = 0; \ + for (M_GET_IT_FIRST cont_oplist (it, l); \ + !M_GET_IT_END_P cont_oplist (it) ; \ + M_GET_IT_NEXT cont_oplist (it)) { \ + if (M_GET_EQUAL type_oplist (*M_GET_IT_CREF cont_oplist (it), data)) \ + count++ ; \ + } \ + return count; \ + } \ + \ + static inline void CAT(name, _map) (container_t l, \ + void (*f)(type_t const) ) \ + { \ + for M_EACH(item, l, cont_oplist) { \ + f(*item); \ + } \ + } \ + \ + M_IF_METHOD(CMP, type_oplist)( \ + static inline type_t *CAT(name, _min) (/*const*/ container_t l) \ + { \ + type_t *min = NULL; \ + for M_EACH(cref, l, cont_oplist) { \ + if (min == NULL || \ + M_GET_CMP type_oplist (*min, *cref) > 0) \ + min = cref; \ + } \ + return min; \ + } \ + \ + static inline type_t *CAT(name, _max) (/*const*/ container_t l) \ + { \ + type_t *max = NULL; \ + for M_EACH(cref, l, cont_oplist) { \ + if (max == NULL || \ + M_GET_CMP type_oplist (*max, *cref) < 0) \ + max = cref; \ + } \ + return max; \ + } \ + \ + static inline void CAT(name, _minmax) (type_t **min_p, type_t **max_p, \ + /*const*/ container_t l) \ + { \ + type_t *min = NULL; \ + type_t *max = NULL; \ + for M_EACH(cref, l, cont_oplist) { \ + if (min == NULL || \ + M_GET_CMP type_oplist (*min, *cref) > 0) \ + min = cref; \ + if (max == NULL || \ + M_GET_CMP type_oplist (*max, *cref) < 0) \ + max = cref; \ + } \ + *min_p = min; \ + *max_p = max; \ + } \ + \ + /* Sort can be generated from 3 algorithms: */ \ + /* - a specialized version defined by the container */ \ + /* - an insertion sort (need 'previous' method) */ \ + /* - a selection sort */ \ + M_IF_METHOD(SORT, cont_oplist)( \ + /* optimized sort for container */ \ + static inline int CAT(name,_sort_cmp)(const type_t*a,const type_t*b) {\ + return M_GET_CMP type_oplist(*a, *b); \ + } \ + static inline void CAT(name, _sort)(container_t l) \ + { \ + M_GET_SORT cont_oplist(l, CAT(name, _sort_cmp)); \ + } \ + , \ + M_IF_METHOD(IT_PREVIOUS, cont_oplist)( \ + /* generic insertion sort */ \ + static inline void CAT(name, _sort)(container_t l) \ + { \ + it_t it1; \ + it_t it2; \ + it_t it21; \ + for(M_GET_IT_FIRST cont_oplist (it1, l); \ + !M_GET_IT_LAST_P cont_oplist (it1); \ + M_GET_IT_NEXT cont_oplist (it1)) { \ + type_t x; /* Do not use SET, as it is a MOVE operation */ \ + memcpy (&x, M_GET_IT_CREF cont_oplist (it1), sizeof (type_t)); \ + M_GET_IT_SET cont_oplist (it2, it1); \ + M_GET_IT_SET cont_oplist (it21, it1); \ + M_GET_IT_PREVIOUS cont_oplist (it2); \ + while (!M_GET_IT_END_P cont_oplist (it2) \ + && M_GET_CMP type_oplist (*M_GET_IT_CREF cont_oplist (it2), \ + x) > 0) { \ + memcpy(M_GET_IT_REF cont_oplist (it21), \ + M_GET_IT_CREF cont_oplist (it2), sizeof (type_t) ); \ + M_GET_IT_SET cont_oplist (it21, it2); \ + M_GET_IT_PREVIOUS cont_oplist (it2); \ + } \ + memcpy(M_GET_IT_REF cont_oplist (it21), &x, sizeof (type_t) ); \ + } \ + } \ + , \ + /* generic selection sort */ \ + static inline void CAT(name, _sort)(container_t l) \ + { \ + it_t it1; \ + it_t it2; \ + for(M_GET_IT_FIRST cont_oplist (it1, l); \ + !M_GET_IT_LAST_P cont_oplist (it1); \ + M_GET_IT_NEXT cont_oplist (it1)) { \ + it_t it_min; \ + M_GET_IT_SET cont_oplist (it_min, it1); \ + M_GET_IT_SET cont_oplist (it2, it1); \ + for(M_GET_IT_NEXT cont_oplist (it2) ; \ + !M_GET_IT_END_P cont_oplist (it2); \ + M_GET_IT_NEXT cont_oplist (it2)) { \ + if (M_GET_CMP type_oplist (*M_GET_IT_CREF cont_oplist (it2), \ + *M_GET_IT_CREF cont_oplist (it_min)) < 0) { \ + M_GET_IT_SET cont_oplist (it_min, it2); \ + } \ + } \ + if (M_GET_IT_EQUAL_P cont_oplist (it_min, it1) == false) { \ + type_t x; /* Do not use SET, as it is a MOVE operation */ \ + memcpy (&x, M_GET_IT_CREF cont_oplist (it1), sizeof (type_t)); \ + memcpy (M_GET_IT_REF cont_oplist (it1), \ + M_GET_IT_CREF cont_oplist (it_min), sizeof (type_t)); \ + memcpy (M_GET_IT_REF cont_oplist (it_min), &x, sizeof (type_t)); \ + } \ + } \ + } \ + ) /* IF IT_PREVIOUS METHOD */ \ + ) /* IF SORT METHOD */ \ + ,) /* IF CMP METHOD */ \ + \ + M_IF_METHOD(EXT_ALGO, type_oplist)( \ + M_GET_EXT_ALGO type_oplist (name, cont_oplist, type_oplist) \ + , ) \ + +//TODO: const_iterator & CM_EACH missing... +//TODO: Algorithm missing +// nth_element, ... + +#define ALGOI_MAP(container, cont_oplist, func) \ + for M_EACH(item, l, cont_oplist) { \ + func(*item); \ + } \ + +#define ALGOI_MAP_ARG(container, cont_oplist, func, ...) \ + for M_EACH(item, l, cont_oplist) { \ + func(__VA_ARGS, *item); \ + } \ + +#endif diff --git a/m-array.h b/m-array.h new file mode 100644 index 00000000..ee755c7a --- /dev/null +++ b/m-array.h @@ -0,0 +1,639 @@ +/* + * MLIB - ARRAY module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_ARRAY_H +#define __M_ARRAY_H + +#include +#include +#include +#include + +#include "m-core.h" + +/* Define an array of a given type. + USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */ +#define ARRAY_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (ARRAYI_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST ), \ + ARRAYI_DEF2(name, __VA_ARGS__ )) + +/* Define the oplist of an array of type. + USAGE: ARRAY_OPLIST(name[, oplist of the type]) */ +#define ARRAY_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (ARRAYI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \ + ARRAYI_OPLIST(__VA_ARGS__ )) + + +/********************************** INTERNAL ************************************/ + +#define ARRAYI_OPLIST(name, oplist) \ + (INIT(CAT3(array_, name, _init)) \ + ,INIT_SET(CAT3(array_, name, _init_set)) \ + ,SET(CAT3(array_, name, _set)) \ + ,CLEAR(CAT3(array_, name, _clear)) \ + ,INIT_MOVE(CAT3(array_, name, _init_move)) \ + ,MOVE(CAT3(array_, name, _move)) \ + ,TYPE(CAT3(array_,name,_t)) \ + ,SUBTYPE(CAT3(array_type_,name,_t)) \ + ,IT_TYPE(CAT3(array_it_,name,_t)) \ + ,IT_FIRST(CAT3(array_,name,_it)) \ + ,IT_SET(CAT3(array_,name,_it_set)) \ + ,IT_END_P(CAT3(array_,name,_end_p)) \ + ,IT_LAST_P(CAT3(array_,name,_last_p)) \ + ,IT_EQUAL_P(CAT3(array_,name,_it_equal_p)) \ + ,IT_NEXT(CAT3(array_,name,_next)) \ + ,IT_PREVIOUS(CAT3(array_,name,_previous)) \ + ,IT_REF(CAT3(array_,name,_ref)) \ + ,IT_CREF(CAT3(array_,name,_cref)) \ + ,CLEAN(CAT3(array_,name,_clean)) \ + ,PUSH(CAT3(array_,name,_push_back)) \ + ,POP(CAT3(array_,name,_pop_back)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(CMP, oplist)(SORT(CAT3(array_, name, _sort)),) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(CAT3(array_, name, _get_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(CAT3(array_, name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(CAT3(array_, name, _in_str)),) \ + ,M_IF_METHOD(EQUAL, oplist)(EQUAL(CAT3(array_, type, _equal_p)),) \ + ) + +// Compute alloc from size. +#define ARRAYI_INC_ALLOC_SIZE(n) ((n) < 8 ? 16 : (n) * 2) + +// Internal +#define ARRAYI_DEF2(name, type, oplist) \ + typedef struct CAT3(array_, name, _s) { \ + size_t size, alloc; \ + type *ptr; \ + } CAT3(array_, name,_t)[1]; \ + \ + typedef union { \ + type *ptr; \ + const type *cptr; \ + } CAT3(array_union_, name,_t); \ + \ + typedef type CAT3(array_type_,name,_t); \ + \ + typedef struct CAT3(array_it_,name,_s) { \ + size_t index; \ + struct CAT3(array_, name, _s) *array; \ + } CAT3(array_it_,name,_t)[1]; \ + \ + static inline const type * \ + CAT3(array_, name, _const_cast)(type *ptr) \ + { \ + CAT3(array_union_, name,_t) u; \ + u.ptr = ptr; \ + return u.cptr; \ + } \ + \ + static inline void \ + CAT3(array_, name, _init)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL); \ + v->size = v->alloc = 0; \ + v->ptr = NULL; \ + } \ + \ + static inline void \ + CAT3(array_, name, _clean)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL && v->size <= v->alloc); \ + for(size_t i = 0; i < v->size; i++) \ + M_GET_CLEAR oplist(v->ptr[i]); \ + v->size = 0; \ + } \ + \ + static inline void \ + CAT3(array_, name, _clear)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL && v->size <= v->alloc); \ + CAT3(array_, name, _clean)(v); \ + M_MEMORY_FREE(v->ptr); \ + v->alloc = 0; \ + v->ptr = NULL; \ + } \ + \ + static inline void \ + CAT3(array_, name, _set)(CAT3(array_, name,_t) d, \ + const CAT3(array_, name,_t) s) \ + { \ + assert (d != NULL && s != NULL \ + && d->size <= d->alloc && s->size <= s->alloc); \ + if (d == s) return; \ + if (s->size > d->alloc) { \ + size_t alloc = ARRAYI_INC_ALLOC_SIZE(s->size); \ + type *ptr = M_MEMORY_REALLOC (type, d->ptr, alloc); \ + if (ptr == NULL) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return ; \ + } \ + d->ptr = ptr; \ + d->alloc = alloc; \ + } \ + size_t i, step1 = M_MIN(s->size, d->size); \ + for(i = 0; i < step1; i++) \ + M_GET_SET oplist (d->ptr[i], s->ptr[i]); \ + for( ; i < s->size; i++) \ + M_GET_INIT_SET oplist (d->ptr[i], s->ptr[i]); \ + for( ; i < d->size; i++) \ + M_GET_CLEAR oplist (d->ptr[i]); \ + d->size = s->size; \ + assert (d->size <= d->alloc); \ + } \ + \ + static inline void \ + CAT3(array_, name, _init_set)(CAT3(array_, name,_t) d, \ + const CAT3(array_, name,_t) s) \ + { \ + assert (d != s); \ + CAT3(array_, name, _init)(d); \ + CAT3(array_, name, _set)(d, s); \ + } \ + \ + static inline void \ + CAT3(array_, name, _init_move)(CAT3(array_, name,_t) d, \ + CAT3(array_, name,_t) s) \ + { \ + assert (d != s); \ + d->size = s->size; \ + d->alloc = s->alloc; \ + d->ptr = s->ptr; \ + s->size = s->alloc = 0; \ + s->ptr = NULL; \ + } \ + \ + static inline void \ + CAT3(array_, name, _move)(CAT3(array_, name,_t) d, \ + CAT3(array_, name,_t) s) \ + { \ + assert (d != s); \ + CAT3(array_, name, _clear)(d); \ + CAT3(array_, name, _init_move)(d, s); \ + } \ + \ + static inline void \ + CAT3(array_, name, _set_at)(CAT3(array_, name,_t) v, size_t i, type x) \ + { \ + assert(v != NULL && i < v->size && v->ptr != NULL); \ + M_GET_SET oplist(v->ptr[i], x); \ + } \ + \ + static inline const type * \ + CAT3(array_, name, _back)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL && v->ptr != NULL); \ + return CAT3(array_, name, _const_cast)(&v->ptr[v->size-1]); \ + } \ + \ + static inline type * \ + CAT3(array_, name, _push_raw)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL && v->size <= v->alloc); \ + if (v->size >= v->alloc) { \ + assert(v->size == v->alloc); \ + size_t alloc = ARRAYI_INC_ALLOC_SIZE(v->alloc); \ + assert (alloc > v->size); \ + type *ptr = M_MEMORY_REALLOC (type, v->ptr, alloc); \ + if (ptr == NULL) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return NULL; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + assert(v->ptr != NULL); \ + type *ret = &v->ptr[v->size]; \ + v->size++; \ + return ret; \ + } \ + \ + static inline void \ + CAT3(array_, name, _push_back)(CAT3(array_, name,_t) v, type const x) \ + { \ + assert (v != NULL); \ + type *data = CAT3(array_, name, _push_raw)(v); \ + if (data == NULL) \ + return; \ + M_GET_INIT_SET oplist(*data, x); \ + } \ + \ + static inline type * \ + CAT3(array_, name, _push_new)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL); \ + type *data = CAT3(array_, name, _push_raw)(v); \ + if (data == NULL) \ + return NULL; \ + M_GET_INIT oplist(*data); \ + return data; \ + } \ + \ + static inline void \ + CAT3(array_, name, _push_at)(CAT3(array_, name,_t) v, size_t key, type const x) \ + { \ + assert (v != NULL && key <= v->size && v->size <= v->alloc); \ + if (v->size >= v->alloc) { \ + assert(v->size == v->alloc); \ + size_t alloc = ARRAYI_INC_ALLOC_SIZE(v->alloc); \ + assert (alloc > v->size); \ + type *ptr = M_MEMORY_REALLOC (type, v->ptr, alloc); \ + if (ptr == NULL) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + assert(v->ptr != NULL); \ + memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \ + v->size++; \ + M_GET_INIT_SET oplist (v->ptr[key], x); \ + } \ + \ + static inline void \ + CAT3(array_, name, _resize)(CAT3(array_, name, _t) v, size_t size) \ + { \ + assert (v != NULL && v->size <= v->alloc); \ + if (v->size > size) { \ + /* Decrease size of array */ \ + for(size_t i = size ; i < v->size; i++) \ + M_GET_CLEAR oplist(v->ptr[i]); \ + v->size = size; \ + } else if (v->size < size) { \ + /* Increase size of array */ \ + if (size > v->alloc) { \ + size_t alloc = size ; \ + type *ptr = M_MEMORY_REALLOC (type, v->ptr, alloc); \ + if (ptr == NULL) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + for(size_t i = v->size ; i < size; i++) \ + M_GET_INIT oplist(v->ptr[i]); \ + v->size = size; \ + } \ + assert (v != NULL && v->size <= v->alloc); \ + } \ + \ + static inline void \ + CAT3(array_, name, _set_at2)(CAT3(array_, name,_t) v, size_t idx, type x) \ + { \ + assert (v != NULL && v->size <= v->alloc); \ + size_t size = idx + 1; \ + if (v->size <= size) { \ + /* Increase size of array */ \ + if (size > v->alloc) { \ + size_t alloc = ARRAYI_INC_ALLOC_SIZE (size) ; \ + type *ptr = M_MEMORY_REALLOC (type, v->ptr, alloc); \ + if (ptr == NULL) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + for(size_t i = v->size ; i < size; i++) \ + M_GET_INIT oplist(v->ptr[i]); \ + v->size = size; \ + } \ + M_GET_SET oplist(v->ptr[idx], x); \ + assert (v != NULL && v->size <= v->alloc); \ + } \ + \ + static inline void \ + CAT3(array_, name, _pop_back)(type *dest, CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL && v->size > 0 && v->ptr != NULL); \ + v->size--; \ + if (dest) \ + M_IF_METHOD(MOVE, oplist) ( \ + M_GET_MOVE oplist(*dest, v->ptr[v->size]); else \ + , \ + M_GET_SET oplist(*dest, v->ptr[v->size]); \ + ) \ + M_GET_CLEAR oplist (v->ptr[v->size]); \ + } \ + \ + static inline bool \ + CAT3(array_, name, _empty_p)(const CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL); \ + return v->size == 0; \ + } \ + \ + static inline size_t \ + CAT3(array_, name, _size)(const CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL); \ + return v->size; \ + } \ + \ + static inline size_t \ + CAT3(array_, name, _capacity)(const CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL); \ + return v->alloc; \ + } \ + \ + static inline void \ + CAT3(array_, name, _pop_at)(type *dest, CAT3(array_, name,_t) v, size_t i) \ + { \ + assert(v != NULL && v->size > 0 && i < v->size && v->ptr != NULL); \ + if (dest) \ + M_IF_METHOD(MOVE, oplist) ( \ + M_GET_MOVE oplist(*dest, v->ptr[i]); else \ + , \ + M_GET_SET oplist (*dest, v->ptr[i]); \ + ) \ + M_GET_CLEAR oplist (v->ptr[i]); \ + memmove(&v->ptr[i], &v->ptr[i+1], sizeof(type)*(v->size - i) ); \ + v->size--; \ + } \ + \ + static inline void \ + CAT3(array_, name, _insert_v)(CAT3(array_, name,_t) v, size_t i, size_t j) \ + { \ + assert (v != NULL); \ + assert(i < v->size && j <= v->size && i < j); \ + size_t size = v->size + (j-i); \ + if (size > v->alloc) { \ + size_t alloc = ARRAYI_INC_ALLOC_SIZE (size) ; \ + type *ptr = M_MEMORY_REALLOC (type, v->ptr, alloc); \ + if (ptr == NULL) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + v->ptr = ptr; \ + v->alloc = alloc; \ + } \ + memmove(&v->ptr[j], &v->ptr[i], sizeof(type)*(v->size - j) ); \ + for(size_t k = i ; k < j; k++) \ + M_GET_INIT oplist(v->ptr[i]); \ + v->size = size; \ + } \ + \ + static inline void \ + CAT3(array_, name, _remove_v)(CAT3(array_, name,_t) v, size_t i, size_t j) \ + { \ + assert (v != NULL); \ + assert(i < v->size && j <= v->size && i < j && v->ptr != NULL); \ + for(size_t k = i ; k < j; k++) \ + M_GET_CLEAR oplist(v->ptr[k]); \ + memmove(&v->ptr[i], &v->ptr[j], sizeof(type)*(v->size - j) ); \ + v->size -= (j-i); \ + } \ + \ + static inline void \ + CAT3(array_,name, _remove)(CAT3(array_, name,_t) v, size_t i) \ + { \ + CAT3(array_, name, _pop_at)(NULL, v, i); \ + } \ + \ + static inline void \ + CAT3(array_, name, _swap)(CAT3(array_, name,_t) v, size_t i, size_t j) \ + { \ + assert (v != NULL); \ + assert(i < v->size && j < v->size && v->ptr != NULL); \ + type tmp; \ + M_IF_METHOD(INIT_MOVE, oplist) ( \ + M_GET_INIT_MOVE oplist (tmp, v->ptr[i]); \ + M_GET_INIT_MOVE oplist (v->ptr[i], v->ptr[j]); \ + M_GET_INIT_MOVE oplist (v->ptr[j], tmp); \ + , \ + M_GET_INIT_SET oplist (tmp, v->ptr[i]); \ + M_GET_SET oplist(v->ptr[i], v->ptr[j]); \ + M_GET_SET oplist(v->ptr[j], tmp); \ + M_GET_CLEAR oplist(tmp); \ + ) /* IF INIT_MOVE method */ \ + } \ + \ + static inline void \ + CAT3(array_, name, _shrink_to_fit)(CAT3(array_, name,_t) v) \ + { \ + assert (v != NULL && v->size <= v->alloc); \ + v->ptr = M_MEMORY_REALLOC (type, v->ptr, v->size); \ + assert(v->ptr != NULL || v->size == 0); \ + v->alloc = v->size; \ + } \ + \ + static inline type * \ + CAT3(array_, name, _get)(CAT3(array_, name,_t) v, size_t i) \ + { \ + assert (v != NULL && i < v->size && v->ptr != NULL); \ + return &v->ptr[i]; \ + } \ + \ + static inline const type * \ + CAT3(array_, name, _cget)(const CAT3(array_, name,_t) v, size_t i) \ + { \ + assert(v != NULL && i < v->size && v->ptr != NULL); \ + return CAT3(array_, name, _const_cast)(&v->ptr[i]); \ + } \ + \ + static inline void \ + CAT3(array_, name, _it)(CAT3(array_it_,name,_t) it, \ + CAT3(array_, name, _t) v) \ + { \ + assert (it != NULL && v != NULL); \ + it->index = 0; \ + it->array = v; \ + } \ + \ + static inline void \ + CAT3(array_, name, _it_set)(CAT3(array_it_,name,_t) it, \ + const CAT3(array_it_, name, _t) org) \ + { \ + assert (it != NULL && org != NULL); \ + it->index = org->index; \ + it->array = org->array; \ + } \ + \ + static inline bool \ + CAT3(array_, name, _end_p)(const CAT3(array_it_, name, _t) it) \ + { \ + assert(it != NULL && it->array != NULL); \ + return (it->index) >= it->array->size; \ + } \ + \ + static inline bool \ + CAT3(array_, name, _last_p)(const CAT3(array_it_, name, _t) it) \ + { \ + assert(it != NULL && it->array != NULL); \ + /* Case i=0 & s=0 ==> true since unsigned type */ \ + return (it->index) >= it->array->size-1; \ + } \ + \ + static inline bool \ + CAT3(array_, name, _it_equal_p)(const CAT3(array_it_, name,_t) it1, \ + const CAT3(array_it_, name,_t) it2) \ + { \ + assert(it1 != NULL && it2 != NULL); \ + return it1->array == it2->array && it1->index == it2->index; \ + } \ + \ + static inline void \ + CAT3(array_, name, _next)(CAT3(array_it_,name,_t) it) \ + { \ + assert(it != NULL && it->array != NULL); \ + it->index ++; \ + } \ + \ + static inline void \ + CAT3(array_, name, _previous)(CAT3(array_it_,name,_t) it) \ + { \ + assert(it != NULL && it->array != NULL); \ + /* In the case index=0, it will be set to (unsigned) -1 \ + ==> it will be greater than size ==> end_p will return true */ \ + it->index --; \ + } \ + \ + static inline type * \ + CAT3(array_, name, _ref)(CAT3(array_it_, name,_t) it) \ + { \ + assert(it != NULL); \ + return CAT3(array_, name, _get)(it->array, it->index); \ + } \ + \ + static inline const type * \ + CAT3(array_, name, _cref)(const CAT3(array_it_, name,_t) it) \ + { \ + assert(it != NULL); \ + return CAT3(array_, name, _cget)(it->array, it->index); \ + } \ + \ + static inline void \ + CAT3(array_, name, _insert)(CAT3(array_it_, name,_t) it, type const x) \ + { \ + assert(it != NULL); \ + CAT3(array_, name, _push_at)(it->array, it->index, x); \ + } \ + \ + M_IF_METHOD(CMP, oplist) \ + ( \ + static inline void CAT3(array_, name, _sort)(CAT3(array_, name,_t) l, \ + int (*func_type) (const type *a, const type *b))\ + { \ + /* Using qsort is more compact but slower than a full templated \ + version which can be twice faster */ \ + int (*func_void)(const void*, const void*); \ + /* There is no way to avoid the cast */ \ + func_void = (int (*)(const void*, const void*))func_type; \ + qsort (l->ptr, l->size, sizeof(type), func_void); \ + } \ + ,) /* IF CMP oplist */ \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + static inline void \ + CAT3(array_, name, _get_str)(string_t str, CAT3(array_, name,_t) array, \ + bool append) \ + { \ + assert (str != NULL && array != NULL); \ + (append ? string_cat_str : string_set_str) (str, "["); \ + CAT3(array_it_, name, _t) it; \ + for (CAT3(array_, name, _it)(it, array) ; \ + !CAT3(array_, name, _end_p)(it); \ + CAT3(array_, name, _next)(it)){ \ + const type *item = CAT3(array_, name, _cref)(it); \ + M_GET_GET_STR oplist (str, *item, true); \ + if (!CAT3(array_, name, _last_p)(it)) \ + string_push_back (str, M_GET_SEPARATOR oplist); \ + } \ + string_push_back (str, ']'); \ + } \ + , /* no str */ ) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + static inline void \ + CAT3(array_, name, _out_str)(FILE *file, CAT3(array_, name,_t) array) \ + { \ + assert (file != NULL && array != NULL); \ + fprintf (file, "["); \ + CAT3(array_it_, name, _t) it; \ + for (CAT3(array_, name, _it)(it, array) ; \ + !CAT3(array_, name, _end_p)(it); \ + CAT3(array_, name, _next)(it)){ \ + const type *item = CAT3(array_, name, _cref)(it); \ + M_GET_OUT_STR oplist (file, *item); \ + if (!CAT3(array_, name, _last_p)(it)) \ + fputc (M_GET_SEPARATOR oplist, file); \ + } \ + fprintf (file, "]"); \ + } \ + , /* no out_str */ ) \ + \ + M_IF_METHOD(IN_STR, oplist)( \ + static inline bool \ + CAT3(array_, name, _in_str)(CAT3(array_, name,_t) array, FILE *file) \ + { \ + assert (file != NULL && array != NULL); \ + CAT3(array_, name,_clean)(array); \ + char c = fgetc(file); \ + if (c != '[') return false; \ + type item; \ + M_GET_INIT oplist (item); \ + do { \ + bool b = M_GET_IN_STR oplist (item, file); \ + if (!b) { M_GET_CLEAR oplist (item); return false; } \ + CAT3(array_, name, _push_back)(array, item); \ + c = fgetc(file); \ + } while (c == M_GET_SEPARATOR oplist && !feof(file) && !ferror(file)); \ + M_GET_CLEAR oplist (item); \ + return c == ']'; \ + } \ + , /* no in_str */ ) \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + static inline bool \ + CAT3(array_, name, _equal_p)(CAT3(array_, name,_t) array1, \ + CAT3(array_, name,_t) array2) \ + { \ + assert (array1 != NULL && array2 != NULL); \ + CAT3(array_it_, name, _t) it1; \ + CAT3(array_it_, name, _t) it2; \ + CAT3(array_, name, _it)(it1, array1); \ + CAT3(array_, name, _it)(it2, array2); \ + while (!CAT3(array_, name, _end_p)(it1) \ + &&!CAT3(array_, name, _end_p)(it2)) { \ + const type *item1 = CAT3(array_, name, _cref)(it1); \ + const type *item2 = CAT3(array_, name, _cref)(it2); \ + bool b = M_GET_EQUAL oplist (*item1, *item2); \ + if (!b) return false; \ + CAT3(array_, name, _next)(it1); \ + CAT3(array_, name, _next)(it2); \ + } \ + return CAT3(array_, name, _end_p)(it1) \ + && CAT3(array_, name, _end_p)(it2); \ + } \ + , /* no equal */ ) \ + \ + +// TODO: Conditional HASH if HASH is present +// TODO: 'remove' interface is different in list & array ==> erase for iterator & pop for size_t + +#endif diff --git a/m-bitset.h b/m-bitset.h new file mode 100644 index 00000000..2cc38a4d --- /dev/null +++ b/m-bitset.h @@ -0,0 +1,382 @@ +/* + * MLIB - BITSET module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_BITSET_H +#define __M_BITSET_H + +#include +#include +#include +#include +#include + +#include "m-core.h" + +typedef struct bitset_s { + size_t size; // Size is the number of bits + size_t alloc; // Alloc is the number of allocated bytes + unsigned char *ptr; +} bitset_t[1]; + +typedef struct bitset_it_s { + size_t index; + struct bitset_s *set; +} bitset_it_t[1]; + +// Compute alloc from size. +#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 8 ? 16 : (n) * 2) +#define BITSETI_TO_ALLOC(n) (((n) + CHAR_BIT - 1) / CHAR_BIT) +#define BITSETI_FROM_ALLOC(n) ((n) * CHAR_BIT) + +#define BITSETI_CONTRACT(t) \ + assert (t != NULL); \ + assert (t->size <= BITSETI_FROM_ALLOC (t->alloc)); \ + assert (t->size == 0 || t->ptr != NULL); + +static inline void +bitset_init(bitset_t t) +{ + assert (t != NULL); + assert (M_POWEROF2_P(CHAR_BIT)); + t->size = t->alloc = 0; + t->ptr = NULL; +} + +static inline void +bitset_clean(bitset_t t) +{ + BITSETI_CONTRACT(t); + t->size = 0; +} + +static inline void +bitset_clear(bitset_t t) +{ + bitset_clean(t); + M_MEMORY_FREE(t->ptr); + t->alloc = 0; + t->ptr = NULL; +} + +static inline void +bitset_set(bitset_t d, const bitset_t s) +{ + BITSETI_CONTRACT(d); + BITSETI_CONTRACT(s); + if (d == s) return; + const size_t needAlloc = BITSETI_TO_ALLOC (s->size); + if (s->size > BITSETI_FROM_ALLOC (d->alloc)) { + unsigned char *ptr = M_MEMORY_REALLOC (unsigned char, d->ptr, needAlloc); + if (ptr == NULL) { + M_MEMORY_FULL(needAlloc); + return ; + } + d->ptr = ptr; + d->alloc = needAlloc; + } + memcpy (d->ptr, s->ptr, needAlloc); + d->size = s->size; + BITSETI_CONTRACT(d); +} + +static inline void +bitset_init_set(bitset_t d, const bitset_t s) +{ + assert (d != s); + bitset_init(d); + bitset_set(d, s); +} + +static inline void +bitset_init_move(bitset_t d, bitset_t s) +{ + BITSETI_CONTRACT(s); + d->size = s->size; + d->alloc = s->alloc; + d->ptr = s->ptr; + s->alloc = 0; + s->ptr = NULL; + BITSETI_CONTRACT(d); +} + +static inline void +bitset_move(bitset_t d, bitset_t s) +{ + bitset_clear(d); + bitset_init_move (d, s); +} + +static inline void +bitset_set_at(bitset_t v, size_t i, bool x) +{ + BITSETI_CONTRACT(v); + assert (i < v->size && v->ptr != NULL); + size_t offset = i / CHAR_BIT; + size_t index = i % CHAR_BIT; + if (x == true){ + v->ptr[offset] |= 1<ptr[offset] &= ~(1<size >= BITSETI_FROM_ALLOC (v->alloc)) { + const size_t needAlloc = BITSETI_INC_ALLOC_SIZE(v->alloc); + unsigned char *ptr = M_MEMORY_REALLOC (unsigned char, v->ptr, needAlloc); + if (ptr == NULL) { + M_MEMORY_FULL(needAlloc); + return; + } + v->ptr = ptr; + v->alloc = needAlloc; + } + assert(v->ptr != NULL); + v->size ++; + bitset_set_at (v, v->size - 1, x); +} + +static inline void +bitset_resize (bitset_t v, size_t size) +{ + BITSETI_CONTRACT (v); + size_t oldbytes = BITSETI_TO_ALLOC (v->size); + size_t newbytes = BITSETI_TO_ALLOC (size); + if (oldbytes != newbytes) { + unsigned char *ptr = M_MEMORY_REALLOC (unsigned char, v->ptr, newbytes); + if (ptr == NULL) { + M_MEMORY_FULL(newbytes); + return; + } + v->ptr = ptr; + v->alloc = newbytes; + } + v->size = size; + BITSETI_CONTRACT (v); +} + +static inline bool +bitset_get(const bitset_t v, size_t i) +{ + BITSETI_CONTRACT(v); + assert (v->ptr != NULL && i < v->size); + size_t offset = i / CHAR_BIT; + size_t index = i % CHAR_BIT; + return ( v->ptr[offset] & (1 << index) ) != 0; +} +#define bitset_cget bitset_get + +static inline void +bitset_pop_back(bool *dest, bitset_t v) +{ + BITSETI_CONTRACT (v); + assert (v->size > 0); + if (dest) { + *dest = bitset_get (v, v->size - 1); + } + v->size--; + BITSETI_CONTRACT (v); +} + +static inline bool +bitset_back(bitset_t v) +{ + assert(v->size > 0); + return bitset_get(v, v->size-1); +} + +static inline bool +bitset_empty_p(bitset_t v) +{ + BITSETI_CONTRACT (v); + return v->size == 0; +} + +static inline size_t +bitset_size(bitset_t v) +{ + BITSETI_CONTRACT (v); + return v->size; +} + +static inline size_t +bitset_capacity(bitset_t v) +{ + BITSETI_CONTRACT (v); + return BITSETI_FROM_ALLOC (v->alloc); +} + +static inline void +bitset_swap (bitset_t v, size_t i, size_t j) +{ + BITSETI_CONTRACT (v); + assert (i < v->size && j < v->size); + bool i_val = bitset_get(v, i); + bool j_val = bitset_get(v, j); + bitset_set_at (v, i, j_val); + bitset_set_at (v, j, i_val); + BITSETI_CONTRACT (v); +} + +static inline unsigned char +bitseti_lshift(unsigned char ptr[], size_t n, unsigned char carry) +{ + for(size_t i = 0; i < n; i++) + { + unsigned char v = ptr[i]; + ptr[i] = (v << 1) | carry; + carry = (v >> (CHAR_BIT-1) ); + } + return carry; +} + +static inline unsigned char +bitseti_rshift(unsigned char ptr[], size_t n, unsigned char carry) +{ + for(size_t i = n - 1; i < n; i--) + { + unsigned char v = ptr[i]; + v = (v >> 1) | (carry << (CHAR_BIT-1) ); + carry = v & 1; + } + return carry; +} + +static inline void +bitset_push_at(bitset_t set, size_t key, bool value) +{ + BITSETI_CONTRACT (set); + // First push another value to extend the array to the right size + bitset_push_back(set, true); + assert (set->ptr != NULL && key < set->size); + // Then shift it + size_t offset = key / CHAR_BIT; + size_t index = key % CHAR_BIT; + // BEFORE: 76543210 FEDBCA98 + // if key=5 ==> + // AFTER: 65X43210 EDBCA987 000000F + unsigned char v = set->ptr[offset]; + unsigned char mask = (1<> (CHAR_BIT-1) ); + v = (v & mask) | (value << index) | ((v & ~mask) << 1); + set->ptr[offset] = v; + size_t size = (set->size + CHAR_BIT - 1) / CHAR_BIT; + v = bitseti_lshift(&set->ptr[offset+1], size - offset - 1, carry); + BITSETI_CONTRACT (set); +} + +static inline void +bitset_pop_at(bool *dest, bitset_t set, size_t key) +{ + BITSETI_CONTRACT (set); + assert (set->ptr != NULL && key < set->size); + if (dest) { + *dest = bitset_get (set, key); + } + // Shift it + size_t offset = key / CHAR_BIT; + size_t index = key % CHAR_BIT; + // BEFORE: 76543210 FEDBCA98 + // if key=5 ==> + // AFTER: 8643210 XFEDBCA9 + size_t size = (set->size + CHAR_BIT - 1) / CHAR_BIT; + unsigned char v, mask, carry; + carry = bitseti_rshift(&set->ptr[offset+1], size - offset - 1, false); + v = set->ptr[offset]; + mask = (1<>1) & ~mask) | (carry << (CHAR_BIT-1)) ; + set->ptr[offset] = v; + // Decrease size + set->size --; + BITSETI_CONTRACT (set); +} + +static inline void +bitset_it(bitset_it_t it, bitset_t set) +{ + BITSETI_CONTRACT (set); + it->index = 0; + it->set = set; +} + +static inline void +bitset_it_set(bitset_it_t it, const bitset_it_t itorg) +{ + assert (it != NULL && itorg != NULL); + it->index = itorg->index; + it->set = itorg->set; +} + +static inline bool +bitset_end_p(const bitset_it_t it) +{ + assert (it != NULL && it->set != NULL); + return (it->index) >= (it->set->size); +} + +static inline bool +bitset_last_p(const bitset_it_t it) +{ + assert (it != NULL && it->set != NULL); + return (it->index) >= (it->set->size)-1; +} + +static inline bool +bitset_it_equal_p(const bitset_it_t it1, const bitset_it_t it2) +{ + assert (it1 != NULL && it2 != NULL); + return it1->index == it2->index && it1->set == it2->set; +} + +static inline void +bitset_next(bitset_it_t it) +{ + assert (it != NULL && it->set != NULL); + it->index++; +} + +static inline void +bitset_previous(bitset_it_t it) +{ + assert (it != NULL && it->set != NULL); + it->index--; +} + +static inline bool +bitset_ref(const bitset_it_t it) +{ + assert (it != NULL && it->set != NULL); + return bitset_get(it->set, it->index); +} +#define bitset_cref bitset_ref + +// TODO: stringify... +// TODO: OPLIST +// TODO: set_at2, insert_v, remove_v, shrink_to_fit, + +#endif diff --git a/m-buffer.h b/m-buffer.h new file mode 100644 index 00000000..9fcc6fc3 --- /dev/null +++ b/m-buffer.h @@ -0,0 +1,244 @@ +/* + * MLIB - BUFFER & STACK interface + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_BUFFER_H +#define __M_BUFFER_H + +#include +#include +#include +#include + +#include "m-core.h" +#include "m-mutex.h" + +typedef enum { + BUFFER_QUEUE = 0, BUFFER_STACK = 1, + BUFFER_BLOCKING = 0, BUFFER_UNBLOCKING = 2, + BUFFER_THREAD_SAFE = 0, BUFFER_THREAD_UNSAFE = 4, + BUFFER_PUSH_INIT_POP_MOVE = 8, + BUFFER_PUSH_OVERWRITE = 16 +} buffer_policy_e; + +#define BUFFER_DEF(name, type, m_size, ... ) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (BUFFERI_DEF2(name, type, m_size,__VA_ARGS__, ()), \ + BUFFERI_DEF2(name, type, m_size,__VA_ARGS__)) + +/********************************** INTERNAL ************************************/ + +#define BUFFERI_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size)) +#define BUFFERI_SIZE(m_size) BUFFERI_IF_CTE_SIZE(m_size) (m_size, v->size) + +#define BUFFERI_DEF2(name, type, m_size, policy, oplist) \ + typedef struct CAT3(buffer_, name, _s) { \ + M_MUTEX_T mutex; \ + M_COND_T there_is_data; \ + M_COND_T there_is_room_for_data; \ + BUFFERI_IF_CTE_SIZE(m_size)( ,size_t size;) \ + size_t idx_prod, idx_cons, number; \ + type *data; \ + } CAT3(buffer_, name,_t)[1]; \ + \ +static inline void \ + CAT3(buffer_, name, _init)(CAT3(buffer_, name,_t) v, size_t size) \ +{ \ + assert( size > 0); \ + BUFFERI_IF_CTE_SIZE(m_size)(assert(size == m_size), v->size = size); \ + v->idx_prod = v->idx_cons = v->number = 0; \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + M_MUTEX_INIT(v->mutex); \ + M_COND_INIT(v->there_is_data); \ + M_COND_INIT(v->there_is_room_for_data); \ + } else \ + assert(((policy)&BUFFER_UNBLOCKING) == BUFFER_UNBLOCKING); \ + v->data = M_MEMORY_REALLOC (type, NULL, BUFFERI_SIZE(m_size)); \ + if (v->data == NULL) { \ + M_MEMORY_FULL (BUFFERI_SIZE(m_size)*sizeof(type)); \ + return; \ + } \ + if (((policy) & BUFFER_PUSH_INIT_POP_MOVE) == 0) { \ + for(size_t i = 0; i < size; i++) \ + M_GET_INIT oplist(v->data[i]); \ + } \ +} \ + \ + static inline void \ + CAT3(bufferi_, name, _clear_obj)(CAT3(buffer_, name,_t) v) \ + { \ + if (((policy)&BUFFER_PUSH_INIT_POP_MOVE) == 0) { \ + for(size_t i = 0; i < BUFFERI_SIZE(m_size); i++) \ + M_GET_CLEAR oplist(v->data[i]); \ + } else { \ + size_t i = ((policy)&BUFFER_STACK) != 0 ? 0 : v->idx_cons; \ + while (i != v->idx_prod) { \ + M_GET_CLEAR oplist(v->data[i]); \ + i++; \ + if (((policy)&BUFFER_STACK) == 0 && i >= BUFFERI_SIZE(m_size)) \ + i = 0; \ + } \ + } \ + v->idx_prod = v->idx_cons = v->number = 0; \ + } \ + \ + static inline void \ + CAT3(buffer_, name, _clear)(CAT3(buffer_, name,_t) v) \ + { \ + int rc; \ + CAT3(bufferi_, name, _clear_obj)(v); \ + M_MEMORY_FREE (v->data); \ + v->data = NULL; \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + rc = M_MUTEX_CLEAR(v->mutex); \ + assert (rc == 0); \ + rc = M_COND_CLEAR(v->there_is_data); \ + assert (rc == 0); \ + rc = M_COND_CLEAR(v->there_is_room_for_data); \ + assert (rc == 0); \ + } \ + } \ + \ + static inline void \ + CAT3(buffer_, name, _clean)(CAT3(buffer_, name,_t) v) \ + { \ + assert (v->number <= BUFFERI_SIZE(m_size)); \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) \ + M_MUTEX_LOCK(v->mutex); \ + if (((policy)&BUFFER_PUSH_INIT_POP_MOVE) != 0) \ + CAT3(bufferi_, name, _clear_obj)(v); \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + M_COND_SIGNAL(v->there_is_room_for_data); \ + M_MUTEX_UNLOCK(v->mutex); \ + } \ + } \ + \ + static inline bool \ + CAT3(buffer_, name, _empty_p)(CAT3(buffer_, name,_t) v) \ + { \ + assert(v->number <= BUFFERI_SIZE(m_size)); \ + return v->number == 0; \ + } \ + \ + static inline bool \ + CAT3(buffer_, name, _full_p)(CAT3(buffer_, name,_t) v) \ + { \ + assert(v->number <= BUFFERI_SIZE(m_size)); \ + return v->number == BUFFERI_SIZE(m_size); \ + } \ + \ + static inline bool \ + CAT3(buffer_, name, _push)(CAT3(buffer_, name,_t) v, type const data) \ + { \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + M_MUTEX_LOCK(v->mutex); \ + while (((policy)&BUFFER_PUSH_OVERWRITE) == 0 \ + && CAT3(buffer_, name, _full_p)(v)) { \ + if (((policy) & BUFFER_UNBLOCKING) == BUFFER_UNBLOCKING) { \ + M_MUTEX_UNLOCK(v->mutex); \ + return false; \ + } \ + M_COND_WAIT(v->there_is_room_for_data, v->mutex); \ + } \ + } else if (((policy)&BUFFER_PUSH_OVERWRITE) == 0 \ + && CAT3(buffer_, name, _full_p)(v)) \ + return false; \ + \ + if (((policy)&BUFFER_PUSH_OVERWRITE) != 0 \ + && v->number == BUFFERI_SIZE(m_size) ) { \ + /* Let's clear the last element to overwrite it */ \ + if (((policy) & BUFFER_STACK) == 0) { \ + v->idx_prod = (v->idx_prod - 1) % BUFFERI_SIZE(m_size); \ + } else { \ + (v->idx_prod) --; \ + } \ + v->number --; \ + if (((policy) & BUFFER_PUSH_INIT_POP_MOVE) != 0) { \ + M_GET_CLEAR oplist(v->data[v->idx_prod]); \ + } \ + } \ + \ + if (((policy) & BUFFER_PUSH_INIT_POP_MOVE) == 0) { \ + M_GET_SET oplist(v->data[v->idx_prod], data); \ + } else { \ + M_GET_INIT_SET oplist(v->data[v->idx_prod], data); \ + } \ + if (((policy) & BUFFER_STACK) != BUFFER_STACK) { \ + v->idx_prod = (v->idx_prod +1) % BUFFERI_SIZE(m_size); \ + } else { \ + (v->idx_prod) ++; \ + } \ + v->number ++; \ + \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + M_COND_SIGNAL(v->there_is_data); \ + M_MUTEX_UNLOCK(v->mutex); \ + } \ + return true; \ + } \ + \ + static inline bool \ + CAT3(buffer_, name, _pop)(type *data, CAT3(buffer_, name,_t) v) \ + { \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + M_MUTEX_LOCK(v->mutex); \ + while (CAT3(buffer_, name, _empty_p)(v)) { \ + if (((policy) & BUFFER_UNBLOCKING) == BUFFER_UNBLOCKING) { \ + M_MUTEX_UNLOCK(v->mutex); \ + return false; \ + } \ + M_COND_WAIT(v->there_is_data, v->mutex); \ + } \ + } else if (CAT3(buffer_, name, _empty_p)(v)) \ + return false; \ + \ + if (((policy) & BUFFER_STACK) != BUFFER_STACK) { \ + if (((policy) & BUFFER_PUSH_INIT_POP_MOVE ) == 0) { \ + M_GET_SET oplist (*data, v->data[v->idx_cons]); \ + } else { \ + M_GET_INIT_MOVE oplist (*data, v->data[v->idx_cons]); \ + } \ + v->idx_cons = (v->idx_cons +1) % BUFFERI_SIZE(m_size); \ + } else { \ + v->idx_prod --; \ + if (((policy) & BUFFER_PUSH_INIT_POP_MOVE ) == 0) { \ + M_GET_SET oplist (*data, v->data[v->idx_prod]); \ + } else { \ + M_GET_INIT_MOVE oplist (*data, v->data[v->idx_prod]); \ + } \ + } \ + v->number --; \ + \ + if (((policy) & BUFFER_THREAD_UNSAFE) != BUFFER_THREAD_UNSAFE) { \ + M_COND_SIGNAL(v->there_is_room_for_data); \ + M_MUTEX_UNLOCK(v->mutex); \ + } \ + return true; \ + } \ + +// NOTE: SET & INIT_SET are deliberatly missing. TBC if it the right way. + +// TODO: priority queue when it is the one with the huge priority which is taken out +// TODO: merge queue waiting for all threads to finish producing one object to make it available to consummer (allow partial production by different threads) + +#endif diff --git a/m-core.h b/m-core.h new file mode 100644 index 00000000..1ef94e7e --- /dev/null +++ b/m-core.h @@ -0,0 +1,801 @@ +/* + * MLIB - Extended Pre-processing macros module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_MACRO_H +#define __M_MACRO_H + +#include +#include + +//TODO: Prefix all macros with "M_" prefix. + +/***************************************************************/ +/************************ Compiler Macro ***********************/ +/***************************************************************/ + +#if !defined(NDEBUG) +# define M_ASSUME(x) assert(x) +#elif defined(__GNUC__) \ + && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480 +# define M_ASSUME(x) \ + (! __builtin_constant_p (!!(x) || !(x)) || (x) ? \ + (void) 0 : __builtin_unreachable()) +#elif defined(_MSC_VER) +# define M_ASSUME(x) __assume(x) +#else +# define M_ASSUME(x) assert(x) +#endif + +#ifdef __GNUC__ +# define M_LIKELY(cond) __builtin_expect(!!(cond), 1) +# define M_UNLIKELY(cond) __builtin_expect(!!(cond), 0) +#else +# define M_LIKELY(cond) (cond) +# define M_UNLIKELY(cond) (cond) +#endif + + +/***************************************************************/ +/****************** Preprocessing Times Macro ******************/ +/***************************************************************/ + +/* Maximum number of argument which can be handled by this header. + Can be increased for future release of this header. */ +#define M_MAX_NB_ARGUMENT 26 + +/* Basic handling of concatenation of symbols: + * CAT, CAT3, CAT4 + */ +#define CAT0(a, ...) a ## __VA_ARGS__ +#define CAT(a, ...) CAT0(a, __VA_ARGS__) +#define CAT3_0(a, b, ...) a ## b ## __VA_ARGS__ +#define CAT3(a, b, ...) CAT3_0(a ,b, __VA_ARGS__) +#define CAT4_0(a, b, c, ...) a ## b ## c ## __VA_ARGS__ +#define CAT4(a, b, c, ...) CAT4_0(a ,b, c, __VA_ARGS__) + +/* Increment the number given in argument (from [0..29[) */ +#define M_INC(x) CAT(M_INC_, x) +#define M_INC_0 1 +#define M_INC_1 2 +#define M_INC_2 3 +#define M_INC_3 4 +#define M_INC_4 5 +#define M_INC_5 6 +#define M_INC_6 7 +#define M_INC_7 8 +#define M_INC_8 9 +#define M_INC_9 10 +#define M_INC_10 11 +#define M_INC_11 12 +#define M_INC_12 13 +#define M_INC_13 14 +#define M_INC_14 15 +#define M_INC_15 16 +#define M_INC_16 17 +#define M_INC_17 18 +#define M_INC_18 19 +#define M_INC_19 20 +#define M_INC_20 21 +#define M_INC_21 22 +#define M_INC_22 23 +#define M_INC_23 24 +#define M_INC_24 25 +#define M_INC_25 26 +#define M_INC_26 27 +#define M_INC_27 28 +#define M_INC_28 29 +#define M_INC_29 OVERFLOW + +/* Decrement the number given in argument (from [0..29[) */ +#define M_DEC(x) CAT(M_DEC_, x) +#define M_DEC_0 overflow +#define M_DEC_1 0 +#define M_DEC_2 1 +#define M_DEC_3 2 +#define M_DEC_4 3 +#define M_DEC_5 4 +#define M_DEC_6 5 +#define M_DEC_7 6 +#define M_DEC_8 7 +#define M_DEC_9 8 +#define M_DEC_10 9 +#define M_DEC_11 10 +#define M_DEC_12 11 +#define M_DEC_13 12 +#define M_DEC_14 13 +#define M_DEC_15 14 +#define M_DEC_16 15 +#define M_DEC_17 16 +#define M_DEC_18 17 +#define M_DEC_19 18 +#define M_DEC_20 19 +#define M_DEC_21 20 +#define M_DEC_22 21 +#define M_DEC_23 22 +#define M_DEC_24 23 +#define M_DEC_25 24 +#define M_DEC_26 25 +#define M_DEC_27 26 +#define M_DEC_28 27 +#define M_DEC_29 28 + +/* Return the nth argument: + M_RET_ARG1 + M_RET_ARG2 + M_RET_ARG3 + M_RET_ARG27 + */ +#define M_RETI_ARG1(a, ...) a +#define M_RET_ARG1(...) M_RETI_ARG1(__VA_ARGS__) + +#define M_RETI_ARG2(a, b, ...) b +#define M_RET_ARG2(...) M_RETI_ARG2(__VA_ARGS__) + +#define M_RETI_ARG3(a, b, c, ...) c +#define M_RET_ARG3(...) M_RETI_ARG3(__VA_ARGS__) + +#define M_RETI_ARG27(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa, ...) aa +#define M_RET_ARG27(...) M_RETI_ARG27(__VA_ARGS__) + + +/* Convert an integer or a symbol into 0 (if 0) or 1 (if not 0). + 1 if symbol unknown */ +#define M_TOBOOL_0 1, 0, +#define M_BOOL(x) M_RET_ARG2(CAT(M_TOBOOL_, x), 1, useless) + +/* Inverse 0 into 1 and 1 into 0 */ +#define M_INV_0 1 +#define M_INV_1 0 +#define M_INV(x) CAT(M_INV_, x) + +/* Perform a AND between the inputs */ +#define M_AND_00 0 +#define M_AND_01 0 +#define M_AND_10 0 +#define M_AND_11 1 +#define M_AND(x,y) CAT3(M_AND_, x, y) + +/* Perform a OR between the inputs */ +#define M_OR_00 0 +#define M_OR_01 1 +#define M_OR_10 1 +#define M_OR_11 1 +#define M_OR(x,y) CAT3(M_OR_, x, y) + +/* M_IF Macro : + M_IF(42)(Execute if true, execute if false) + Example: M_IF(0)(true_action, false_action) --> false_action */ +#define M_IF_CAT(c) CAT(M_IF_, c) +#define M_IF_0(true_macro, ...) __VA_ARGS__ +#define M_IF_1(true_macro, ...) true_macro +#define M_IF(c) M_IF_CAT(M_BOOL(c)) + +/* Return 1 if comma inside the argument list, 0 otherwise */ +#define M_COMMA_P(...) M_RET_ARG27(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless) + +/* Return 1 if the argument is 'empty', 0 otherwise. + Handle: EMPTY_P(), EMPTY_P(x), EMPTY_P(()) and EMPTY_P(,) and EMPTY_P(f) with #define f() 2,3 */ +#define M_EMPTY_DETECT(...) 0, 1, +#define M_EMPTY_P_C1(...) M_COMMA_P(M_EMPTY_DETECT __VA_ARGS__ () ) +#define M_EMPTY_P_C2(...) M_COMMA_P(M_EMPTY_DETECT __VA_ARGS__) +#define M_EMPTY_P_C3(...) M_COMMA_P(__VA_ARGS__ () ) +#define M_EMPTY_P(...) M_AND(M_EMPTY_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTY_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTY_P_C3(__VA_ARGS__)))) + +/* Generate a comma later in the next evaluation pass. */ +#define M_DEFERRED_COMMA , + +/* M_IF macro for empty arguments: + M_IF_EMPTY(arguments)(action if empty, action if not empty) */ +#define M_IF_EMPTY(...) M_IF(M_EMPTY_P(__VA_ARGS__)) + +/* Return 1 if argument is "()" or "(x)" */ +#define M_PARENTHESIS_DETECT(...) 0, 1, +#define M_PARENTHESIS_P(x) M_RET_ARG2(M_PARENTHESIS_DETECT x, 0, useless) + +/* Necessary macros to handle recursivity */ +/* Delay the evaluation by one level or two or three or ... */ +/* The argument is a macro-function which has to be deferred */ +#define M_DELAY0() +#define M_DELAY1(...) __VA_ARGS__ M_DELAY0 () +#define M_DELAY2(...) __VA_ARGS__ M_DELAY1 (M_DELAY0) () +#define M_DELAY3(...) __VA_ARGS__ M_DELAY2 (M_DELAY0) () +#define M_DELAY4(...) __VA_ARGS__ M_DELAY3 (M_DELAY0) () +#define M_DELAY5(...) __VA_ARGS__ M_DELAY4 (M_DELAY0) () +#define M_DELAY6(...) __VA_ARGS__ M_DELAY5 (M_DELAY0) () +#define M_DELAY7(...) __VA_ARGS__ M_DELAY6 (M_DELAY0) () +#define M_DELAY8(...) __VA_ARGS__ M_DELAY7 (M_DELAY0) () +#define M_DELAY9(...) __VA_ARGS__ M_DELAY8 (M_DELAY0) () + +/* Perform 3^5 evaluation */ +#define M_EVAL(...) M_EVAL1(M_EVAL1(M_EVAL1(__VA_ARGS__))) +#define M_EVAL1(...) M_EVAL2(M_EVAL2(M_EVAL2(__VA_ARGS__))) +#define M_EVAL2(...) M_EVAL3(M_EVAL3(M_EVAL3(__VA_ARGS__))) +#define M_EVAL3(...) M_EVAL4(M_EVAL4(M_EVAL4(__VA_ARGS__))) +#define M_EVAL4(...) M_EVAL0(M_EVAL0(M_EVAL0(__VA_ARGS__))) +#define M_EVAL0(...) __VA_ARGS__ + +/* Apply Macro : + It allows that a won't be evaluated until all arguments of 'a' have been already evaluated */ +#define M_APPLY(a, ...) a (__VA_ARGS__) + +/* Apply 'func' to arg if 'arg' is not empty */ +/* Example: M_APPLY_FUNC(f, 2) ==> 'f(2)' and M_APPLY_FUNC(f, ) ==> '' */ +#define M_APPLY_FUNC(func, arg) \ + M_IF(M_INV(M_EMPTY_P(arg)))(func(arg),) + +/* Apply 'func' to arg1,arg2 if 'arg2' is not empty */ +/* Example: M_APPLY_FUNC2(f, 2, 3) ==> 'f(2, 3)' and M_APPLY_FUNC2(f, 2, ) ==> '' */ +#define M_APPLY_FUNC2(func, arg1, arg2) \ + M_IF(M_INV(M_EMPTY_P(arg2)))(func(arg1, arg2),) + +/* Map a macro to all given arguments (non recursive version) */ +/* Example: M_MAP(f,a, b, c) ==> f(a) f(b) f(c) */ +#define M_MAP_0(func, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ + M_APPLY_FUNC(func, a) M_APPLY_FUNC(func, b) M_APPLY_FUNC(func, c) M_APPLY_FUNC(func, d) M_APPLY_FUNC(func, e) \ + M_APPLY_FUNC(func, f) M_APPLY_FUNC(func, g) M_APPLY_FUNC(func, h) M_APPLY_FUNC(func, i) M_APPLY_FUNC(func, j) \ + M_APPLY_FUNC(func, k) M_APPLY_FUNC(func, l) M_APPLY_FUNC(func, m) M_APPLY_FUNC(func, n) M_APPLY_FUNC(func, o) \ + M_APPLY_FUNC(func, p) M_APPLY_FUNC(func, q) M_APPLY_FUNC(func, r) M_APPLY_FUNC(func, s) M_APPLY_FUNC(func, t) \ + M_APPLY_FUNC(func, u) M_APPLY_FUNC(func, v) M_APPLY_FUNC(func, w) M_APPLY_FUNC(func, x) M_APPLY_FUNC(func, y) \ + M_APPLY_FUNC(func, z) +#define M_MAP(f, ...) M_MAP_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , ) + +/* Map a macro to all given arguments with one additional fixed data*/ +/* Example: M_MAP2(f,data,a, b, c) ==> f(data,a) f(data,b) f(data,c) */ +#define M_MAP2_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ + M_APPLY_FUNC2(func, data, a) M_APPLY_FUNC2(func, data, b) M_APPLY_FUNC2(func, data, c) \ + M_APPLY_FUNC2(func, data, d) M_APPLY_FUNC2(func, data, e) M_APPLY_FUNC2(func, data, f) \ + M_APPLY_FUNC2(func, data, g) M_APPLY_FUNC2(func, data, h) M_APPLY_FUNC2(func, data, i) \ + M_APPLY_FUNC2(func, data, j) M_APPLY_FUNC2(func, data, k) M_APPLY_FUNC2(func, data, l) \ + M_APPLY_FUNC2(func, data, m) M_APPLY_FUNC2(func, data, n) M_APPLY_FUNC2(func, data, o) \ + M_APPLY_FUNC2(func, data, p) M_APPLY_FUNC2(func, data, q) M_APPLY_FUNC2(func, data, r) \ + M_APPLY_FUNC2(func, data, s) M_APPLY_FUNC2(func, data, t) M_APPLY_FUNC2(func, data, u) \ + M_APPLY_FUNC2(func, data, v) M_APPLY_FUNC2(func, data, w) M_APPLY_FUNC2(func, data, x) \ + M_APPLY_FUNC2(func, data, y) M_APPLY_FUNC2(func, data, z) +#define M_MAP2(f, ...) M_MAP2_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , ) + +/* Duplicate of macros for GET_METHOD as it may be called in context where a M_MAP2 is in progress */ +#define M_APPLY_FUNC2B(func, arg1, arg2) \ + M_IF(M_INV(M_EMPTY_P(arg2)))(func(arg1, arg2),) +#define M_MAP2B_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ + M_APPLY_FUNC2B(func, data, a) M_APPLY_FUNC2B(func, data, b) M_APPLY_FUNC2B(func, data, c) \ + M_APPLY_FUNC2B(func, data, d) M_APPLY_FUNC2B(func, data, e) M_APPLY_FUNC2B(func, data, f) \ + M_APPLY_FUNC2B(func, data, g) M_APPLY_FUNC2B(func, data, h) M_APPLY_FUNC2B(func, data, i) \ + M_APPLY_FUNC2B(func, data, j) M_APPLY_FUNC2B(func, data, k) M_APPLY_FUNC2B(func, data, l) \ + M_APPLY_FUNC2B(func, data, m) M_APPLY_FUNC2B(func, data, n) M_APPLY_FUNC2B(func, data, o) \ + M_APPLY_FUNC2B(func, data, p) M_APPLY_FUNC2B(func, data, q) M_APPLY_FUNC2B(func, data, r) \ + M_APPLY_FUNC2B(func, data, s) M_APPLY_FUNC2B(func, data, t) M_APPLY_FUNC2B(func, data, u) \ + M_APPLY_FUNC2B(func, data, v) M_APPLY_FUNC2B(func, data, w) M_APPLY_FUNC2B(func, data, x) \ + M_APPLY_FUNC2B(func, data, y) M_APPLY_FUNC2B(func, data, z) +#define M_MAP2B(f, ...) M_MAP2B_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , ) + + +/* Map a macro to all given pair of arguments (Using recursivity) */ +/* Example: M_MAP_PAIR(f, a, b, c, d) ==> f(a,b) f(c,d) */ +#define M_MAP_PAIR_L0_INDIRECT() M_MAP_PAIR_L0 +#define M_MAP_PAIR_L0(f, ...) M_IF_NARGS_EQ2(__VA_ARGS__)( f(__VA_ARGS__) , M_MAP_PAIR_L1(f, __VA_ARGS__)) +#define M_MAP_PAIR_L1(f, a, b, ...) f(a,b) M_DELAY3(M_MAP_PAIR_L0_INDIRECT) () (f, __VA_ARGS__) +#define M_MAP_PAIR(f, ...) M_IF_EMPTY(__VA_ARGS__)( /* end */, M_EVAL(M_MAP_PAIR_L0(f, __VA_ARGS__))) + +/* Map a macro to all given arguments and reduce all theses computation + with another reduce macro (using recursivity) */ +/* Example: M_REDUCE(f, g, a, b, c) ==> g( f(a), g( f(b), f(c)) */ +#define M_REDUCE_L0_INDIRECT() M_REDUCE_L0 +#define M_REDUCE_L1(f, g, cur, a, ...) M_DELAY3(M_REDUCE_L0_INDIRECT) () (f, g, g(cur, f(a)), __VA_ARGS__) +#define M_REDUCE_L0(f, g, cur, ...) M_IF_NARGS_EQ1(__VA_ARGS__)( g(cur, f(__VA_ARGS__)) , M_REDUCE_L1(f, g, cur, __VA_ARGS__)) +#define M_REDUCE1(f, g, a, ...) M_EVAL(M_REDUCE_L0(f, g, f(a), __VA_ARGS__)) +#define M_REDUCE(f, g, ...) M_IF_NARGS_EQ1(__VA_ARGS__)( f(__VA_ARGS__), M_REDUCE1(f, g, __VA_ARGS__)) + +/* Map a macro to all given arguments and reduce all theses computation + with another reduce macro with an argument (non-recursive way - should be way faster): + M_REDUCE2(f, g, data, a, b, c) ==> g( f(data,a), g( f(data, b), f(data, c))) */ +#define M_REDUCE2(f,g,d,...) \ + M_RET_ARG27(__VA_ARGS__, M_REDUCEI_26, M_REDUCEI_25, M_REDUCEI_24, \ + M_REDUCEI_23, M_REDUCEI_22, M_REDUCEI_21, M_REDUCEI_20, \ + M_REDUCEI_19, M_REDUCEI_18, M_REDUCEI_17, M_REDUCEI_16, \ + M_REDUCEI_15, M_REDUCEI_14, M_REDUCEI_13, M_REDUCEI_12, \ + M_REDUCEI_11, M_REDUCEI_10, M_REDUCEI_9, M_REDUCEI_8, \ + M_REDUCEI_7, M_REDUCEI_6, M_REDUCEI_5, M_REDUCEI_4, \ + M_REDUCEI_3, M_REDUCEI_2, M_REDUCEI_1, useless)(f,g,d,__VA_ARGS__) +#define M_REDUCEI_1(f1,g1,d1,a) f1(d1, a) +#define M_REDUCEI_2(f1,g1,d1,a,b) g1(f1(d1, a),f1(d1,b)) +#define M_REDUCEI_3(f1,g1,d1,a,b,c) g1(g1(f1(d1, a),f1(d1,b)),f1(d1,c)) +#define M_REDUCEI_4(f1,g1,d1,a,b,c,d) g1(g1(f1(d1, a),f1(d1,b)),g1(f1(d1,c),f1(d1,d))) +#define M_REDUCEI_5(f1,g1,d1,a,b,c,d,e) g1(g1(g1(f1(d1, a),f1(d1,b)),g1(f1(d1,c),f1(d1,d))),f1(d1,e)) +#define M_REDUCEI_6(f1,g1,d1,a,b,c,d,e,f) g1(g1(g1(f1(d1, a),f1(d1,b)),f1(d1,c)),g1(g1(f1(d1,d),f1(d1,e)),f1(d1,f))) +#define M_REDUCEI_7(f1,g1,d1,a,b,c,d,e,f,g) g1(M_REDUCEI_6(f1,g1,d1,a,b,c,d,e,f),f1(d1,g)) +#define M_REDUCEI_8(f1,g1,d1,a,b,c,d,e,f,g,h) g1(M_REDUCEI_4(f1,g1,d1,a,b,c,d),M_REDUCEI_4(f1,g1,d1,e,f,g,h)) +#define M_REDUCEI_9(f1,g1,d1,a,b,c,d,e,f,g,h,i) g1(M_REDUCEI_8(f1,g1,d1,a,b,c,d,e,f,g,h),f1(d1,i)) +#define M_REDUCEI_10(f1,g1,d1,a,b,c,d,e,f,g,h,i,j) g1(M_REDUCEI_5(f1,g1,d1,a,b,c,d,e),M_REDUCEI_5(f1,g1,d1,f,g,h,i,j)) +#define M_REDUCEI_11(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k) g1(M_REDUCEI_10(f1,g1,d1,a,b,c,d,e,f,g,h,i,j),f1(d1,k)) +#define M_REDUCEI_12(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l) g1(M_REDUCEI_6(f1,g1,d1,a,b,c,d,e,f),M_REDUCEI_6(f1,g1,d1,g,h,i,j,k,l)) +#define M_REDUCEI_13(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m) g1(M_REDUCEI_12(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l),f1(d1,m)) +#define M_REDUCEI_14(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n) g1(M_REDUCEI_7(f1,g1,d1,a,b,c,d,e,f,g),M_REDUCEI_7(f1,g1,d1,h,i,j,k,l,m,n)) +#define M_REDUCEI_15(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) g1(M_REDUCEI_14(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n),f1(d1,o)) +#define M_REDUCEI_16(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) g1(M_REDUCEI_8(f1,g1,d1,a,b,c,d,e,f,g,h),M_REDUCEI_8(f1,g1,d1,i,j,k,l,m,n,o,p)) +#define M_REDUCEI_17(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) g1(M_REDUCEI_16(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p),f1(d1,q)) +#define M_REDUCEI_18(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) g1(M_REDUCEI_9(f1,g1,d1,a,b,c,d,e,f,g,h,i),M_REDUCEI_9(f1,g1,d1,j,k,l,m,n,o,p,q,r)) +#define M_REDUCEI_19(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) g1(M_REDUCEI_18(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r),f1(d1,s)) +#define M_REDUCEI_20(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) g1(M_REDUCEI_10(f1,g1,d1,a,b,c,d,e,f,g,h,i,j),M_REDUCEI_9(f1,g1,d1,k,l,m,n,o,p,q,r,s,t)) +#define M_REDUCEI_21(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u) g1(M_REDUCEI_20(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t),f1(d1,u)) +#define M_REDUCEI_22(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v) g1(M_REDUCEI_11(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k),M_REDUCEI_9(f1,g1,d1,l,m,n,o,p,q,r,s,t,u,v)) +#define M_REDUCEI_23(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w) g1(M_REDUCEI_22(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v),f1(d1,w)) +#define M_REDUCEI_24(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x) g1(M_REDUCEI_11(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l),M_REDUCEI_11(f1,g1,d1,m,n,o,p,q,r,s,t,u,v,w,x)) +#define M_REDUCEI_25(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y) g1(M_REDUCEI_24(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x),f1(d1,y)) +#define M_REDUCEI_26(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z) g1(M_REDUCEI_12(f1,g1,d1,a,b,c,d,e,f,g,h,i,j,k,l,m),M_REDUCEI_12(f1,g1,d1,n,o,p,q,r,s,t,u,v,w,x,y,z)) + + +/* Sequence of numerical (using recursivity) */ +/* Example: M_SEQ(init,end, macro)==>macro(init), macro(init+1), ...macro(end) */ +#define M_SEQ_L0_INDIRECT() M_SEQ_L0 +#define M_SEQ_L0(current,end,f) M_IF(M_EQUAL(current, end))( f(current) , M_SEQ_L1(current, end, f)) +#define M_SEQ_L1(current,end,f) f(current) , M_DELAY3(M_SEQ_L0_INDIRECT) () (M_INC(current), end, f) +#define M_SEQ(init,end,f) M_EVAL(M_SEQ_L0(init,end,f)) + +/* Sequence of numerical with data (using recursivity) */ +/* Example: M_SEQ2(init, end, macro, data)==>macro(data, init), macro(data, init+1), ...macro(data, end) */ +#define M_SEQ2_L0_INDIRECT() M_SEQ2_L0 +#define M_SEQ2_L0(current,end,f,d) M_IF(M_EQUAL(current, end))( f(d, current) , M_SEQ2_L1(current, end, f, d)) +#define M_SEQ2_L1(current,end,f,d) f(d,current) , M_DELAY3(M_SEQ2_L0_INDIRECT) () (M_INC(current), end, f, d) +#define M_SEQ2(init,end,f,data) M_EVAL(M_SEQ2_L0(init,end,f,data)) + +/* Return the input (delay evaluation) */ +#define M_ID(...) __VA_ARGS__ + +/* M_NARGS: Return number of argument. + (don't work for empty arg) */ +#define M_NARGS(...) \ + M_RET_ARG27(__VA_ARGS__, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, \ + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, useless) + +/* If the number of arguments is 1 */ +#define M_IF_NARGS_EQ1(...) M_IF(M_EQUAL(M_NARGS(__VA_ARGS__), 1)) + +/* If the number of arguments is 2 */ +#define M_IF_NARGS_EQ2(...) M_IF(M_EQUAL(M_NARGS(__VA_ARGS__), 2)) + +/* If NDEBUG macro is defined + M_IF_DEBUG(code if NDEBUG is not defined) + Note: not 100% robust */ +#define M_TEST_NDEBUG_P() CAT3(M_, NDEBUG, _TEST) +#define M_NDEBUG_TEST 0 +#define M_IF_DEBUG(a) M_IF(M_TEST_NDEBUG_P())(,a) + +/* NOTEQUAL(val1,val2) with val from [0 to 30[ + Return 1 or 0 if val1=val2 + */ +#define M_NOTEQUAL_0_0 0 +#define M_NOTEQUAL_1_1 0 +#define M_NOTEQUAL_2_2 0 +#define M_NOTEQUAL_3_3 0 +#define M_NOTEQUAL_4_4 0 +#define M_NOTEQUAL_5_5 0 +#define M_NOTEQUAL_6_6 0 +#define M_NOTEQUAL_7_7 0 +#define M_NOTEQUAL_8_8 0 +#define M_NOTEQUAL_9_9 0 +#define M_NOTEQUAL_10_10 0 +#define M_NOTEQUAL_11_11 0 +#define M_NOTEQUAL_12_12 0 +#define M_NOTEQUAL_13_13 0 +#define M_NOTEQUAL_14_14 0 +#define M_NOTEQUAL_15_15 0 +#define M_NOTEQUAL_16_16 0 +#define M_NOTEQUAL_17_17 0 +#define M_NOTEQUAL_18_18 0 +#define M_NOTEQUAL_19_19 0 +#define M_NOTEQUAL_20_20 0 +#define M_NOTEQUAL_21_21 0 +#define M_NOTEQUAL_22_22 0 +#define M_NOTEQUAL_23_23 0 +#define M_NOTEQUAL_24_24 0 +#define M_NOTEQUAL_25_25 0 +#define M_NOTEQUAL_26_26 0 +#define M_NOTEQUAL_27_27 0 +#define M_NOTEQUAL_28_28 0 +#define M_NOTEQUAL_29_29 0 +#define M_NOTEQUAL(x,y) M_BOOL(CAT4(M_NOTEQUAL_, x, _, y)) + +/* ADD: Through recursivity. + Example: ADD(4,5) --> 9 */ +#define M_ADD_L0_INDIRECT() M_ADD_L0 +#define M_ADD_L1(x,y) M_DELAY3(M_ADD_L0_INDIRECT) () (M_INC(x), M_DEC(y)) +#define M_ADD_L0(x,y) M_IF(M_BOOL(y)) (M_ADD_L1(x,y) , x) +#define M_ADD(x,y) M_EVAL(M_ADD_L0(x,y)) + +/* SUB: Through recursivity + Example: SUB(15,6) --> 11 */ +#define M_SUB_L0_INDIRECT() M_SUB_L0 +#define M_SUB_L1(x,y) M_DELAY3(M_SUB_L0_INDIRECT) () (M_DEC(x), M_DEC(y)) +#define M_SUB_L0(x,y) M_IF(M_BOOL(y)) (M_SUB_L1(x,y) , x) +#define M_SUB(x,y) M_EVAL(M_SUB_L0(x,y)) + +/* EQUAL(val1,val2) with valN from [0 to 30[ + Return 1 if val1=val2 or 0 + Example: M_EQUAL(1,2) --> 0 + */ +#define M_EQUAL(x,y) M_INV(M_NOTEQUAL(x,y)) + +/* Revert the list given as parameter + Example: M_INVERT(a,b,c) --> (c,b,a) */ +/* Generated by: + for i in $(seq 1 26) ; do printf "#define M_INVERT_%d(" $i ; for j in $(seq 1 $i) ; do printf "_%d" $j ; if test $i -ne $j ; then printf "," ; fi ; done; printf ") (" ; for j in $(seq $i -1 1) ; do printf "_%d" $j ; if test 1 -ne $j ; then printf "," ; fi ; done ; printf ")\n" ; done */ +#define M_INVERT_0() () +#define M_INVERT_1(_1) (_1) +#define M_INVERT_2(_1,_2) (_2,_1) +#define M_INVERT_3(_1,_2,_3) (_3,_2,_1) +#define M_INVERT_4(_1,_2,_3,_4) (_4,_3,_2,_1) +#define M_INVERT_5(_1,_2,_3,_4,_5) (_5,_4,_3,_2,_1) +#define M_INVERT_6(_1,_2,_3,_4,_5,_6) (_6,_5,_4,_3,_2,_1) +#define M_INVERT_7(_1,_2,_3,_4,_5,_6,_7) (_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_8(_1,_2,_3,_4,_5,_6,_7,_8) (_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_9(_1,_2,_3,_4,_5,_6,_7,_8,_9) (_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_10(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10) (_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_11(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11) (_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_12(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12) (_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_13(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13) (_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_14(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14)\ + (_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_15(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15)\ + (_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_16(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16)\ + (_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_17(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17)\ + (_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_18(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18)\ + (_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_19(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19)\ + (_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_20(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20)\ + (_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_21(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21)\ + (_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_22(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22)\ + (_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_23(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23)\ + (_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_24(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24)\ + (_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_25(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25)\ + (_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT_26(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26)\ + (_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) +#define M_INVERT(...) CAT(M_INVERT_, M_NARGS(__VA_ARGS__))(__VA_ARGS__) + + +/***************************************************************/ +/******************** Compile Times Macro **********************/ +/***************************************************************/ + +/* C11 MACROS */ + +/* Return the string format of a variable */ +#define M_PRINTF_FORMAT(x) \ + _Generic(((void)0,x), \ + char: "%c", \ + signed char: "%hhd", \ + unsigned char: "%hhu", \ + signed short: "%hd", \ + unsigned short: "%hu", \ + signed int: "%d", \ + unsigned int: "%u", \ + long int: "%ld", \ + unsigned long int: "%lu", \ + long long int: "%lld", \ + unsigned long long int: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%Lf", \ + const char *: "%s", \ + char *: "%s", \ + const void *: "%p", \ + void *: "%p") + +/* Print a C variable if it is a standard type to stdout. + Note: final ';' is needed to be called within a M_MAP */ +#define M_PRINT_ARG(x) printf(M_PRINTF_FORMAT(x), x); + +/* Print a C variable if it is a standard type to the given file 'f' + Note: final ';' is needed to be called within a M_MAP2 */ +#define M_FPRINT_ARG(f, x) fprintf(f, M_PRINTF_FORMAT(x), x); + +/* Generic PRINT macro: print all its inputs regardless of the type + provided it is a generic "non-struct" type. */ +#define M_PRINT(...) do { M_MAP(M_PRINT_ARG, __VA_ARGS__) } while (0) + +/* Generic FPRINT macro: print all its inputs regardless of the type + provided it is a generic "non-struct" type into the file 'f'. */ +#define M_FPRINT(f,...) do { M_MAP2(M_FPRINT_ARG, f, __VA_ARGS__) } while (0) + +/* Return the minimum between x and y (computed in compile time) */ +#define M_MIN(x, y) ((x) < (y) ? (x) : (y)) + +/* Return the maximum between x and y (computed in compile time) */ +#define M_MAX(x, y) ((x) > (y) ? (x) : (y)) + +/* Is the number a power of 2? (computed in compile time) */ +#define M_POWEROF2_P(n) (!((n)&((n)-1))) + + +/************************************************************/ +/******************** METHODS handling **********************/ +/************************************************************/ + +/* List of supported methods */ +#define M_INIT_INIT(a) ,a, +#define M_INIT_SET_INIT_SET(a) ,a, +#define M_INIT_MOVE_INIT_MOVE(a) ,a, +#define M_SET_SET(a) ,a, +#define M_MOVE_MOVE(a) ,a, +#define M_CLEAR_CLEAR(a) ,a, +#define M_DEL_DEL(a) ,a, +#define M_HASH_HASH(a) ,a, +#define M_EQUAL_EQUAL(a) ,a, +#define M_CMP_CMP(a) ,a, +#define M_TYPE_TYPE(a) ,a, +#define M_SUBTYPE_SUBTYPE(a) ,a, +#define M_OPLIST_OPLIST(a) ,a, +#define M_SORT_SORT(a) ,a, +#define M_UPDATE_UPDATE(a) ,a, +#define M_IT_TYPE_IT_TYPE(a) ,a, +#define M_IT_FIRST_IT_FIRST(a) ,a, +#define M_IT_LAST_IT_LAST(a) ,a, +#define M_IT_SET_IT_SET(a) ,a, +#define M_IT_END_P_IT_END_P(a) ,a, +#define M_IT_LAST_P_IT_LAST_P(a) ,a, +#define M_IT_EQUAL_P_IT_EQUAL_P(a) ,a, +#define M_IT_NEXT_IT_NEXT(a) ,a, +#define M_IT_PREVIOUS_IT_PREVIOUS(a) ,a, +#define M_IT_REF_IT_REF(a) ,a, +#define M_IT_CREF_IT_CREF(a) ,a, +#define M_ADD_ADD(a) ,a, +#define M_SUB_SUB(a) ,a, +#define M_MUL_MUL(a) ,a, +#define M_DIV_DIV(a) ,a, +#define M_CLEAN_CLEAN(a) ,a, +#define M_PUSH_PUSH(a) ,a, +#define M_POP_POP(a) ,a, +#define M_REVERSE_REVERSE(a) ,a, +#define M_GET_STR_GET_STR(a) ,a, +#define M_OUT_STR_OUT_STR(a) ,a, +#define M_SET_STR_SET_STR(a) ,a, +#define M_IN_STR_IN_STR(a) ,a, +#define M_SEPARATOR_SEPARATOR(a) ,a, +#define M_EXT_ALGO_EXT_ALGO(a) ,a, + +/* From an oplist - an unorded list of methods : like "INIT(mpz_init),CLEAR(mpz_clear),SET(mpz_set)" - + Return the given method in the oplist or the default method. + Example: + M_GET_METHOD(INIT, my_default, INIT(mpz_init),CLEAR(mpz_clear),SET(mpz_set)) --> mpz_init + M_GET_METHOD(INIT, my_default, CLEAR(mpz_clear),SET(mpz_set)) --> my_default */ +#define M_GET_METHOD(method, method_default, ...) \ + M_RET_ARG2 (M_MAP2B(CAT, CAT3(M_, method, _), __VA_ARGS__), method_default,) + +/* Get the given method */ +#define M_GET_INIT(...) M_GET_METHOD(INIT, M_INIT_DEFAULT, __VA_ARGS__) +#define M_GET_INIT_SET(...) M_GET_METHOD(INIT_SET, M_SET_DEFAULT, __VA_ARGS__) +#define M_GET_INIT_MOVE(...) M_GET_METHOD(INIT_MOVE, M_MOVE_DEFAULT, __VA_ARGS__) +#define M_GET_SET(...) M_GET_METHOD(SET, M_SET_DEFAULT, __VA_ARGS__) +#define M_GET_MOVE(...) M_GET_METHOD(MOVE, M_MOVE_DEFAULT, __VA_ARGS__) +#define M_GET_CLEAR(...) M_GET_METHOD(CLEAR, M_CLEAR_DEFAULT, __VA_ARGS__) +#define M_GET_DEL(...) M_GET_METHOD(DEL, M_DEL_DEFAULT, __VA_ARGS__) +#define M_GET_HASH(...) M_GET_METHOD(HASH, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_EQUAL(...) M_GET_METHOD(EQUAL, M_EQUAL_DEFAULT, __VA_ARGS__) +#define M_GET_CMP(...) M_GET_METHOD(CMP, M_CMP_DEFAULT, __VA_ARGS__) +#define M_GET_UPDATE(...) M_GET_METHOD(UPATE, M_MEMCPY_DEFAULT, __VA_ARGS__) +#define M_GET_TYPE(...) M_GET_METHOD(TYPE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SUBTYPE(...) M_GET_METHOD(SUBTYPE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_OPLIST(...) M_GET_METHOD(OPLIST, (), __VA_ARGS__) +#define M_GET_SORT(...) M_GET_METHOD(SORT, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_TYPE(...) M_GET_METHOD(IT_TYPE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_FIRST(...) M_GET_METHOD(IT_FIRST, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_LAST(...) M_GET_METHOD(IT_LAST, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_SET(...) M_GET_METHOD(IT_SET, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_END_P(...) M_GET_METHOD(IT_END_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_LAST_P(...) M_GET_METHOD(IT_LAST_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_EQUAL_P(...) M_GET_METHOD(IT_EQUAL_P, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_NEXT(...) M_GET_METHOD(IT_NEXT, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_PREVIOUS(...) M_GET_METHOD(IT_PREVIOUS, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_REF(...) M_GET_METHOD(IT_REF, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_IT_CREF(...) M_GET_METHOD(IT_CREF, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_ADD(...) M_GET_METHOD(ADD, M_ADD_DEFAULT, __VA_ARGS__) +#define M_GET_SUB(...) M_GET_METHOD(SUB, M_SUB_DEFAULT, __VA_ARGS__) +#define M_GET_MUL(...) M_GET_METHOD(MUL, M_MUL_DEFAULT, __VA_ARGS__) +#define M_GET_DIV(...) M_GET_METHOD(DIV, M_DIV_DEFAULT, __VA_ARGS__) +#define M_GET_CLEAN(...) M_GET_METHOD(CLEAN, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_PUSH(...) M_GET_METHOD(PUSH, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_POP(...) M_GET_METHOD(POP, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_REVERSE(...) M_GET_METHOD(REVERSE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_GET_STR(...) M_GET_METHOD(GET_STR, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_OUT_STR(...) M_GET_METHOD(OUT_STR, M_FPRINT_ARG, __VA_ARGS__) +#define M_GET_IN_STR(...) M_GET_METHOD(IN_STR, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_SEPARATOR(...) M_GET_METHOD(SEPARATOR, ',', __VA_ARGS__) +#define M_GET_EXT_ALGO(...) M_GET_METHOD(EXT_ALGO, M_NO_EXT_ALGO, __VA_ARGS__) + +/* Define the default method */ +#define M_INIT_DEFAULT(a) ((a) = 0) +#define M_MEMSET_DEFAULT(a) (memset(&(a), 0, sizeof (a))) +#define M_SET_DEFAULT(a,b) ((a) = (b)) +#define M_MOVE_DEFAULT(a,b) (M_MEMCPY_DEFAULT(a, b), M_MEMSET_DEFAULT(b)) +#define M_MEMCPY_DEFAULT(a,b) (memcpy(&(a), &(b), sizeof (a))) +#define M_CLEAR_DEFAULT(a) (void)a +#define M_DEL_DEFAULT(a) free(a) +#define M_NO_DEFAULT(...) m_no_default_function +#define M_EQUAL_DEFAULT(a,b) ((a) == (b)) +#define M_CMP_DEFAULT(a,b) ((a) < (b) ? -1 : (a) > (b)) +#define M_ADD_DEFAULT(a,b,c) ((a) = (b) + (c)) +#define M_SUB_DEFAULT(a,b,c) ((a) = (b) - (c)) +#define M_MUL_DEFAULT(a,b,c) ((a) = (b) * (c)) +#define M_DIV_DEFAULT(a,b,c) ((a) = (b) / (c)) +#define M_NO_EXT_ALGO(n,co,to) + +/* Default hash: perform a hash of the memory pattern of the object */ +// TODO: Better hash function? +static inline size_t +m_core_hash(const void *ptr, size_t s) +{ + const unsigned char *p = (const unsigned char *) ptr; + size_t hash = 0; + for(size_t i = 0 ; i < s; i++) { + unsigned int c = p[i]; + hash = hash * 31421 + c + 6297; + } + return hash; +} +#define M_HASH_DEFAULT(a) m_core_hash(&(a), sizeof (a)) + +#define M_DEFAULT_OPLIST \ + (INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), \ + CLEAR(M_CLEAR_DEFAULT), EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \ + INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \ + HASH(M_HASH_DEFAULT) \ + /*ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), MUL(M_MUL_DEFAULT),*/ \ + /*DIV(M_DIV_DEFAULT)*/ ) + +/* From an oplist (...) return ... */ +#define M_OPLIST_FLAT(...) __VA_ARGS__ + +/* Concat two oplists in one. op1 will have higher priority to op2 */ +#define M_OPLIST_CAT(op1,op2) (M_OPLIST_FLAT op1, M_OPLIST_FLAT op2) + +/* Appl an oplist */ +#define M_OPLIST_APPLY(a, oplist) a oplist + +/* Test if a method is present in an oplist. + Return 0 or 1 */ +#define M_TEST_METHOD_P(method, oplist) \ + M_BOOL(M_GET_METHOD (method, 0, M_OPLIST_FLAT oplist)) + +/* Perfom a preprocessing M_IF, if the method is present in the oplist. + Example: M_IF_METHOD(HASH, oplist)(define function with HASH method, ) */ +#define M_IF_METHOD(method, oplist) M_IF(M_TEST_METHOD_P(method, oplist)) + +/* Perform a preprocessing M_IF if the method exists in both oplist. + Example: M_IF_METHOD_BOTH(HASH, oplist1, oplist2) (define function with HASH method, ) */ +#define M_IF_METHOD_BOTH(method, oplist1, oplist2) \ + M_IF(M_AND(M_TEST_METHOD_P(method, oplist1), M_TEST_METHOD_P(method, oplist2))) + +/* Perform a preprocessing M_IF if the method exists for all oplist. + Example: M_IF_METHOD_ALL(HASH, oplist1, oplist2) (define function with HASH method, ) */ +#define M_IF_METHOD_ALL(method, ...) \ + M_IF(M_REDUCE2(M_TEST_METHOD_P, M_AND, method, __VA_ARGS__)) + +/* Define M_EACH macro allowing to iterate over a container + First argument will be a created pointer to the underlying type. + Example: for M_EACH(item, list, LIST_OPLIST) { action; } */ +#define M_EACH(item, container, oplist) \ + M_EACHI(item, container, oplist, CAT(local_iterator_, __LINE__), \ + CAT(local_cont_, __LINE__)) + +/* Internal for M_EACH */ +#define M_EACHI(item,container,oplist, iterator, cont) \ + (bool cont = true; cont; cont = false) \ + for(M_GET_SUBTYPE oplist *item; cont ; cont = false) \ + for(M_GET_IT_TYPE oplist iterator; cont ; cont = false) \ + for(M_GET_IT_FIRST oplist (iterator, container) ; \ + !M_GET_IT_END_P oplist (iterator) && (item = M_GET_IT_REF oplist (iterator), true) ; \ + M_GET_IT_NEXT oplist (iterator)) + +/* Define M_LET macro allowing to define, auto init & auto clear an object + within the next bracket. + Example: + M_LET(a, STRING_OPLIST) { do something with a } or + M_LET(a, b, c, STRING_OPLIST) { do something with a, b & c } */ +#define M_LET(...) \ + M_ID(M_LETI M_INVERT( __VA_ARGS__, CAT(local_cont_, __LINE__) )) + +#define M_LETI(cont, oplist, ...) \ + for(bool cont = true; cont ; cont = false) \ + M_MAP2(M_LETI_SINGLE, (cont, oplist,), __VA_ARGS__) + +#define M_LETI_SINGLE(data, name) \ + M_LETI_SINGLE2(M_RET_ARG1 data, M_RET_ARG2 data, name) + +#define M_LETI_SINGLE2(cont, oplist, name) \ + for(M_GET_TYPE oplist name; \ + (M_GET_INIT oplist (name), cont); \ + (M_GET_CLEAR oplist (name), cont = false)) + +/* Check if 'n' is assignable to an object of type 'type' + Return the object. */ +#define M_ASSIGN_CAST(type, n) \ + ((type) { 0 } = (n)) + + +/* By putting this after a method, we transform the argument list + so that the first argument becomes a pointer to the destination. */ +#define M_IPTR(...) ( & __VA_ARGS__ ) + + +/************************************************************/ +/********************* MEMORY handling **********************/ +/************************************************************/ + +#ifndef M_MEMORY_ALLOC +#ifdef __cplusplus +# include +# define M_MEMORY_ALLOC(type) ((type*)std::malloc (sizeof (type))) +# define M_MEMORY_REALLOC(type, ptr, n) \ + ((type*) std::realloc ((ptr), (n)*sizeof (type))) +# define M_MEMORY_FREE(ptr) std::free(ptr) +#else +# include +# define M_MEMORY_ALLOC(type) malloc (sizeof (type)) +# define M_MEMORY_REALLOC(type, ptr, n) realloc ((ptr), (n)*sizeof (type)) +# define M_MEMORY_FREE(ptr) free(ptr) +#endif +#endif + +// Basic ERROR handling macros. +// Note: Can be overloaded by used code to: +// * throw an exception if needed. +// * set a global error variable and return +// * abort +#ifndef M_MEMORY_FULL +#include +#include +#define M_MEMORY_FULL(size) do { \ + fprintf(stderr, "ERROR: Can not allocate memory (%lu bytes).\n", \ + (unsigned long) size); \ + abort(); \ + } while (0) +#endif + +#ifndef M_INIT_FAILURE +#include +#include +#define M_INIT_FAILURE() do { \ + fprintf(stderr, "ERROR: Can not initialize data.\n"); \ + abort(); \ + } while (0) +#endif + +#ifndef M_ASSERT_INIT +#define M_ASSERT_INIT(expr) do { \ + if (!(expr)) { \ + fprintf(stderr, "ERROR: Can not initialize data: %s\n", #expr); \ + abort(); \ + } } while (0) +#endif + +#endif diff --git a/m-dict.h b/m-dict.h new file mode 100644 index 00000000..c66d8e83 --- /dev/null +++ b/m-dict.h @@ -0,0 +1,468 @@ +/* + * MLIB - DICTIONARY Module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_DICT_H +#define __M_DICT_H + +#include "m-list.h" +#include "m-array.h" +#include "m-string.h" +#include "m-tuple.h" + +/* Define a dictionnary with the key key_type to the value value_type. */ +#define DICT_DEF2(name, key_type, key_oplist, value_type, value_oplist) \ + TUPLE_DEF2(CAT(dict_pair_, name), (key, key_type, key_oplist), (value, value_type, value_oplist)) \ + LIST_DEF(CAT(dict_pair_, name), CAT3(dict_pair_,name,_t), TUPLE_OPLIST(CAT(dict_pair_, name), key_oplist, value_oplist)) \ + ARRAY_DEF(CAT(list_dict_pair_,name), CAT3(list_dict_pair_,name,_t), LIST_OPLIST(CAT(dict_pair_, name))) \ + \ + typedef struct CAT3(dict_, name, _s) { \ + size_t used, lower_limit, upper_limit; \ + CAT3(array_list_dict_pair_,name,_t) table; \ + } CAT3(dict_, name, _t)[1]; \ + \ + typedef struct CAT3(dict_it_, name, _s) { \ + CAT3(array_it_list_dict_pair_, name, _t) array_it; \ + CAT3(list_it_dict_pair_, name, _t) list_it; \ + } CAT3(dict_it_, name, _t)[1]; \ + \ + static inline void \ + CAT3(dict_, name, _init)(CAT3(dict_,name,_t) map) \ + { \ + map->used = 0; \ + CAT3(array_list_dict_pair_,name,_init)(map->table); \ + CAT3(array_list_dict_pair_,name,_resize)(map->table, DICTI_INITIAL_SIZE); \ + map->lower_limit = DICTI_LOWER_BOUND(DICTI_INITIAL_SIZE); \ + map->upper_limit = DICTI_UPPER_BOUND(DICTI_INITIAL_SIZE); \ + } \ + \ + static inline void \ + CAT3(dict_, name, _init_set)(CAT3(dict_,name,_t) map, \ + const CAT3(dict_,name,_t) org) \ + { \ + DICTI_CONTRACT(name, org); \ + map->used = org->used; \ + map->lower_limit = org->lower_limit; \ + map->upper_limit = org->upper_limit; \ + CAT3(array_list_dict_pair_,name,_init_set)(map->table, org->table); \ + } \ + \ + static inline void \ + CAT3(dict_, name, _set)(CAT3(dict_,name,_t) map, \ + const CAT3(dict_,name,_t) org) \ + { \ + DICTI_CONTRACT(name, map); \ + DICTI_CONTRACT(name, org); \ + map->used = org->used; \ + map->lower_limit = org->lower_limit; \ + map->upper_limit = org->upper_limit; \ + CAT3(array_list_dict_pair_,name,_set)(map->table, org->table); \ + } \ + \ + static inline void \ + CAT3(dict_,name,_clear)(CAT3(dict_,name,_t) map) \ + { \ + CAT3(array_list_dict_pair_,name,_clear)(map->table); \ + } \ + \ + static inline void \ + CAT3(dict_, name, _init_move)(CAT3(dict_,name,_t) map, \ + CAT3(dict_,name,_t) org) \ + { \ + DICTI_CONTRACT(name, org); \ + map->used = org->used; \ + map->lower_limit = org->lower_limit; \ + map->upper_limit = org->upper_limit; \ + CAT3(array_list_dict_pair_,name,_init_move)(map->table, org->table); \ + } \ + \ + static inline void \ + CAT3(dict_, name, _move)(CAT3(dict_,name,_t) map, \ + CAT3(dict_,name,_t) org) \ + { \ + DICTI_CONTRACT(name, map); \ + DICTI_CONTRACT(name, org); \ + assert (map != org); \ + CAT3(dict_,name,_clear)(map); \ + CAT3(dict_,name,_init_move)(map, org); \ + } \ + \ + static inline void \ + CAT3(dict_,name,_clean)(CAT3(dict_,name,_t) map) \ + { \ + CAT3(array_list_dict_pair_,name,_clean)(map->table); \ + CAT3(array_list_dict_pair_,name,_resize)(map->table, DICTI_INITIAL_SIZE); \ + map->lower_limit = DICTI_LOWER_BOUND(DICTI_INITIAL_SIZE); \ + map->upper_limit = DICTI_UPPER_BOUND(DICTI_INITIAL_SIZE); \ + map->used = 0; \ + } \ + \ + static inline size_t \ + CAT3(dict_,name,_size)(const CAT3(dict_,name,_t) map) \ + { \ + DICTI_CONTRACT(name, map); \ + return map->used; \ + } \ + \ + static inline value_type * \ + CAT3(dict_,name,_get)(const CAT3(dict_,name,_t) map, const key_type key) \ + { \ + DICTI_CONTRACT(name, map); \ + size_t hash = M_GET_HASH key_oplist (key); \ + hash = hash & (CAT3(array_list_dict_pair_,name,_size)(map->table) - 1); \ + const CAT3(list_dict_pair_,name,_t) *list_ptr = \ + CAT3(array_list_dict_pair_,name,_cget)(map->table, hash); \ + CAT3(list_it_dict_pair_,name,_t) it; \ + for(CAT3(list_dict_pair_,name,_it)(it, *list_ptr); \ + !CAT3(list_dict_pair_,name,_end_p)(it); \ + CAT3(list_dict_pair_,name,_next)(it)) { \ + CAT3(dict_pair_,name,_t) *ref = CAT3(list_dict_pair_,name,_ref)(it); \ + if (M_GET_EQUAL key_oplist ((*ref)->key, key)) \ + return &(*ref)->value; \ + } \ + return NULL; \ + } \ + \ + static inline void \ + CAT3(dicti_,name,_resize_up)(CAT3(dict_,name,_t) map) \ + { \ + size_t old_size = CAT3(array_list_dict_pair_,name,_size)(map->table); \ + size_t new_size = old_size * 2; \ + /* Resize the dictionnary */ \ + CAT3(array_list_dict_pair_,name,_resize)(map->table, new_size); \ + /* Move the items to the new upper part */ \ + for(size_t i = 0; i < old_size; i++) { \ + CAT3(list_dict_pair_,name,_t) *list = \ + CAT3(array_list_dict_pair_,name,_get)(map->table, i); \ + if (CAT3(list_dict_pair_,name,_empty_p)(*list)) \ + continue; \ + CAT3(list_it_dict_pair_,name,_t) it; \ + CAT3(list_dict_pair_,name,_it)(it, *list); \ + while (!CAT3(list_dict_pair_,name,_end_p)(it)) { \ + CAT3(dict_pair_,name,_ptr) pair = *CAT3(list_dict_pair_,name,_ref)(it); \ + size_t hash = M_GET_HASH key_oplist (pair->key); \ + if ((hash & (new_size-1)) >= old_size) { \ + assert( (hash & (new_size-1)) == (i + old_size)); \ + CAT3(list_dict_pair_,name,_t) *new_list = \ + CAT3(array_list_dict_pair_,name,_get)(map->table, i + old_size); \ + CAT3(list_dict_pair_,name,_move_back)(*new_list, *list, it); \ + } else { \ + CAT3(list_dict_pair_,name,_next)(it); \ + } \ + } \ + } \ + map->upper_limit = DICTI_UPPER_BOUND(new_size); \ + map->lower_limit = DICTI_LOWER_BOUND(new_size); \ + } \ + \ + static inline void \ + CAT3(dicti_,name,_resize_down)(CAT3(dict_,name,_t) map) \ + { \ + assert (map != NULL); \ + size_t old_size = CAT3(array_list_dict_pair_,name,_size)(map->table); \ + assert ((old_size % 2) == 0); \ + size_t new_size = old_size / 2; \ + assert (new_size >= DICTI_INITIAL_SIZE); \ + /* Move all items to the lower part of the dictionnary */ \ + for(size_t i = new_size; i < old_size; i++) { \ + CAT3(list_dict_pair_,name,_t) *list = \ + CAT3(array_list_dict_pair_,name,_get)(map->table, i); \ + if (CAT3(list_dict_pair_,name,_empty_p)(*list)) \ + continue; \ + CAT3(list_it_dict_pair_,name,_t) it; \ + CAT3(list_dict_pair_,name,_it)(it, *list); \ + while (!CAT3(list_dict_pair_,name,_end_p)(it)) { \ + M_IF_DEBUG(CAT3(dict_pair_,name,_ptr) pair = *CAT3(list_dict_pair_,name,_ref)(it);) \ + M_IF_DEBUG(size_t hash = M_GET_HASH key_oplist (pair->key);) \ + assert ((hash & (old_size -1)) == i); \ + assert ((hash & (new_size -1)) == i - new_size); \ + CAT3(list_dict_pair_,name,_t) *new_list = \ + CAT3(array_list_dict_pair_,name,_get)(map->table, i - new_size); \ + CAT3(list_dict_pair_,name,_move_back)(*new_list, *list, it); \ + } \ + } \ + /* Resize the dictionary */ \ + CAT3(array_list_dict_pair_,name,_resize)(map->table, new_size); \ + map->upper_limit = DICTI_UPPER_BOUND(new_size); \ + map->lower_limit = DICTI_LOWER_BOUND(new_size); \ + } \ + \ + static inline void \ + CAT3(dict_,name,_set_at)(CAT3(dict_,name,_t) map, \ + const key_type key, const value_type value) \ + { \ + DICTI_CONTRACT(name, map); \ + \ + size_t hash = M_GET_HASH key_oplist(key); \ + hash = hash & (CAT3(array_list_dict_pair_,name,_size)(map->table) - 1); \ + CAT3(list_dict_pair_,name,_t) *list_ptr = \ + CAT3(array_list_dict_pair_,name,_get)(map->table, hash); \ + CAT3(list_it_dict_pair_,name,_t) it; \ + for(CAT3(list_dict_pair_,name,_it)(it, *list_ptr); \ + !CAT3(list_dict_pair_,name,_end_p)(it); \ + CAT3(list_dict_pair_,name,_next)(it)) { \ + CAT3(dict_pair_,name,_ptr) ref = *CAT3(list_dict_pair_,name,_ref)(it); \ + if (M_GET_EQUAL key_oplist(ref->key, key)) { \ + M_GET_SET value_oplist(ref->value, value); \ + return; \ + } \ + } \ + CAT3(dict_pair_,name,_init_set2)(*CAT3(list_dict_pair_,name,_push_raw)(*list_ptr), \ + key, value); \ + map->used ++; \ + if (map->used > map->upper_limit) \ + CAT3(dicti_,name,_resize_up)(map); \ + } \ + \ + static inline void \ + CAT3(dict_,name,_remove)(CAT3(dict_,name,_t) map, const key_type key) \ + { \ + DICTI_CONTRACT(name, map); \ + \ + size_t hash = M_GET_HASH key_oplist(key); \ + hash = hash & (CAT3(array_list_dict_pair_,name,_size)(map->table) - 1); \ + CAT3(list_dict_pair_,name,_t) *list_ptr = \ + CAT3(array_list_dict_pair_,name,_get)(map->table, hash); \ + CAT3(list_it_dict_pair_,name,_t) it; \ + for(CAT3(list_dict_pair_,name,_it)(it, *list_ptr); \ + !CAT3(list_dict_pair_,name,_end_p)(it); \ + CAT3(list_dict_pair_,name,_next)(it)) { \ + CAT3(dict_pair_,name,_ptr) ref = *CAT3(list_dict_pair_,name,_ref)(it); \ + if (M_GET_EQUAL key_oplist(ref->key, key)) { \ + CAT3(list_dict_pair_,name,_remove)(*list_ptr, it); \ + map->used --; \ + break; \ + } \ + } \ + if (map->used < map->lower_limit) \ + CAT3(dicti_, name, _resize_down)(map); \ + } \ + \ + static inline void \ + CAT3(dict_, name, _it)(CAT3(dict_it_, name,_t) it, \ + CAT3(dict_, name,_t) d) \ + { \ + CAT3(array_list_dict_pair_, name, _it)(it->array_it, d->table); \ + CAT3(list_dict_pair_,name,_t) *ref = \ + CAT3(array_list_dict_pair_, name, _ref)(it->array_it); \ + CAT3(list_dict_pair_, name, _it)(it->list_it, *ref); \ + while (CAT3(list_dict_pair_, name, _end_p)(it->list_it)) { \ + CAT3(array_list_dict_pair_, name, _next)(it->array_it); \ + if (CAT3(array_list_dict_pair_, name, _end_p)(it->array_it))break; \ + ref = CAT3(array_list_dict_pair_, name, _ref)(it->array_it); \ + CAT3(list_dict_pair_, name, _it)(it->list_it, *ref); \ + } \ + } \ + \ + static inline void \ + CAT3(dict_, name, _it_set)(CAT3(dict_it_, name,_t) it, \ + const CAT3(dict_it_, name,_t) ref) \ + { \ + assert (it != NULL && ref != NULL); \ + CAT3(array_list_dict_pair_, name, _it_set)(it->array_it, \ + ref->array_it); \ + CAT3(list_dict_pair_, name, _it_set)(it->list_it, ref->list_it); \ + } \ + \ + static inline bool \ + CAT3(dict_, name, _end_p)(const CAT3(dict_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + return CAT3(list_dict_pair_, name, _end_p)(it->list_it); \ + } \ + \ + static inline void \ + CAT3(dict_, name, _next)(CAT3(dict_it_, name,_t) it) \ + { \ + assert(it != NULL); \ + CAT3(list_dict_pair_, name, _next)(it->list_it); \ + CAT3(list_dict_pair_,name,_t) *ref; \ + while (CAT3(list_dict_pair_, name, _end_p)(it->list_it)) { \ + CAT3(array_list_dict_pair_, name, _next)(it->array_it); \ + if (CAT3(array_list_dict_pair_, name, _end_p)(it->array_it))break; \ + ref = CAT3(array_list_dict_pair_, name, _ref)(it->array_it); \ + CAT3(list_dict_pair_, name, _it)(it->list_it, *ref); \ + } \ + } \ + \ + static inline bool \ + CAT3(dict_, name, _last_p)(const CAT3(dict_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + CAT3(dict_it_, name,_t) it2; \ + CAT3(dict_, name,_it_set)(it2, it); \ + CAT3(dict_, name, _next)(it2); \ + return CAT3(dict_, name, _end_p)(it2); \ + } \ + \ + static inline CAT3(dict_pair_,name,_t) * \ + CAT3(dict_, name, _ref)(CAT3(dict_it_, name,_t) it) \ + { \ + assert(it != NULL); \ + return CAT3(list_dict_pair_, name, _ref)(it->list_it); \ + } \ + \ + static inline const CAT3(dict_pair_,name,_t) * \ + CAT3(dict_, name, _cref)(const CAT3(dict_it_, name,_t) it) \ + { \ + assert(it != NULL); \ + return CAT3(list_dict_pair_, name, _cref)(it->list_it); \ + } \ + \ + M_IF_METHOD(EQUAL, value_oplist)( \ + static inline bool \ + CAT3(dict_, name, _equal_p)(CAT3(dict_, name,_t) dict1, \ + CAT3(dict_, name,_t) dict2) \ + { \ + assert (dict1 != NULL && dict2 != NULL); \ + /* NOTE: Key type has mandatory equal operator */ \ + return dict1->used == dict2-> used && \ + CAT3(array_list_dict_pair_, name, _equal_p)(dict1->table, \ + dict2->table); \ + } \ + , /* no value equal */ ) \ + \ + M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \ + static inline void \ + CAT3(dict_, name, _get_str)(string_t str, \ + CAT3(dict_, name,_t) dict, \ + const bool append) \ + { \ + assert (str != NULL && dict != NULL); \ + DICTI_CONTRACT(name, dict); \ + (append ? string_cat_str : string_set_str) (str, "{"); \ + CAT3(dict_it_, name, _t) it; \ + for (CAT3(dict_, name, _it)(it, dict) ; \ + !CAT3(dict_, name, _end_p)(it); \ + CAT3(dict_, name, _next)(it)){ \ + const CAT3(dict_pair_,name,_t) *item = \ + CAT3(dict_, name, _cref)(it); \ + M_GET_GET_STR key_oplist (str, (*item)->key, true); \ + string_push_back (str, ':'); \ + M_GET_GET_STR value_oplist (str, (*item)->value, true); \ + if (!CAT3(dict_, name, _last_p)(it)) \ + string_push_back (str, ','); \ + } \ + string_push_back (str, '}'); \ + } \ + , /* no GET_STR */ ) \ + \ + M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)( \ + static inline void \ + CAT3(dict_, name, _out_str)(FILE *file, \ + CAT3(dict_, name,_t) dict) \ + { \ + assert (file != NULL && dict != NULL); \ + DICTI_CONTRACT(name, dict); \ + fputc ('{', file); \ + CAT3(dict_it_, name, _t) it; \ + for (CAT3(dict_, name, _it)(it, dict) ; \ + !CAT3(dict_, name, _end_p)(it); \ + CAT3(dict_, name, _next)(it)){ \ + const CAT3(dict_pair_,name,_t) *item = \ + CAT3(dict_, name, _cref)(it); \ + M_GET_OUT_STR key_oplist (file, (*item)->key); \ + fputc (':', file); \ + M_GET_OUT_STR value_oplist (file, (*item)->value); \ + if (!CAT3(dict_, name, _last_p)(it)) \ + fputc (',', file); \ + } \ + fputc ('}', file); \ + } \ + , /* no OUT_STR */ ) \ + \ + M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \ + static inline bool \ + CAT3(dict_, name, _in_str)(CAT3(dict_, name,_t) dict, FILE *file) \ + { \ + assert (file != NULL && dict != NULL); \ + DICTI_CONTRACT(name, dict); \ + CAT3(dict_, name, _clean)(dict); \ + char c = fgetc(file); \ + if (c != '{') return false; \ + key_type key; \ + value_type value; \ + M_GET_INIT key_oplist (key); \ + M_GET_INIT value_oplist (value); \ + do { \ + bool b = M_GET_IN_STR key_oplist (key, file); \ + if (!b) { c = 0; break; } \ + c = fgetc(file); \ + if (c != ':') { c = 0; break; } \ + b = M_GET_IN_STR value_oplist (value, file); \ + if (!b) { c = 0; break; } \ + CAT3(dict_, name, _set_at)(dict, key, value); \ + c = fgetc(file); \ + } while (c == ',' && !feof(file) && !ferror(file)); \ + M_GET_CLEAR key_oplist (key); \ + M_GET_CLEAR value_oplist (value); \ + return c == '}'; \ + } \ + , /* no IN_STR */ ) \ + \ + +/* Define the oplist of a dictionnary. + DICT_OPLIST(name[, oplist of the key type, oplist of the value type]) */ +#define DICT_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (DICTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST ), \ + DICTI_OPLIST(__VA_ARGS__ )) + +/********************************** INTERNAL ************************************/ + +/* Define the oplist of a dictionnary */ +#define DICTI_OPLIST(type, key_oplist, value_oplist) \ + (INIT(CAT3(dict_, type, _init)), \ + INIT_SET(CAT3(dict_, type, _init_set)), \ + SET(CAT3(dict_, type, _set)), \ + CLEAR(CAT3(dict_, type, _clear)), \ + INIT_MOVE(CAT3(dict_, type, _init_move)), \ + MOVE(CAT3(dict_, type, _move)), \ + TYPE(CAT3(dict_, type, _t)), \ + SUBTYPE(CAT3(dict_pair_,type,_t)), \ + IT_TYPE(CAT3(dict_it_,type,_t)), \ + IT_FIRST(CAT3(dict_,type,_it)), \ + IT_SET(CAT3(dict_, type, _it_set)), \ + IT_END_P(CAT3(dict_,type,_end_p)), \ + IT_LAST_P(CAT3(dict_,type,_last_p)), \ + IT_NEXT(CAT3(dict_,type,_next)), \ + IT_REF(CAT3(dict_,type,_ref)), \ + IT_CREF(CAT3(dict_,type,_cref)) \ + ,M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)(GET_STR(CAT3(array_, type, _get_str)),) \ + ,M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(CAT3(array_, type, _out_str)),) \ + ,M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(CAT3(array_, type, _in_str)),) \ + ,M_IF_METHOD(EQUAL, value_oplist)(EQUAL(CAT3(dict_, type, _equal_p)),) \ + ,KEY_OPLIST(key_oplist) \ + ,VALUE_OPLIST(value_oplist) \ + ) + +#define DICTI_LOWER_BOUND(s) ((s) <= DICTI_INITIAL_SIZE ? 0 : (s) * 1 / 4) +#define DICTI_UPPER_BOUND(s) ((s) * 2 / 3) +#define DICTI_INITIAL_SIZE 16 + +#define DICTI_CONTRACT(name, map) do { \ + assert(map->used <= map->upper_limit); \ + assert(M_POWEROF2_P(CAT3(array_list_dict_pair_,name,_size)(map->table))); \ + } while (0) + +#endif diff --git a/m-i-list.h b/m-i-list.h new file mode 100644 index 00000000..efdb1bd1 --- /dev/null +++ b/m-i-list.h @@ -0,0 +1,336 @@ +/* + * MLIB - Intrusive List module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_I_LIST_H +#define __M_I_LIST_H + +#include +#include +#include +#include +#include + +#include "m-core.h" + +/* Interface to add to a structure to allow intrusive support. + name: name of the intrusive list. + type: name of the type of the structure (aka. struct test_s) - not used currently +*/ +#define ILIST_INTERFACE(name, type) \ + struct CAT3(ilist_head_, name, _s) { \ + struct CAT3(ilist_head_, name, _s) *next; \ + struct CAT3(ilist_head_, name, _s) *prev; \ + } name + +/* Define a list of a given type. + LIST_DEF(name, type [, oplist_of_the_type]) */ +#define ILIST_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (ILISTI_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST, ILISTI_CHECK_INSTANCE ), \ + ILISTI_DEF2(name, __VA_ARGS__, ILISTI_CHECK_INSTANCE )) + +/* Define the oplist of an ilist of type */ +#define ILIST_OPLIST(name) \ + (INIT(CAT3(ilist_, name, _init)), \ + CLEAR(CAT3(ilist_, name, _clear)), \ + TYPE(CAT3(ilist_,name,_t)), \ + SUBTYPE(CAT3(ilist_type_,name,_t)), \ + IT_TYPE(CAT3(ilist_it_,name,_t)), \ + IT_FIRST(CAT3(ilist_,name,_it)), \ + IT_SET(CAT3(ilist_,name,_it_set)), \ + IT_END_P(CAT3(ilist_,name,_end_p)), \ + IT_LAST_P(CAT3(ilist_,name,_last_p)), \ + IT_EQUAL_P(CAT3(ilist_,name,_it_equal_p)), \ + IT_NEXT(CAT3(ilist_,name,_next)), \ + IT_REF(CAT3(ilist_,name,_ref)), \ + IT_CREF(CAT3(ilist_,name,_cref)), \ + PUSH(CAT3(ilist_,name,_push_back)), \ + POP(CAT3(ilist_,name,_pop_back)) \ + ) +//TODO: Add oplist as optional argument of ILIST_OPLIST + +/********************************** INTERNAL ************************************/ +/* Check if there is only one instance on the list in debug mode. + * It doesn't work with C++ (as global vars with the same name are not merged). + * (Not sure if it is really mandatory, nor a good idea) */ +#if !defined(__cplusplus) && !defined (NDEBUG) +# define ILISTI_CHECK_INSTANCE 1 +#else +# define ILISTI_CHECK_INSTANCE 0 +#endif + +/* + * From a pointer to a 'field_type' 'field' of a 'type'structure, + * return pointer to the structure. + * Note: Lot of cast. Can be unsafe. + */ +#define ILISTI_TYPE_FROM_FIELD(type, ptr, field_type, field) \ + ( ( (void)((ptr) == (field_type*)(ptr))) , \ + ((type *)(void*)( (char *)(ptr) - offsetof(type, field) )) ) + +#define ILISTI_DEF2(name, type, oplist, check_instance) \ + typedef struct CAT3(ilist_head_, name, _s) CAT3(ilist_, name, _t)[1]; \ + \ + typedef type CAT3(ilist_type_,name, _t); \ + \ + typedef struct CAT3(ilist_it_, name, _s) { \ + struct CAT3(ilist_head_, name, _s) *head; \ + struct CAT3(ilist_head_, name, _s) *previous; \ + struct CAT3(ilist_head_, name, _s) *current; \ + struct CAT3(ilist_head_, name, _s) *next; \ + } CAT3(ilist_it_, name, _t)[1]; \ + \ + typedef union { \ + type *ptr; \ + const type *cptr; \ + } CAT3(ilist_union_, name,_t); \ + \ + static inline const type * \ + CAT3(ilist_, name, _const_cast)(type *ptr) \ + { \ + CAT3(ilist_union_, name,_t) u; \ + u.ptr = ptr; \ + return u.cptr; \ + } \ + \ + M_IF(check_instance)(bool CAT3(ilist_instance_, name, _g);, ) \ + \ + static inline void CAT3(ilist_, name, _init)(CAT3(ilist_, name, _t) list) \ + { \ + assert (list != NULL); \ + M_IF(check_instance)(assert (CAT3(ilist_instance_, name, _g) == false),); \ + M_IF(check_instance)(CAT3(ilist_instance_, name, _g) = true,); \ + list->next = list; \ + list->prev = list; \ + } \ + \ + static inline void CAT3(ilist_, name, _clean)(CAT3(ilist_, name, _t) list) \ + { \ + assert (list != NULL); \ + list->next = list; \ + list->prev = list; \ + } \ + \ + static inline void CAT3(ilist_, name, _clear)(CAT3(ilist_, name, _t) list) \ + { \ + M_IF(check_instance)(assert (CAT3(ilist_instance_, name, _g) == true),); \ + M_IF(check_instance)(CAT3(ilist_instance_, name, _g) = false,); \ + CAT3(ilist_, name, _clean)(list); \ + } \ + \ + static inline bool CAT3(ilist_, name, _empty_p)(const CAT3(ilist_, name, _t) list) \ + { \ + return list->next == list; \ + } \ + \ + static inline size_t CAT3(ilist_, name, _size)(const CAT3(ilist_, name, _t) list) \ + { \ + size_t s = 0; \ + for(const struct CAT3(ilist_head_, name, _s) *it = list->next ; \ + it != list; it = it->next) \ + s++; \ + return s; \ + } \ + \ + static inline void CAT3(ilist_, name, _push_back)(CAT3(ilist_, name, _t) list, \ + type *obj) \ + { \ + assert (list != NULL && obj != NULL); \ + struct CAT3(ilist_head_, name, _s) *prev = list->prev; \ + list->prev = &obj->name; \ + obj->name.prev = prev; \ + obj->name.next = list; \ + prev->next = &obj->name; \ + } \ + \ + static inline void CAT3(ilist_, name, _push_front)(CAT3(ilist_, name, _t) list, \ + type *obj) \ + { \ + assert (list != NULL && obj != NULL); \ + struct CAT3(ilist_head_, name, _s) *next = list->next; \ + list->next = &obj->name; \ + obj->name.next = next; \ + obj->name.prev = list; \ + next->prev = &obj->name; \ + } \ + \ + static inline void CAT3(ilist_, name, _push_after)(type *obj_it, \ + type *obj_ins) \ + { \ + assert (obj_it != NULL && obj_ins != NULL); \ + struct CAT3(ilist_head_, name, _s) *next = obj_it->name.next; \ + obj_it->name.next = &obj_ins->name; \ + obj_ins->name.next = next; \ + obj_ins->name.prev = &obj_it->name; \ + next->prev = &obj_ins->name; \ + } \ + \ + static inline void CAT3(ilist_, name, _init_field)(type *obj) \ + { \ + assert (obj != NULL); \ + obj->name.next = NULL; \ + obj->name.prev = NULL; \ + } \ + \ + static inline void CAT3(ilist_, name, _unlink)(type *obj) \ + { \ + assert (obj != NULL); \ + struct CAT3(ilist_head_, name, _s) *next = obj->name.next; \ + struct CAT3(ilist_head_, name, _s) *prev = obj->name.prev; \ + next->prev = prev; \ + prev->next = next; \ + obj->name.next = NULL; \ + obj->name.prev = NULL; \ + } \ + \ + static inline type * \ + CAT3(ilist_, name, _back)(const CAT3(ilist_, name,_t) list) \ + { \ + assert(list != NULL && !CAT3(ilist_, name, _empty_p)(list)); \ + return ILISTI_TYPE_FROM_FIELD(type, list->prev, \ + struct CAT3(ilist_head_, name, _s), name); \ + } \ + \ + static inline type * \ + CAT3(ilist_, name, _front)(const CAT3(ilist_, name,_t) list) \ + { \ + assert(list != NULL && !CAT3(ilist_, name, _empty_p)(list)); \ + return ILISTI_TYPE_FROM_FIELD(type, list->next, \ + struct CAT3(ilist_head_, name, _s), name); \ + } \ + \ + static inline void \ + CAT3(ilist_, name, _it)(CAT3(ilist_it_, name,_t) it, \ + CAT3(ilist_, name,_t) list) \ + { \ + assert (it != NULL && list != NULL); \ + assert (list->next != NULL && list->next->next != NULL); \ + it->head = list; \ + it->current = list->next; \ + it->next = list->next->next; \ + it->previous = list; \ + } \ + \ + static inline void \ + CAT3(ilist_, name, _it_set)(CAT3(ilist_it_, name,_t) it, \ + const CAT3(ilist_it_, name,_t) cit) \ + { \ + assert (it != NULL && cit != NULL); \ + it->head = cit->head; \ + it->current = cit->current; \ + it->next = cit->next; \ + it->previous = cit->previous; \ + } \ + \ + static inline bool \ + CAT3(ilist_, name, _end_p)(const CAT3(ilist_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + return it->current == it->head; \ + } \ + \ + static inline bool \ + CAT3(ilist_, name, _last_p)(const CAT3(ilist_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + return it->next == it->head; \ + } \ + \ + static inline void \ + CAT3(ilist_, name, _next)(CAT3(ilist_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + /* Can't set it->previous to it->current. \ + it->current may have been unlinked from the list */ \ + it->current = it->next; \ + assert (it->current != NULL); \ + it->next = it->current->next; \ + it->previous = it->current->prev; \ + assert (it->next != NULL && it->previous != NULL); \ + } \ + \ + static inline void \ + CAT3(ilist_, name, _previous)(CAT3(ilist_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + /* Can't set it->next to it->current. \ + it->current may have been unlinked from the list */ \ + it->current = it->previous; \ + assert (it->current != NULL); \ + it->next = it->current->next; \ + it->previous = it->current->prev; \ + assert (it->next != NULL && it->previous != NULL); \ + } \ + \ + static inline bool \ + CAT3(ilist_, name, _it_equal_p)(const CAT3(ilist_it_, name,_t) it1, \ + const CAT3(ilist_it_, name,_t) it2 ) \ + { \ + assert (it1 != NULL && it2 != NULL); \ + /* No need to check for next & previous */ \ + return it1->head == it2->head && it1->current == it2->current; \ + } \ + \ + static inline type * \ + CAT3(ilist_, name, _ref)(const CAT3(ilist_it_, name,_t) it) \ + { \ + assert (it != NULL && it->current != NULL); \ + /* check if 'it' was not deleted */ \ + assert (it->current->next == it->next); \ + assert (it->current->prev == it->previous); \ + assert (!CAT3(ilist_, name, _end_p)(it)); \ + return ILISTI_TYPE_FROM_FIELD(type, it->current, \ + struct CAT3(ilist_head_, name, _s), name); \ + } \ + \ + static inline const type * \ + CAT3(ilist_, name, _cref)(const CAT3(ilist_it_, name,_t) it) \ + { \ + type *ptr = CAT3(ilist_, name, _ref)(it); \ + return CAT3(ilist_, name, _const_cast)(ptr); \ + } \ + \ + static inline type * \ + CAT3(ilist_, name, _pop_back)(CAT3(ilist_, name,_t) list) \ + { \ + assert (!CAT3(ilist_, name, _empty_p)(list)); \ + type *obj = CAT3(ilist_, name, _back)(list); \ + list->prev = list->prev->prev; \ + list->prev->next = list; \ + return obj; \ + } \ + \ + static inline type * \ + CAT3(ilist_, name, _pop_front)(CAT3(ilist_, name,_t) list) \ + { \ + assert (!CAT3(ilist_, name, _empty_p)(list)); \ + type *obj = CAT3(ilist_, name, _front)(list); \ + list->next = list->next->next; \ + list->next->prev = list; \ + return obj; \ + } \ + +//TODO: C++ compatibility + +#endif diff --git a/m-i-shared.h b/m-i-shared.h new file mode 100644 index 00000000..c53bc8b9 --- /dev/null +++ b/m-i-shared.h @@ -0,0 +1,113 @@ +/* + * MLIB - INTRUSIVE SHARED PTR Module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_SHARED_PTR__H +#define __M_SHARED_PTR__H + +#include +#include +#include +#include + +#include "m-core.h" + +#define ISHARED_PTR_OPLIST(name) ( \ + INIT(M_INIT_DEFAULT), \ + CLEAR(CAT3(ishared_, name, _clear)), \ + SET(CAT3(ishared_, name, _set) M_IPTR), \ + INIT_SET(CAT3(ishared_, name, _init_set2) M_IPTR), \ + TYPE(CAT3(ishared_,name,_t)), \ + SUBTYPE(CAT3(ishared_type_,name,_t)), \ + ) + +/* Interface to add to a structure to allow intrusive support. + name: name of the intrusive shared pointer. + type: name of the type of the structure (aka. struct test_s) - not used currently */ +#define ISHARED_PTR_INTERFACE(name, type) \ + int CAT(name, _cpt) + + +#define ISHARED_PTR_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (ISHAREDI_PTR_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST ), \ + ISHAREDI_PTR_DEF2(name, __VA_ARGS__ )) + +/********************************** INTERNAL ************************************/ + +#define ISHAREDI_PTR_DEF2(name, type, oplist) \ + \ + typedef type *CAT3(ishared_,name,_t); \ + typedef type CAT3(ishared_type_,name,_t); \ + \ + static inline type * \ + CAT3(ishared_, name, _init)(type *ptr) \ + { \ + if (ptr != NULL) \ + ptr->CAT(name, _cpt) = 1; \ + return ptr; \ + } \ + \ + static inline type * \ + CAT3(ishared_, name, _init_set)(type *shared) \ + { \ + if (shared != NULL) \ + atomic_fetch_add(&(shared->CAT(name, _cpt)), 1); \ + return shared; \ + } \ + \ + static inline void \ + CAT3(ishared_, name, _init_set2)(type ** ptr, type *shared) \ + { \ + assert (ptr != NULL); \ + *ptr = CAT3(ishared_, name, _init_set)(shared); \ + } \ + \ + static inline void \ + CAT3(ishared_, name, _clear)(type *shared) \ + { \ + if (shared != NULL) { \ + if (atomic_fetch_sub(&(shared->CAT(name, _cpt)), 1) == 1) { \ + M_GET_CLEAR oplist (*shared); \ + M_GET_DEL oplist (shared); \ + } \ + } \ + } \ + \ + static inline void \ + CAT3(ishared_, name, _release)(type *shared) \ + { \ + CAT3(ishared_, name, _clear)(shared); \ + } \ + \ + static inline void \ + CAT3(ishared_, name, _set)(type ** ptr, type *shared) \ + { \ + assert (ptr != NULL); \ + CAT3(ishared_, name, _clear)(*ptr); \ + *ptr = CAT3(ishared_, name, _init_set)(shared); \ + } \ + \ + +#endif + diff --git a/m-list.h b/m-list.h new file mode 100644 index 00000000..cc107d97 --- /dev/null +++ b/m-list.h @@ -0,0 +1,559 @@ +/* + * MLIB - LIST module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_LIST_H +#define __M_LIST_H + +#include +#include +#include +#include + +#include "m-core.h" + +/* Define a list of a given type. + USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */ +#define LIST_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (LISTI_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST ), \ + LISTI_DEF2(name, __VA_ARGS__ )) + +/* Define the oplist of a list of type. + USAGE: LIST_OPLIST(name [, oplist_of_the_type]) */ +#define LIST_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (LISTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \ + LISTI_OPLIST(__VA_ARGS__ )) + + +/********************************** INTERNAL ************************************/ + +#define LISTI_OPLIST(name, oplist) \ + (INIT(CAT3(list_, name, _init)), \ + INIT_SET(CAT3(list_, name, _init_set)), \ + SET(CAT3(list_, name, _set)), \ + CLEAR(CAT3(list_, name, _clear)), \ + MOVE(CAT3(list_, name, _move)), \ + INIT_MOVE(CAT3(list_, name, _init_move)), \ + TYPE(CAT3(list_,name,_t)), \ + SUBTYPE(CAT3(list_type_,name,_t)), \ + IT_TYPE(CAT3(list_it_,name,_t)), \ + IT_FIRST(CAT3(list_,name,_it)), \ + IT_SET(CAT3(list_,name,_it_set)), \ + IT_END_P(CAT3(list_,name,_end_p)), \ + IT_EQUAL_P(CAT3(list_,name,_it_equal_p)), \ + IT_LAST_P(CAT3(list_,name,_last_p)), \ + IT_NEXT(CAT3(list_,name,_next)), \ + IT_REF(CAT3(list_,name,_ref)), \ + IT_CREF(CAT3(list_,name,_cref)), \ + CLEAN(CAT3(list_,name,_clean)), \ + PUSH(CAT3(list_,name,_push_back)), \ + POP(CAT3(list_,name,_pop_back)) \ + ,REVERSE(CAT3(list_,name,_reverse)) \ + ,OPLIST(oplist) \ + ,M_IF_METHOD(GET_STR, oplist)(GET_STR(CAT3(list_, name, _get_str)),) \ + ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(CAT3(list_, name, _out_str)),) \ + ,M_IF_METHOD(IN_STR, oplist)(IN_STR(CAT3(list_, name, _in_str)),) \ + ,M_IF_METHOD(EQUAL, oplist)(EQUAL(CAT3(list_, name, _equal_p)),) \ + ) + +#define LISTI_DEF2(name, type, oplist) \ + typedef struct CAT3(list_, name, _s) { \ + struct CAT3(list_, name, _s) *next; \ + type data; \ + } *CAT3(list_, name,_t)[1]; \ + \ + typedef struct CAT3(list_, name, _s) **CAT3(list_,name, _ptr); \ + \ + typedef const struct CAT3(list_, name, _s) *const*CAT3(list_,name, _srcptr);\ + \ + typedef type CAT3(list_type_,name, _t); \ + \ + typedef struct CAT3(list_it_, name, _s) { \ + struct CAT3(list_, name, _s) *previous; \ + CAT3(list_, name,_t) l; \ + } CAT3(list_it_, name,_t)[1]; \ + \ + typedef union { \ + type *ptr; \ + const type *cptr; \ + } CAT3(list_union_, name,_t); \ + \ + static inline const type * \ + CAT3(list_, name, _const_cast)(type *ptr) \ + { \ + CAT3(list_union_, name,_t) u; \ + u.ptr = ptr; \ + return u.cptr; \ + } \ + \ + static inline void \ + CAT3(list_, name, _init)(CAT3(list_, name,_t) v) \ + { \ + assert( v != NULL); \ + *v = NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _clean)(CAT3(list_, name,_t) v) \ + { \ + assert (v != NULL); \ + struct CAT3(list_, name, _s) *it = *v; \ + while (it != NULL) { \ + struct CAT3(list_, name, _s) *next = it->next; \ + M_GET_CLEAR oplist(it->data); \ + M_MEMORY_FREE (it); \ + it = next; \ + } \ + *v = NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _clear)(CAT3(list_, name,_t) v) \ + { \ + CAT3(list_, name, _clean)(v); \ + } \ + \ + static inline const type * \ + CAT3(list_, name, _back)(const CAT3(list_, name,_t) v) \ + { \ + assert(v != NULL && *v != NULL); \ + return CAT3(list_, name, _const_cast)(&((*v)->data)); \ + } \ + \ + static inline type * \ + CAT3(list_, name, _push_raw)(CAT3(list_, name,_t) v) \ + { \ + assert (v != NULL); \ + struct CAT3(list_, name, _s) *next; \ + next = M_MEMORY_ALLOC (struct CAT3(list_, name, _s)); \ + if (next == NULL) { \ + M_MEMORY_FULL(sizeof (struct CAT3(list_, name, _s))); \ + return NULL; \ + } \ + type *ret = &next->data; \ + next->next = *v; \ + *v = next; \ + return ret; \ + } \ + \ + static inline void \ + CAT3(list_, name, _push_back)(CAT3(list_, name,_t) v, type const x) \ + { \ + type *data = CAT3(list_, name, _push_raw)(v); \ + if (data == NULL) \ + return; \ + M_GET_INIT_SET oplist(*data, x); \ + } \ + \ + static inline type * \ + CAT3(list_, name, _push_new)(CAT3(list_, name,_t) v) \ + { \ + type *data = CAT3(list_, name, _push_raw)(v); \ + if (data == NULL) \ + return NULL; \ + M_GET_INIT oplist(*data); \ + return data; \ + } \ + \ + static inline void \ + CAT3(list_, name, _pop_back)(type *data, CAT3(list_, name,_t) v) \ + { \ + assert(v != NULL && *v != NULL); \ + if (data != NULL) { \ + M_GET_SET oplist(*data, (*v)->data); \ + } \ + M_GET_CLEAR oplist((*v)->data); \ + struct CAT3(list_, name, _s) *tofree = *v; \ + *v = (*v)->next; \ + M_MEMORY_FREE (tofree); \ + } \ + \ + static inline bool \ + CAT3(list_, name, _empty_p)(const CAT3(list_, name,_t) v) \ + { \ + assert (v!= NULL); \ + return *v == NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _swap)(CAT3(list_, name,_t) l, \ + CAT3(list_, name,_t) v) \ + { \ + assert (v != NULL && l != NULL); \ + struct CAT3(list_, name, _s) *tmp = *l; \ + *l = *v; \ + *v = tmp; \ + } \ + \ + static inline void \ + CAT3(list_, name, _it)(CAT3(list_it_, name,_t) l, \ + const CAT3(list_, name,_t) v) \ + { \ + assert (v != NULL && l[0].l != NULL); \ + l[0].l[0] = *v; \ + l[0].previous = NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _it_set)(CAT3(list_it_, name,_t) it1, \ + const CAT3(list_it_, name,_t) it2) \ + { \ + assert (it1 != NULL && it2 != NULL); \ + it1[0].l[0] = it2[0].l[0]; \ + it1[0].previous = it2[0].previous; \ + } \ + \ + static inline bool \ + CAT3(list_, name, _end_p)(const CAT3(list_it_, name,_t) v) \ + { \ + assert (v!= NULL); \ + return v[0].l[0] == NULL; \ + } \ + \ + static inline bool \ + CAT3(list_, name, _last_p)(const CAT3(list_it_, name,_t) v) \ + { \ + assert (v!= NULL); \ + return v[0].l[0] == NULL || v[0].l[0]->next == NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _next)(CAT3(list_it_, name,_t) tail) \ + { \ + assert(tail != NULL && tail[0].l[0] != NULL); \ + tail[0].previous = tail[0].l[0]; \ + tail[0].l[0] = tail[0].l[0]->next; \ + } \ + \ + static inline bool \ + CAT3(list_, name, _it_equal_p)(const CAT3(list_it_, name,_t) it1, \ + const CAT3(list_it_, name,_t) it2) \ + { \ + assert(it1 != NULL && it2 != NULL); \ + return it1[0].l[0] == it2[0].l[0]; \ + } \ + \ + static inline type * \ + CAT3(list_, name, _ref)(CAT3(list_it_, name,_t) v) \ + { \ + assert(v != NULL && v[0].l[0] != NULL); \ + return &(v[0].l[0]->data); \ + } \ + \ + static inline const type * \ + CAT3(list_, name, _cref)(const CAT3(list_it_, name,_t) v) \ + { \ + assert(v != NULL && v[0].l[0] != NULL); \ + return CAT3(list_, name, _const_cast)(&(v[0].l[0]->data)); \ + } \ + \ + static inline size_t \ + CAT3(list_, name, _size)(const CAT3(list_, name,_t) list) \ + { \ + assert (list != NULL); \ + size_t size = 0; \ + /* FIXME: Store size field within the list to get faster? */ \ + struct CAT3(list_, name, _s) *it = *list; \ + while (it != NULL) { \ + size ++; \ + it = it->next; \ + } \ + return size; \ + } \ + \ + static inline bool \ + CAT3(list_, name, _sublist_p)(const CAT3(list_, name,_t) list, \ + const CAT3(list_it_, name,_t) sublist) \ + { \ + assert (list != NULL && sublist != NULL); \ + struct CAT3(list_, name, _s) *it = *list; \ + while (it != NULL) { \ + if (it == sublist[0].l[0]) return true; \ + it = it->next; \ + } \ + return (sublist[0].l[0] == NULL); \ + } \ + \ + static inline type * \ + CAT3(list_, name, _get)(const CAT3(list_, name,_t) list, size_t i) \ + { \ + assert (list != NULL); \ + struct CAT3(list_, name, _s) *it = *list; \ + /* FIXME: How to avoid the double iteration over the list? */ \ + size_t len = CAT3(list_,name,_size)(list); \ + assert (i < len); \ + size_t j = len-1; \ + while (it != NULL) { \ + if (i == j) return &it->data; \ + it = it->next; \ + j--; \ + } \ + assert(false); \ + return NULL; /* Can not be reached */ \ + } \ + \ + static inline const type * \ + CAT3(list_, name, _cget)(const CAT3(list_, name,_t) l, size_t i) \ + { \ + return CAT3(list_, name, _const_cast)(CAT3(list_, name, _get)(l,i)); \ + } \ + \ + static inline void \ + CAT3(list_, name, _insert)(CAT3(list_, name,_t) list, \ + CAT3(list_it_, name,_t) insertion_point, \ + type const x) \ + { \ + assert (list != NULL && insertion_point != NULL); \ + assert(CAT3(list_, name, _sublist_p)(list, insertion_point)); \ + struct CAT3(list_, name, _s) *next; \ + next = M_MEMORY_ALLOC (struct CAT3(list_, name, _s)); \ + if (next == NULL) { \ + M_MEMORY_FULL(sizeof (struct CAT3(list_, name, _s))); \ + return; \ + } \ + M_GET_INIT_SET oplist(next->data, x); \ + /* FIXME: How to set an iterator to NULL? */ \ + if (insertion_point[0].l[0] == NULL) { \ + next->next = *list; \ + *list = next; \ + } else { \ + next->next = insertion_point[0].l[0]->next; \ + insertion_point[0].l[0]->next = next; \ + } \ + } \ + \ + static inline void \ + CAT3(list_, name, _remove)(CAT3(list_, name,_t) list, \ + CAT3(list_it_, name,_t) removing_point) \ + { \ + assert (list != NULL && removing_point != NULL); \ + assert(removing_point[0].l[0] != NULL); \ + assert(CAT3(list_, name, _sublist_p)(list, removing_point)); \ + struct CAT3(list_, name, _s) *next = removing_point[0].l[0]->next; \ + if (removing_point->previous == NULL) { \ + *list = next; \ + } else { \ + removing_point->previous->next = next; \ + } \ + M_GET_CLEAR oplist(removing_point[0].l[0]->data); \ + M_MEMORY_FREE (removing_point[0].l[0]); \ + removing_point[0].l[0] = next; \ + } \ + \ + static inline void \ + CAT3(list_, name, _move_back)(CAT3(list_, name,_t) nv, \ + CAT3(list_, name,_t) ov, \ + CAT3(list_it_, name,_t) it) \ + { \ + assert (nv != NULL && ov != NULL && it != NULL && nv != ov); \ + assert(it[0].l[0] != NULL); \ + assert(CAT3(list_, name, _sublist_p)(ov, it)); \ + /* Remove it from ov */ \ + struct CAT3(list_, name, _s) *next = it[0].l[0]->next; \ + struct CAT3(list_, name, _s) *current = it[0].l[0]; \ + if (it->previous == NULL) { \ + *ov = next; \ + } else { \ + it->previous->next = next; \ + } \ + /* Update it to next element */ \ + it[0].l[0] = next; \ + /* Move current in nv */ \ + current->next = *nv; \ + *nv = current; \ + } \ + \ + static inline void \ + CAT3(list_, name, _set)(CAT3(list_, name,_t) list, \ + const CAT3(list_, name,_t) org) \ + { \ + assert (list != NULL && org != NULL); \ + struct CAT3(list_, name, _s) *next, *it_org; \ + struct CAT3(list_, name, _s) **update_list; \ + if (list == org) return; \ + /* TODO: Reuse memory of list */ \ + CAT3(list_, name, _clean)(list); \ + update_list = list; \ + it_org = *org; \ + while (it_org != NULL) { \ + next = M_MEMORY_ALLOC (struct CAT3(list_, name, _s)); \ + *update_list = next; \ + if (next == NULL) { \ + M_MEMORY_FULL(sizeof (struct CAT3(list_, name, _s))); \ + return; \ + } \ + update_list = &next->next; \ + M_GET_INIT_SET oplist(next->data, it_org->data); \ + it_org = it_org->next; \ + } \ + *update_list = NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _init_set)(CAT3(list_, name,_t) list, \ + const CAT3(list_, name,_t) org) \ + { \ + assert (list != NULL && org != NULL && list != org); \ + CAT3(list_, name, _init)(list); \ + CAT3(list_, name, _set)(list, org); \ + } \ + \ + static inline void \ + CAT3(list_, name, _init_move)(CAT3(list_, name,_t) list, \ + CAT3(list_, name,_t) org) \ + { \ + assert (list != NULL && org != NULL && list != org); \ + *list = *org; \ + *org = NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _move)(CAT3(list_, name,_t) list, \ + CAT3(list_, name,_t) org) \ + { \ + assert (list != NULL && org != NULL && list != org); \ + CAT3(list_, name, _clear)(list); \ + CAT3(list_, name, _init_move)(list, org); \ + } \ + \ + static inline void \ + CAT3(list_, name, _slice)(CAT3(list_, name,_t) list1, \ + CAT3(list_, name,_t) list2) \ + { \ + assert (list1 != NULL && list2 != NULL && list1 != list2); \ + struct CAT3(list_, name, _s) **update_list = list1; \ + struct CAT3(list_, name, _s) *it = *list1; \ + while (it != NULL) { \ + update_list = &it->next; \ + it = it->next; \ + } \ + *update_list = *list2; \ + *list2 = NULL; \ + } \ + \ + static inline void \ + CAT3(list_, name, _reverse)(CAT3(list_, name,_t) list) \ + { \ + assert (list != NULL); \ + struct CAT3(list_, name, _s) *previous = NULL, *it = *list, *next; \ + while (it != NULL) { \ + next = it->next; \ + it->next = previous; \ + previous = it; \ + it = next; \ + } \ + *list = previous; \ + } \ + \ + M_IF_METHOD(GET_STR, oplist)( \ + static inline void \ + CAT3(list_, name, _get_str)(string_t str, const CAT3(list_, name,_t) list, \ + bool append) \ + { \ + assert (str != NULL && list != NULL); \ + (append ? string_cat_str : string_set_str) (str, "["); \ + CAT3(list_it_, name, _t) it; \ + for (CAT3(list_, name, _it)(it, list) ; \ + !CAT3(list_, name, _end_p)(it); \ + CAT3(list_, name, _next)(it)){ \ + const type *item = CAT3(list_, name, _cref)(it); \ + M_GET_GET_STR oplist (str, *item, true); \ + if (!CAT3(list_, name, _last_p)(it)) \ + string_push_back (str, M_GET_SEPARATOR oplist); \ + } \ + string_push_back (str, ']'); \ + } \ + , /* no str */ ) \ + \ + M_IF_METHOD(OUT_STR, oplist)( \ + static inline void \ + CAT3(list_, name, _out_str)(FILE *file, const CAT3(list_, name,_t) list) \ + { \ + assert (file != NULL && list != NULL); \ + fputc ('[', file); \ + CAT3(list_it_, name, _t) it; \ + for (CAT3(list_, name, _it)(it, list) ; \ + !CAT3(list_, name, _end_p)(it); \ + CAT3(list_, name, _next)(it)){ \ + const type *item = CAT3(list_, name, _cref)(it); \ + M_GET_OUT_STR oplist (file, *item); \ + if (!CAT3(list_, name, _last_p)(it)) \ + fputc (M_GET_SEPARATOR oplist, file); \ + } \ + fputc (']', file); \ + } \ + , /* no out_str */ ) \ + \ + M_IF_METHOD(IN_STR, oplist)( \ + static inline bool \ + CAT3(list_, name, _in_str)(CAT3(list_, name,_t) list, FILE *file) \ + { \ + assert (file != NULL && list != NULL); \ + CAT3(list_, name,_clean)(list); \ + char c = fgetc(file); \ + if (c != '[') return false; \ + type item; \ + M_GET_INIT oplist (item); \ + do { \ + bool b = M_GET_IN_STR oplist (item, file); \ + if (!b) { c = 0; break; } \ + CAT3(list_, name, _push_back)(list, item); \ + c = fgetc(file); \ + } while (c == M_GET_SEPARATOR oplist && !feof(file) && !ferror(file)); \ + M_GET_CLEAR oplist (item); \ + CAT3(list_, name, _reverse)(list); \ + return c == ']'; \ + } \ + , /* no in_str */ ) \ + \ + M_IF_METHOD(EQUAL, oplist)( \ + static inline bool \ + CAT3(list_, name, _equal_p)(const CAT3(list_, name,_t) list1, \ + const CAT3(list_, name,_t) list2) \ + { \ + assert (list1 != NULL && list2 != NULL); \ + CAT3(list_it_, name, _t) it1; \ + CAT3(list_it_, name, _t) it2; \ + if (list1 == list2) return true; \ + CAT3(list_, name, _it)(it1, list1); \ + CAT3(list_, name, _it)(it2, list2); \ + while (!CAT3(list_, name, _end_p)(it1) \ + &&!CAT3(list_, name, _end_p)(it2)) { \ + const type *item1 = CAT3(list_, name, _cref)(it1); \ + const type *item2 = CAT3(list_, name, _cref)(it2); \ + bool b = M_GET_EQUAL oplist (*item1, *item2); \ + if (!b) return false; \ + CAT3(list_, name, _next)(it1); \ + CAT3(list_, name, _next)(it2); \ + } \ + return CAT3(list_, name, _end_p)(it1) \ + && CAT3(list_, name, _end_p)(it2); \ + } \ + , /* no equal */ ) \ + \ + +// TODO: Conditional HASH if HASH is present + +#endif diff --git a/m-mutex.h b/m-mutex.h new file mode 100644 index 00000000..d74d3df2 --- /dev/null +++ b/m-mutex.h @@ -0,0 +1,151 @@ +/* + * MLIB - Slin Mutex & Thread wrapper + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_MUTEX_H +#define __M_MUTEX_H + +#include +#include "m-core.h" + +//TODO: static inline versions should be cleaner + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \ + && !defined(__STDC_NO_THREADS__) + +/****************************** C11 version ********************************/ + +#include + +#define M_MUTEX_T mtx_t +#define M_MUTEX_INIT(_mutex) do { \ + int rc = mtx_init(&(_mutex), 0); \ + M_ASSERT_INIT (rc == 0); \ + } while (0) +#define M_MUTEX_CLEAR(_mutex) mtx_destroy(&(_mutex)) +#define M_MUTEX_LOCK(_mutex) mtx_lock(&(_mutex)) +#define M_MUTEX_UNLOCK(_mutex) mtx_unlock(&(_mutex)) + +#define M_COND_T cnd_t +#define M_COND_INIT(_cond) do { \ + int rc = cnd_init(&(_cond)); \ + M_ASSERT_INIT (rc == 0); \ + } while (0) +#define M_COND_CLEAR(_cond) cnd_destroy(&(_cond)) +#define M_COND_SIGNAL(_cond) cnd_signal(&(_cond)) +#define M_COND_WAIT(_cond, _mutex) cnd_wait(&(_cond),&(_mutex)) + +#define M_THREAD_T thrd_t +#define M_THREAD_CREATE(h, func, arg) do { \ + int rc = thrd_create(&(h), ((int)(*)(void*))func, arg); \ + M_ASSERT_INIT (rc == thrd_success); \ + } while (0) +#define M_THREAD_JOIN(h) do { \ + int rc = thrd_join((h), NULL); \ + assert (rc == thrd_success); \ + } while (0) +#define M_THREAD_EXIT(arg) thrd_exit(arg) + +#elif defined(WIN32) || defined(_WIN32) + +/****************************** WIN32 version ******************************/ + +#include + +#define M_MUTEX_T HANDLE +#define M_MUTEX_INIT(_mutex) do { \ + (_mutex) = CreateMutex(NULL, FALSE, NULL); \ + M_ASSERT_INIT((_mutex) != NULL); \ + } while (0) +#define M_MUTEX_CLEAR(_mutex) CloseHandle(_mutex) +#define M_MUTEX_LOCK(_mutex) do { \ + DWORD dwWaitResult = WaitForSingleObject((_mutex), INFINITE); \ + assert (dwWaitResult == WAIT_OBJECT_0); \ + } while(0) +#define M_MUTEX_TRYLOCK(m) (WaitForSingleObject((m), 0) == WAIT_OBJECT_0) +#define M_MUTEX_UNLOCK(_mutex) ReleaseMutex(_mutex) + +#define M_COND_T HANDLE +#define M_COND_INIT(_cond) do { \ + (_cond) = = CreateEvent (NULL, FALSE, FALSE, NULL); \ + M_ASSERT_INIT ((_cond) != NULL); \ + } while (0) +#define M_COND_CLEAR(_cond) CloseHandle(_cond) +#define M_COND_SIGNAL(_cond) PulseEvent(_cond) +#define M_COND_WAIT(_cond, _mutex) do { \ + DWORD dwWaitResult = SignalObjectAndWait((_mutex),(_cond),INFINITE,FALSE); \ + assert (dwWaitResult == WAIT_OBJECT_0); \ + dwWaitResult = WaitForSingleObject((_mutex), INFINITE); \ + assert (dwWaitResult == WAIT_OBJECT_0); \ + } while(0) + +#define M_THREAD_T HANDLE +#define M_THREAD_CREATE(h, func, arg) do { \ + (h) = CreateThread(NULL, 0, (unsigned int (*)(void*)) func, arg, \ + 0, NULL); \ + M_ASSERT_INIT ((h) != NULL); \ + } while (0) +#define M_THREAD_JOIN(h) do { \ + DWORD dwWaitResult = WaitForSingleObject((h), INFINITE); \ + assert (dwWaitResult == WAIT_OBJECT_0); \ + } while (0) +#define M_THREAD_EXIT(arg) ExitThread(arg) + +#else + +/**************************** PTHREAD version ******************************/ + +#include + +#define M_MUTEX_T pthread_mutex_t +#define M_MUTEX_INIT(_mutex) do { \ + int _rc = pthread_mutex_init(&(_mutex), NULL); \ + M_ASSERT_INIT (_rc == 0); \ + } while (0) +#define M_MUTEX_CLEAR(_mutex) pthread_mutex_destroy(&(_mutex)) +#define M_MUTEX_LOCK(_mutex) pthread_mutex_lock(&(_mutex)) +#define M_MUTEX_UNLOCK(_mutex) pthread_mutex_unlock(&(_mutex)) + +#define M_COND_T pthread_cond_t +#define M_COND_INIT(_cond) do { \ + int _rc = pthread_cond_init(&(_cond), NULL); \ + M_ASSERT_INIT (_rc == 0); \ + } while (0) +#define M_COND_CLEAR(_cond) pthread_cond_destroy(&(_cond)) +#define M_COND_SIGNAL(_cond) pthread_cond_signal(&(_cond)) +#define M_COND_WAIT(_cond, _mutex) pthread_cond_wait(&(_cond),&(_mutex)) + +#define M_THREAD_T pthread_t +#define M_THREAD_CREATE(h, func, arg) do { \ + int _rc = pthread_create(&(h), NULL, (void*(*)(void*))func, arg); \ + M_ASSERT_INIT (_rc == 0); \ + } while (0) +#define M_THREAD_JOIN(h) do { \ + int _rc = pthread_join((h), NULL); \ + assert (_rc == 0); \ + } while (0) +#define M_THREAD_EXIT(arg) pthread_exit((void*)arg) + +#endif + +#endif diff --git a/m-rbtree.h b/m-rbtree.h new file mode 100644 index 00000000..36579f9d --- /dev/null +++ b/m-rbtree.h @@ -0,0 +1,753 @@ +/* + * MLIB - RED BLACK TREE module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_RBTREE_H +#define __M_RBTREE_H + +#include +#include +#include +#include +#include + +#include "m-core.h" + +/* Define a binary tree of a given type. + USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */ +#define RBTREE_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (RBTREEI_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST, \ + CAT3(rbtree_, name, _t), struct CAT3(rbtree_node_, name, _s)), \ + RBTREEI_DEF2(name, __VA_ARGS__ , \ + CAT3(rbtree_, name, _t), struct CAT3(rbtree_node_, name, _s))) + +/* Define the oplist of a rbtree of type. + USAGE: RBTREE_OPLIST(name [, oplist_of_the_type]) */ +#define RBTREE_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (RBTREEI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \ + RBTREEI_OPLIST(__VA_ARGS__ )) + + +/********************************** INTERNAL ************************************/ + +#define RBTREEI_OPLIST(name, oplist) \ + (INIT(CAT3(rbtree_, name, _init)), \ + INIT_SET(CAT3(rbtree_, name, _init_set)), \ + SET(CAT3(rbtree_, name, _set)), \ + CLEAR(CAT3(rbtree_, name, _clear)), \ + INIT_MOVE(CAT3(rbtree_, name, _init_move)), \ + MOVE(CAT3(rbtree_, name, _move)), \ + TYPE(CAT3(rbtree_,name,_t)), \ + SUBTYPE(CAT3(rbtree_type_,name,_t)), \ + IT_TYPE(CAT3(rbtree_it_,name,_t)), \ + IT_FIRST(CAT3(rbtree_,name,_it)), \ + IT_SET(CAT3(rbtree_,name,_it_set)), \ + IT_END_P(CAT3(rbtree_,name,_end_p)), \ + IT_LAST_P(CAT3(rbtree_,name,_last_p)), \ + IT_EQUAL_P(CAT3(rbtree_,name,_it_equal_p)), \ + IT_NEXT(CAT3(rbtree_,name,_next)), \ + IT_REF(CAT3(rbtree_,name,_ref)), \ + IT_CREF(CAT3(rbtree_,name,_cref)), \ + CLEAN(CAT3(rbtree_,name,_clean)), \ + PUSH(CAT3(rbtree_,name,_push)), \ + POP(CAT3(rbtree_,name,_pop)) \ + GET_MIN(CAT3(rbtree_,name,_min)), \ + GET_MAX(CAT3(rbtree_,name,_max)) \ + ) + +/* Max depth of the binary tree + It is twice the depth of a perfectly even tree with maximum elements. + Max number of elements is max of size_t. + A perfectly even tree is log2(max(size_t))color = RBTREE_RED) +#define RBTREEI_SET_BLACK(x) ((x)->color = RBTREE_BLACK) +#define RBTREEI_IS_RED(x) ((x)->color == RBTREE_RED) +#define RBTREEI_IS_BLACK(x) ((x)->color == RBTREE_BLACK) +#define RBTREEI_COPY_COLOR(x,y) ((x)->color = (y)->color) + +typedef enum { + RBTREE_BLACK = 0, RBTREE_RED +} rbtreei_color_e; + +// General contact of a tree +#define RBTREEI_CONTRACT(tree) \ + assert ((tree) != NULL); \ + assert ((tree)->node == NULL || RBTREEI_IS_BLACK((tree)->node)); \ + assert ((tree)->size != 0 || (tree)->node == NULL); \ + +// Contract of a node (doesn't check for equal depth in black) +#define RBTREEI_CONTRACT_NODE(node) \ + assert((node) != NULL); \ + assert((node)->color == RBTREE_BLACK || (node)->color == RBTREE_RED); \ + assert((node)->color != RBTREE_RED \ + || (((node)->child[0] == NULL || (node)->child[0]->color == RBTREE_BLACK) \ + && ((node)->child[1] == NULL || (node)->child[1]->color == RBTREE_BLACK))) + +#define RBTREEI_DEF2(name, type, oplist, tree_t, node_t) \ + \ + node_t { \ + node_t *child[2]; \ + type data; \ + rbtreei_color_e color; \ + }; \ + \ + typedef struct CAT3(rbtree_, name, _s) { \ + size_t size; \ + node_t *node; \ + } tree_t[1]; \ + \ + typedef struct { \ + node_t *stack[RBTREEI_MAX_STACK]; \ + char which[RBTREEI_MAX_STACK]; \ + unsigned int cpt; \ + } CAT3(rbtree_it_, name, _t)[1]; \ + \ + typedef type CAT3(rbtree_type_, name, _t); \ + \ + typedef union { \ + type *ptr; \ + const type *cptr; \ + } CAT3(rbtree_union_, name,_t); \ + \ + static inline const type * \ + CAT3(rbtree_, name, _const_cast)(type *ptr) \ + { \ + CAT3(rbtree_union_, name,_t) u; \ + u.ptr = ptr; \ + return u.cptr; \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _init)(tree_t tree) \ + { \ + assert (tree != NULL); \ + tree->size = 0; \ + tree->node = NULL; \ + RBTREEI_CONTRACT(tree); \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _clear)(tree_t tree) \ + { \ + RBTREEI_CONTRACT(tree); \ + node_t *stack[RBTREEI_MAX_STACK]; \ + unsigned int cpt = 0; \ + if (tree->node == NULL) return; \ + stack[cpt++] = tree->node; \ + while (cpt > 0) { \ + node_t *n = stack[cpt-1]; \ + while (true) { \ + RBTREEI_CONTRACT_NODE(n); \ + if (n->child[0] != NULL) { \ + assert (cpt < RBTREEI_MAX_STACK); \ + stack[cpt++] = n->child[0]; \ + n = n->child[0]; \ + stack[cpt-2]->child[0] = NULL; \ + } else if (n->child[1] != NULL) { \ + assert (cpt < RBTREEI_MAX_STACK); \ + stack[cpt++] = n->child[1]; \ + n = n->child[1]; \ + stack[cpt-2]->child[1] = NULL; \ + } else \ + break; \ + } \ + assert (n == stack[cpt - 1]); \ + M_GET_CLEAR oplist (n->data); \ + M_MEMORY_FREE (n); \ + assert((stack[cpt-1] = NULL) == NULL); \ + cpt--; \ + } \ + tree->node = NULL; \ + tree->size = 0; \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _push)(tree_t tree, type const data) \ + { \ + RBTREEI_CONTRACT(tree); \ + node_t *tab[RBTREEI_MAX_STACK]; \ + char which[RBTREEI_MAX_STACK]; \ + unsigned int cpt = 0; \ + node_t *n = tree->node; \ + /* If nothing, create new node */ \ + if (n == NULL) { \ + n = M_MEMORY_ALLOC(node_t); \ + M_GET_INIT_SET oplist (n->data, data); \ + n->child[0] = n->child[1] = NULL; \ + RBTREEI_SET_BLACK (n); \ + tree->node = n; \ + assert(tree->size == 0); \ + tree->size = 1; \ + RBTREEI_CONTRACT(tree); \ + return; \ + } \ + /* Search for insertion point */ \ + tab[cpt] = n; \ + while (n != NULL) { \ + RBTREEI_CONTRACT_NODE(n); \ + int cmp = M_GET_CMP oplist (n->data, data); \ + if (cmp == 0) { \ + break; \ + } else if (cmp > 0) { \ + which[cpt++] = 0; \ + n = n->child[0]; \ + } else { \ + which[cpt++] = 1; \ + n = n->child[1]; \ + } \ + assert (cpt < RBTREEI_MAX_STACK); \ + tab[cpt] = n; \ + } \ + /* If found, update the data (default is set) */ \ + if (n != NULL) { \ + M_GET_UPDATE oplist (n->data, data); \ + RBTREEI_CONTRACT (tree); \ + return; \ + } \ + /* Create new */ \ + n = M_MEMORY_ALLOC(node_t); \ + M_GET_INIT_SET oplist (n->data, data); \ + n->child[0] = n->child[1] = NULL; \ + RBTREEI_SET_RED (n); \ + assert (tab[cpt] == NULL); \ + tab[cpt] = n; \ + tree->size ++; \ + /* Add it in the tree */ \ + assert(tab[cpt-1]->child[0+which[cpt-1]] == NULL); \ + tab[cpt-1]->child[0+which[cpt-1]] = n; \ + /* Fix the tree */ \ + while (cpt >= 2 \ + && RBTREEI_IS_RED(tab[cpt-1]) \ + && tab[cpt-2]->child[1-which[cpt-2]] != NULL \ + && RBTREEI_IS_RED(tab[cpt-2]->child[1-which[cpt-2]])) { \ + RBTREEI_SET_BLACK(tab[cpt-1]); \ + RBTREEI_SET_BLACK(tab[cpt-2]->child[1-which[cpt-2]]); \ + RBTREEI_SET_RED(tab[cpt-2]); \ + cpt-=2; \ + } \ + /* root is always black */ \ + RBTREEI_SET_BLACK(tab[0]); \ + if (cpt <= 1 || RBTREEI_IS_BLACK(tab[cpt-1])) { \ + RBTREEI_CONTRACT (tree); \ + return; \ + } \ + /* Read the grand-father, the father and the element */ \ + node_t *pp = tab[cpt-2]; \ + node_t *p = tab[cpt-1]; \ + node_t *x = tab[cpt]; \ + /* We need to do some rotations */ \ + if (which[cpt-2] == 0) { \ + /* The father is the left child of the grand-father */ \ + if (which[cpt-1] == 0) { \ + /* The child is the left child of its father */ \ + /* Right rotation: cpt is the new grand-father. \ + x is its left child, the grand-father is the right one */ \ + pp->child[0] = p->child[1]; \ + p->child[0] = x; \ + p->child[1] = pp; \ + RBTREEI_SET_BLACK(p); \ + RBTREEI_SET_RED(pp); \ + } else { \ + /* The child is the right child of its father */ \ + /* Left rotation */ \ + pp->child[0] = x->child[1]; \ + p->child[1] = x->child[0]; \ + x->child[0] = p; \ + x->child[1] = pp; \ + RBTREEI_SET_BLACK(x); \ + RBTREEI_SET_RED(p); \ + RBTREEI_SET_RED(pp); \ + p = x; \ + } \ + } else { \ + /* The father is the right child of the grand-father */ \ + if (which[cpt-1] == 0) { \ + /* The child is the left child of its father */ \ + pp->child[1] = x->child[0]; \ + p->child[0] = x->child[1]; \ + x->child[1] = p; \ + x->child[0] = pp; \ + RBTREEI_SET_BLACK(x); \ + RBTREEI_SET_RED(p); \ + RBTREEI_SET_RED(pp); \ + p = x; \ + } else { \ + /* The child is the right child of its father */ \ + pp->child[1] = p->child[0]; \ + p->child[1] = x; \ + p->child[0] = pp; \ + RBTREEI_SET_BLACK(p); \ + RBTREEI_SET_RED(pp); \ + } \ + } \ + /* Insert the new grand father */ \ + if (cpt == 2) { \ + tree->node = p; \ + } else { \ + tab[cpt-3]->child[(int) which[cpt-3]] = p; \ + } \ + /* Done */ \ + RBTREEI_CONTRACT (tree); \ + } \ + \ + static inline size_t \ + CAT3(rbtree_, name, _size)(const tree_t tree) \ + { \ + RBTREEI_CONTRACT (tree); \ + return tree->size; \ + } \ + \ + static inline void \ + CAT3(rbtreei_, name, _it)(CAT3(rbtree_it_, name,_t) it, \ + const CAT3(rbtree_, name,_t) tree, int child) \ + { \ + RBTREEI_CONTRACT (tree); \ + assert (it != NULL); \ + unsigned int cpt = 0; \ + if (tree->node != NULL) { \ + it->which[cpt] = child; \ + node_t *n = it->stack[cpt++] = tree->node; \ + while (n->child[child] != NULL) { \ + assert (cpt < RBTREEI_MAX_STACK); \ + n = n->child[child]; \ + it->which[cpt] = child; \ + it->stack[cpt++] = n; \ + } \ + assert (n == it->stack[cpt - 1]); \ + } \ + it->cpt = cpt; \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _it)(CAT3(rbtree_it_, name,_t) it, \ + const CAT3(rbtree_, name,_t) tree) \ + { \ + CAT3(rbtreei_, name, _it)(it, tree, 0); \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _it_last)(CAT3(rbtree_it_, name,_t) it, \ + const CAT3(rbtree_, name,_t) tree) \ + { \ + CAT3(rbtreei_, name, _it)(it, tree, 1); \ + } \ + \ + static inline bool \ + CAT3(rbtree_, name, _end_p)(const CAT3(rbtree_it_, name,_t) it) \ + { \ + assert (it != NULL); \ + return it->cpt == 0; \ + } \ + \ + static inline void \ + CAT3(rbtreei_, name, _next)(CAT3(rbtree_it_, name,_t) it, int child) \ + { \ + assert (it != NULL); \ + assert (child == 0 || child == 1); \ + unsigned int cpt = it->cpt - 1; \ + node_t *n = it->stack[cpt]; \ + const int right = 1 ^ child; \ + if (n->child[right] != NULL && it->which[cpt] == child) { \ + /* Going right */ \ + assert (cpt < RBTREEI_MAX_STACK); \ + n = n->child[right]; \ + it->which[cpt++] = right; \ + it->stack[cpt] = n; \ + it->which[cpt++] = child; \ + /* Going left */ \ + while (n->child[child] != NULL) { \ + assert (cpt < RBTREEI_MAX_STACK); \ + n = n->child[child]; \ + it->which[cpt] = child; \ + it->stack[cpt++] = n; \ + } \ + assert (n == it->stack[cpt - 1]); \ + } else { \ + /* Going up */ \ + while (cpt > 0 && it->which[cpt-1] == right) cpt--; \ + } \ + it->cpt = cpt; \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _next)(CAT3(rbtree_it_, name,_t) it) \ + { \ + CAT3(rbtreei_, name, _next)(it, 0); \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _previous)(CAT3(rbtree_it_, name,_t) it) \ + { \ + CAT3(rbtreei_, name, _next)(it, 1); \ + } \ + \ + static inline type * \ + CAT3(rbtree_, name, _ref)(const CAT3(rbtree_it_, name,_t) it) \ + { \ + assert(it != NULL && it->cpt > 0); \ + return &(it->stack[it->cpt-1]->data); \ + } \ + \ + static inline const type * \ + CAT3(rbtree_, name, _cref)(const CAT3(rbtree_it_, name,_t) it) \ + { \ + return CAT3(rbtree_, name, _const_cast)(CAT3(rbtree_, name, _ref)(it)); \ + } \ + \ + static inline bool \ + CAT3(rbtree_, name, _it_equal_p)(const CAT3(rbtree_it_, name,_t) it1, \ + const CAT3(rbtree_it_, name,_t) it2) \ + { \ + return it1->cpt == it2->cpt \ + && it1->stack[it1->cpt-1] == it2->stack[it2->cpt-1]; \ + } \ + \ + static inline type * \ + CAT3(rbtree_, name, _min)(const CAT3(rbtree_, name,_t) tree) \ + { \ + RBTREEI_CONTRACT (tree); \ + node_t *n = tree->node; \ + if (n == NULL) return NULL; \ + while (n->child[0] != NULL) { \ + RBTREEI_CONTRACT_NODE (n); \ + n = n->child[0]; \ + } \ + return &n->data; \ + } \ + \ + static inline type * \ + CAT3(rbtree_, name, _max)(const CAT3(rbtree_, name,_t) tree) \ + { \ + RBTREEI_CONTRACT (tree); \ + node_t *n = tree->node; \ + if (n == NULL) return NULL; \ + while (n->child[1] != NULL) { \ + RBTREEI_CONTRACT_NODE (n); \ + n = n->child[1]; \ + } \ + return &n->data; \ + } \ + \ + static inline const type * \ + CAT3(rbtree_, name, _cmin)(const CAT3(rbtree_, name,_t) tree) \ + { \ + return CAT3(rbtree_, name, _const_cast)(CAT3(rbtree_, name, _min)(tree)); \ + } \ + \ + static inline const type * \ + CAT3(rbtree_, name, _cmax)(const CAT3(rbtree_, name,_t) tree) \ + { \ + return CAT3(rbtree_, name, _const_cast)(CAT3(rbtree_, name, _max)(tree)); \ + } \ + \ + static inline type * \ + CAT3(rbtree_, name, _get)(const tree_t tree, type const data) \ + { \ + RBTREEI_CONTRACT (tree); \ + node_t *n = tree->node; \ + while (n != NULL) { \ + RBTREEI_CONTRACT_NODE (n); \ + int cmp = M_GET_CMP oplist (n->data, data); \ + if (cmp == 0) { \ + return &n->data; \ + } else if (cmp > 0) { \ + n = n->child[0]; \ + } else { \ + n = n->child[1]; \ + } \ + } \ + return NULL; \ + } \ + \ + static inline const type * \ + CAT3(rbtree_, name, _cget)(const CAT3(rbtree_, name,_t) tree, \ + type const data) \ + { \ + return CAT3(rbtree_, name, _const_cast)(CAT3(rbtree_, name, _get)(tree, data)); \ + } \ + \ + static inline node_t * \ + CAT3(rbtreei_,name,_copyn)(const node_t *o) \ + { \ + if (o == NULL) return NULL; \ + node_t *n = M_MEMORY_ALLOC(node_t); \ + M_GET_INIT_SET oplist (n->data, o->data); \ + n->child[0] = CAT3(rbtreei_,name,_copyn)(o->child[0]); \ + n->child[1] = CAT3(rbtreei_,name,_copyn)(o->child[1]); \ + RBTREEI_COPY_COLOR (n, o); \ + return n; \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _init_set)(tree_t tree, const tree_t ref) \ + { \ + RBTREEI_CONTRACT (ref); \ + assert (tree != NULL && tree != ref); \ + tree->size = ref->size; \ + tree->node = CAT3(rbtreei_,name,_copyn)(ref->node); \ + RBTREEI_CONTRACT (tree); \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _set)(tree_t tree, const tree_t ref) \ + { \ + RBTREEI_CONTRACT (tree); \ + RBTREEI_CONTRACT (ref); \ + if (tree == ref) return; \ + CAT3(rbtree_,name,_clear)(tree); \ + CAT3(rbtree_,name,_init_set)(tree, ref); \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _init_move)(tree_t tree, tree_t ref) \ + { \ + RBTREEI_CONTRACT (ref); \ + assert (tree != NULL && tree != ref); \ + tree->size = ref->size; \ + tree->node = ref->node; \ + ref->node = NULL; \ + ref->size = 0; \ + RBTREEI_CONTRACT (tree); \ + } \ + \ + static inline void \ + CAT3(rbtree_, name, _move)(tree_t tree, tree_t ref) \ + { \ + RBTREEI_CONTRACT (tree); \ + RBTREEI_CONTRACT (ref); \ + assert (tree != ref); \ + CAT3(rbtree_,name,_clear)(tree); \ + CAT3(rbtree_,name,_init_move)(tree, ref); \ + RBTREEI_CONTRACT (tree); \ + } \ + \ + static inline bool \ + CAT3(rbtree_, name, _empty_p)(const tree_t tree) \ + { \ + RBTREEI_CONTRACT (tree); \ + return tree->size == 0; \ + } \ + \ + static inline rbtreei_color_e \ + CAT3(rbtreei_, name, _get_color)(const node_t *n) \ + { \ + return (n == NULL) ? RBTREE_BLACK : n->color; \ + } \ + \ + static inline void \ + CAT3(rbtreei_, name, _set_color)(node_t *n, rbtreei_color_e c) \ + { \ + if (n != NULL) n->color = c; \ + } \ + \ + static inline node_t * \ + CAT3(rbtreei_, name, _rotate)(node_t *pp, node_t *ppp, const bool right) \ + { \ + assert (pp != NULL && ppp != NULL); \ + bool left = !right; \ + node_t *p = pp->child[right]; \ + assert (p != NULL); \ + pp->child[right] = p->child[left]; \ + p->child[left] = pp; \ + /* Fix grandparent with new parent */ \ + assert(ppp->child[0] == pp || ppp->child[1] == pp); \ + ppp->child[(ppp->child[0] != pp)] = p; \ + return p; \ + } \ + \ + static size_t \ + CAT3(rbtreei_,name,_depth)(const node_t *n) \ + { \ + if (n == NULL) return 1; \ + return (n->color == RBTREE_BLACK) \ + + CAT3(rbtreei_,name,_depth)(n->child[0]); \ + } \ + \ + static inline bool \ + CAT3(rbtree_, name, _pop)(type *data_ptr, tree_t tree, type const key) \ + { \ + RBTREEI_CONTRACT (tree); \ + node_t *tab[RBTREEI_MAX_STACK]; \ + unsigned char which[RBTREEI_MAX_STACK]; \ + unsigned int cpt = 0; \ + node_t *n = tree->node; \ + which[0] = 0; \ + tab[cpt++] = (node_t*) &tree->node; /* hug! */ \ + /* Search for the deletion point */ \ + tab[cpt] = n; \ + while (n != NULL) { \ + RBTREEI_CONTRACT_NODE (n); \ + assert(CAT3(rbtreei_,name,_depth)(n->child[0]) \ + == CAT3(rbtreei_,name,_depth)(n->child[1])); \ + int cmp = M_GET_CMP oplist (n->data, key); \ + if (cmp == 0) { \ + break; \ + } \ + int i = (cmp < 0); \ + which[cpt++] = i; \ + n = n->child[i]; \ + assert (cpt < RBTREEI_MAX_STACK); \ + tab[cpt] = n; \ + } \ + assert (tab[cpt] == n); \ + /* If not found, fail */ \ + if (n == NULL) { \ + return false; \ + } \ + unsigned int cpt_n = cpt; \ + node_t *v = n; /* the replacement node */ \ + node_t *u; /* the deleted node */ \ + rbtreei_color_e v_color = v->color; \ + /* Classical removal of a node from a binary tree */ \ + if (v->child[0] != NULL && v->child[1] != NULL) { \ + /* node has 2 child. */ \ + /* Get the element right next to the deleted one */ \ + v = v->child[1]; \ + which[cpt++] = 1; \ + tab[cpt] = v; \ + while (v != NULL) { \ + /* Always left node */ \ + RBTREEI_CONTRACT_NODE (v); \ + assert(CAT3(rbtreei_,name,_depth)(v->child[0]) \ + == CAT3(rbtreei_,name,_depth)(v->child[1])); \ + which[cpt++] = 0; \ + v = v->child[0]; \ + assert (cpt < RBTREEI_MAX_STACK); \ + tab[cpt] = v; \ + } \ + /* Pop the last element to get the last non-null element */ \ + v = tab[--cpt]; \ + assert (v != NULL); \ + u = v->child[1]; \ + /* Replace 'v' by 'u' in the tree */ \ + assert(tab[cpt-1]->child[which[cpt-1]] == v); \ + tab[cpt-1]->child[which[cpt-1]] = u; \ + /* Replace 'n' by 'v' in the tree */ \ + assert(tab[cpt_n-1] != NULL); \ + assert(tab[cpt_n-1]->child[which[cpt_n-1]] == n); \ + tab[cpt_n-1]->child[which[cpt_n-1]] = v; \ + v->child[0] = n->child[0]; \ + v->child[1] = n->child[1]; \ + v_color = v->color; \ + v->color = n->color; \ + tab[cpt_n] = v; \ + /* For the algorithm, 'u' is now the deleted node */ \ + } else { \ + /* 1 or no child to the node. Replace the element */ \ + v = n; \ + u = v->child[(n->child[0] == NULL)]; \ + assert (cpt_n >= 1 && tab[cpt_n-1]->child[which[cpt_n-1]] == n); \ + assert (n->child[(n->child[0] != NULL)] == NULL); \ + tab[cpt_n-1]->child[which[cpt_n-1]] = u; \ + /* in all cases, this node shall be set to black */ \ + } \ + \ + /* Rebalance from child to root */ \ + if (v_color == RBTREE_BLACK \ + && CAT3(rbtreei_,name,_get_color)(u) == RBTREE_BLACK) { \ + /* tab[0] is NULL, tab[1] is root, u is double black */ \ + node_t *p = u, *s; \ + while (cpt >= 2) { \ + p = tab[--cpt]; \ + bool nbChild = which[cpt]; \ + assert (p != NULL && u == p->child[nbChild]); \ + s = p->child[!nbChild]; \ + /* if sibling is red, perform a rotation to move sibling up */ \ + if (CAT3(rbtreei_,name,_get_color)(s) == RBTREE_RED) { \ + p = CAT3(rbtreei_,name,_rotate) (p, tab[cpt-1], !nbChild); \ + p->color = RBTREE_BLACK; /* was sibling */ \ + tab[cpt] = p; \ + which[cpt++] = nbChild; \ + p = p->child[nbChild]; /* was parent */ \ + assert (p != NULL); \ + p->color = RBTREE_RED; \ + s = p->child[!nbChild]; \ + assert (CAT3(rbtreei_,name,_get_color)(s) == RBTREE_BLACK); \ + } \ + assert (p != NULL && u == p->child[nbChild]); \ + /* if both childreen of s are black */ \ + /* perform recoloring and recur on parent if black */ \ + if (s != NULL \ + && CAT3(rbtreei_,name,_get_color)(s->child[0])== RBTREE_BLACK \ + && CAT3(rbtreei_,name,_get_color)(s->child[1])== RBTREE_BLACK) { \ + assert(CAT3(rbtreei_,name,_depth)(s->child[0]) == CAT3(rbtreei_,name,_depth)(s->child[1])); \ + s->color = RBTREE_RED; \ + if (p->color == RBTREE_RED) { \ + p->color = RBTREE_BLACK; \ + RBTREEI_CONTRACT_NODE(p); \ + assert(CAT3(rbtreei_,name,_depth)(p->child[0]) == CAT3(rbtreei_,name,_depth)(p->child[1])); \ + break; \ + } \ + u = p; \ + } else { \ + assert (s != NULL); \ + /* at least one child of 's' is red */ \ + /* perform rotation(s) */ \ + bool childIsRight = CAT3(rbtreei_,name,_get_color)(s->child[1])== RBTREE_RED; \ + rbtreei_color_e p_color = p->color; \ + if (childIsRight != nbChild) { \ + /* left-left or right-right case */ \ + p = CAT3(rbtreei_,name,_rotate) (p, tab[cpt-1], childIsRight); \ + } else { \ + s = CAT3(rbtreei_,name,_rotate) (s, p, childIsRight); \ + p = CAT3(rbtreei_,name,_rotate) (p, tab[cpt-1], !nbChild); \ + } \ + p->color = p_color; \ + assert(p->child[0] != NULL && p->child[1] != NULL); \ + p->child[0]->color = RBTREE_BLACK; \ + p->child[1]->color = RBTREE_BLACK; \ + RBTREEI_CONTRACT_NODE(p); \ + assert(CAT3(rbtreei_,name,_depth)(p->child[0]) == CAT3(rbtreei_,name,_depth)(p->child[1])); \ + break; \ + } \ + } /* while */ \ + if (cpt == 1 /* root has been reached? */ ) { \ + CAT3(rbtreei_,name,_set_color)(p, RBTREE_BLACK); \ + assert (tree->node == p); \ + } \ + } else { \ + CAT3(rbtreei_,name,_set_color)(u, RBTREE_BLACK); \ + } \ + assert (tree->node == NULL || tree->node->color == RBTREE_BLACK); \ + /* delete it */ \ + if (data_ptr != NULL) \ + M_GET_SET oplist (*data_ptr, n->data); \ + M_GET_CLEAR oplist (n->data); \ + M_MEMORY_FREE (n); \ + tree->size --; \ + RBTREEI_CONTRACT (tree); \ + return true; \ + } + + +// TODO: missing specialized iterator functions (it_from, it_to) +// TODO: _get_str, _in_str, _out_str: how to print them? +// TODO: specialized _sort shall do nothing, but shall check the requested order. How ? + + +#endif diff --git a/m-register.h b/m-register.h new file mode 100644 index 00000000..f22dbf4f --- /dev/null +++ b/m-register.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "m-core.h" + +#ifndef M_CLASS_CPT + +// TODO: Integrate default case with dynamic polymorphisme +// TODO: WIP... + +// Global counter +#define M_CLASS_CPT 0 + +// In case +#define M_REGISTER_TYPE "m-register.h" + +// TODO: Iterator shall also be considered as key +#define M_METHOD_EXPAND(method, id) \ + CAT3(CLASS_,id,_TYPE)*: CAT3(CLASS_,id,_DEFINITION).method, \ + const CAT3(CLASS_,id,_TYPE)*: CAT3(CLASS_,id,_DEFINITION).method + +// TODO: How to define Default method if there is no registered method? +// the current way may produce syntaxically invalid C due to macro expanding to +// wrong term... +#define M_CALL_METHOD(method, def, ...) \ + _Generic(&(M_RET_ARG1(__VA_ARGS__, )), \ + M_SEQ2(0,DEC(CLASS_CPT),M_METHOD_EXPAND, method))(__VA_ARGS__) \ + +#define M_CALL_METHOD2(method, def, ...) \ + _Generic(&(M_RET_ARG2(__VA_ARGS__, )), \ + M_SEQ2(0,DEC(CLASS_CPT),M_METHOD_EXPAND, method))(__VA_ARGS__) \ + +#define M_CLASS_DEFINE_FIELD(a) M_APPLY(M_CLASS_DEFINE_FIELD0 , CAT(M_PROTO_TUPLE_, a)) +#define M_CLASS_M_GET_FUNCPTR(a) M_APPLY(M_CLASS_M_GET_FUNCPTR0, CAT(M_PROTO_TUPLE_, a)) +#define M_CLASS_DEFINE_TYPE(a) M_APPLY(M_CLASS_DEFINE_TYPE0 , CAT(M_PROTO_TUPLE_, a)) +#define M_CLASS_DEFINE_FIELD0(a, b) M_IF(M_BOOL(CAT(M_PROTO_ISMETHOD_, a)))(CAT(M_PROTO_METHOD_, a)(TYPE, a);,/*nothing*/) +#define M_CLASS_M_GET_FUNCPTR0(a,b) M_IF(M_BOOL(CAT(M_PROTO_ISMETHOD_, a)))(.a = b M_DEFERRED_COMMA, /*nothing*/) +#define M_CLASS_DEFINE_TYPE0(a, b) M_IF(M_BOOL(CAT(M_PROTO_ISMETHOD_, a)))(/*nothing*/, typedef b CAT(TYPE_, a);) + +/* Define basic supported methods and types */ +#define M_PROTO_TUPLE_INIT(value) INIT, value +#define M_PROTO_TUPLE_INIT_SET(value) INIT_SET, value +#define M_PROTO_TUPLE_SET(value) SET, value +#define M_PROTO_TUPLE_CLEAR(value) CLEAR, value +#define M_PROTO_TUPLE_EQUAL(value) EQUAL, value +#define M_PROTO_TUPLE_IT_TYPE(value) IT_TYPE, value +#define M_PROTO_TUPLE_PUSH(value) PUSH, value +#define M_PROTO_TUPLE_POP(value) POP, value +#define M_PROTO_TUPLE_PRINT(value) PRINT, value +#define M_PROTO_TUPLE_TOSTR(value) TOSTR, value + +// Define methods +#define M_PROTO_METHOD_INIT(type, alias) void (*alias)(type) +#define M_PROTO_METHOD_INIT_SET(type, alias) void (*alias)(type, const type) +#define M_PROTO_METHOD_SET(type, alias) void (*alias)(type, const type) +#define M_PROTO_METHOD_CLEAR(type, alias) void (*alias)(type) +#define M_PROTO_METHOD_EQUAL(type, alias) bool (*alias)(const type, const type) +#define M_PROTO_METHOD_PUSH(type, alias) void (*alias)(type, const CAT(type, _TYPE)) +#define M_PROTO_METHOD_POP(type, alias) void (*alias)(CAT(type, _TYPE)*, type) +#define M_PROTO_METHOD_PRINT(type, alias) void (*alias)(const type) +#define M_PROTO_METHOD_TOSTR(type, alias) void (*alias)(string_t, const type) + +// Define types +#define M_PROTO_ISMETHOD_IT_TYPE 0 +#define M_PROTO_ISMETHOD_TYPE 0 + +#define INIT_DEFLIST \ + char: "%c", \ + signed char: "%hhd", \ + unsigned char: "%hhu", \ + signed short: "%hd", \ + unsigned short: "%hu", \ + signed int: "%d", \ + unsigned int: "%u", \ + long int: "%ld", \ + unsigned long int: "%lu", \ + long long int: "%lld", \ + unsigned long long int: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%Lf", \ + const char *: "%s", \ + char *: "%s", \ + const void *: "%p", \ + void *: "%p" + +#define new(type) ({type *tmp = malloc(sizeof (type)); init(*tmp); tmp; }) +#define init(...) M_CALL_METHOD(INIT, M_INIT_DEFAULT, __VA_ARGS__) +#define init_set(...) M_CALL_METHOD(INIT_SET, M_SET_DEFAULT, __VA_ARGS__) +#define set(...) M_CALL_METHOD(SET, M_SET_DEFAULT, __VA_ARGS__) +#define clear(...) M_CALL_METHOD(CLEAR, M_CLEAR_DEFAULT, __VA_ARGS__) +#define delete(obj) (clear(*obj), free(obj)) +#define equal(...) M_CALL_METHOD(EQUAL, M_EQUAL_DEFAULT, __VA_ARGS__) +#define push(...) M_CALL_METHOD(PUSH, M_NO_DEFAULT, __VA_ARGS__) +#define pop(...) M_CALL_METHOD2(POP, M_NO_DEFAULT, __VA_ARGS__) +#define print_arg(...) M_CALL_METHOD(PRINT, M_PRINT_ARG, __VA_ARGS__) ; +#define print(...) do { M_MAP(print_arg, __VA_ARGS__) } while (0) + +#endif + +#ifdef TYPE + +#ifndef OPLIST +# error "OPLIST macro not defined." +#endif + +/* Do the stuff */ +#if CLASS_CPT == 0 + +typedef TYPE CLASS_0_TYPE; +M_MAP(M_CLASS_DEFINE_TYPE, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +static const struct { + M_MAP(M_CLASS_DEFINE_FIELD, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +} CLASS_0_DEFINITION = { + M_MAP(M_CLASS_M_GET_FUNCPTR, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +}; +# undef TYPE +# undef OPLIST +# undef CLASS_CPT +# define CLASS_CPT 1 + +#elif CLASS_CPT == 1 + +typedef TYPE CLASS_1_TYPE; +M_MAP(M_CLASS_DEFINE_TYPE, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +static const struct { + M_MAP(M_CLASS_DEFINE_FIELD, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +} CLASS_1_DEFINITION = { + M_MAP(M_CLASS_M_GET_FUNCPTR, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +}; +# undef TYPE +# undef OPLIST +# undef CLASS_CPT +# define CLASS_CPT 2 + +#elif CLASS_CPT == 2 + +typedef TYPE CLASS_2_TYPE; +M_MAP(M_CLASS_DEFINE_TYPE, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +static const struct { + M_MAP(M_CLASS_DEFINE_FIELD, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +} CLASS_2_DEFINITION = { + M_MAP(M_CLASS_M_GET_FUNCPTR, M_OPLIST_APPLY(M_OPLIST_FLAT, OPLIST)) +}; +# undef TYPE +# undef OPLIST +# undef CLASS_CPT +# define CLASS_CPT 3 + +#else +# error "Out of scope" +#endif + +#endif diff --git a/m-shared.h b/m-shared.h new file mode 100644 index 00000000..f92db560 --- /dev/null +++ b/m-shared.h @@ -0,0 +1,188 @@ +/* + * MLIB - SHARED PTR Module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_SHARED_PTR__H +#define __M_SHARED_PTR__H + +#include +#include +#include + +#include + +#include "m-core.h" + +#define SHARED_PTR_OPLIST(name) ( \ + INIT(CAT3(shared_, name, _init)), \ + CLEAR(CAT3(shared_, name, _clear)), \ + INIT_SET(CAT3(shared_, name, _init_set)), \ + SET(CAT3(shared_, name, _set)) \ + INIT_MOVE(CAT3(shared_, name, _init_move)), \ + MOVE(CAT3(shared_, name, _move)) \ + ) + +#define SHARED_PTR_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (SHAREDI_PTR_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST ), \ + SHAREDI_PTR_DEF2(name, __VA_ARGS__ )) + +/********************************** INTERNAL ************************************/ + +#define SHAREDI_PTR_DEF2(name, type, oplist) \ + typedef struct CAT3(shared_, name, _s){ \ + int cpt; \ + type *data; \ + } *CAT3(shared_, name, _t)[1]; \ + \ + typedef union { \ + type *ptr; \ + const type *cptr; \ + } CAT3(shared_union_, name,_t); \ + \ + static inline const type * \ + CAT3(shared_, name, _const_cast)(type *ptr) \ + { \ + CAT3(shared_union_, name,_t) u; \ + u.ptr = ptr; \ + return u.cptr; \ + } \ + \ + static inline void \ + CAT3(shared_, name, _init)(CAT3(shared_, name, _t) shared) \ + { \ + *shared = NULL; \ + } \ + \ + static inline void \ + CAT3(shared_, name, _init2)(CAT3(shared_, name, _t) shared, type *data) \ + { \ + struct CAT3(shared_, name, _s) *ptr; \ + if (data == NULL) { \ + *shared = NULL; \ + return; \ + } \ + ptr = M_MEMORY_ALLOC (struct CAT3(shared_, name, _s)); \ + if (ptr == NULL) \ + M_MEMORY_FULL(sizeof(struct CAT3(shared_, name, _s))); \ + ptr->cpt = 1; \ + ptr->data = data; \ + *shared = ptr; \ + SHAREDI_CONTRACT(shared); \ + } \ + \ + static inline bool \ + CAT3(shared_, name, _NULL_p)(const CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(shared); \ + return *shared == NULL; \ + } \ + \ + static inline void \ + CAT3(shared_, name, _init_set)(CAT3(shared_, name, _t) dest, \ + const CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(shared); \ + assert (dest != shared); \ + *dest = *shared; \ + if (*dest != NULL) \ + atomic_fetch_add(&((*dest)->cpt), 1); \ + SHAREDI_CONTRACT(dest); \ + } \ + \ + static inline void \ + CAT3(shared_, name, _release)(CAT3(shared_, name, _t) dest) \ + { \ + SHAREDI_CONTRACT(dest); \ + if (*dest != NULL) { \ + if (atomic_fetch_sub(&((*dest)->cpt), 1) == 1) { \ + M_GET_CLEAR oplist (*(*dest)->data); \ + M_GET_DEL oplist ((*dest)->data); \ + M_MEMORY_FREE (*dest); \ + } \ + *dest = NULL; \ + } \ + SHAREDI_CONTRACT(dest); \ + } \ + \ + static inline void \ + CAT3(shared_, name, _clear)(CAT3(shared_, name, _t) dest) \ + { \ + SHAREDI_CONTRACT(dest); \ + CAT3(shared_, name, _release)(dest); \ + } \ + \ + static inline void \ + CAT3(shared_, name, _set)(CAT3(shared_, name, _t) dest, \ + const CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(dest); \ + SHAREDI_CONTRACT(shared); \ + CAT3(shared_, name, _release)(dest); \ + CAT3(shared_, name, _init_set)(dest, shared); \ + } \ + \ + static inline void \ + CAT3(shared_, name, _init_move)(CAT3(shared_, name, _t) dest, \ + CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(shared); \ + assert (dest != NULL && dest != shared); \ + *dest = *shared; \ + *shared = NULL; \ + SHAREDI_CONTRACT(dest); \ + } \ + \ + static inline void \ + CAT3(shared_, name, _move)(CAT3(shared_, name, _t) dest, \ + CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(dest); \ + SHAREDI_CONTRACT(shared); \ + assert (dest != shared); \ + CAT3(shared_, name, _clear)(dest); \ + CAT3(shared_, name, _init_move)(dest, shared); \ + } \ + \ + static inline const type * \ + CAT3(shared_, name, _cref)(const CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(shared); \ + return CAT3(shared_, name, _const_cast) ((*shared)->data); \ + } \ + \ + static inline type * \ + CAT3(shared_, name, _ref)(CAT3(shared_, name, _t) shared) \ + { \ + SHAREDI_CONTRACT(shared); \ + return (*shared)->data; \ + } \ + +/********************************** INTERNAL ************************************/ + +#define SHAREDI_CONTRACT(shared) do { \ + assert(shared != NULL); \ + assert(*shared == NULL || (*shared)->cpt >= 1); \ + } while (0) + +#endif diff --git a/m-snapshot.h b/m-snapshot.h new file mode 100644 index 00000000..dbe277d5 --- /dev/null +++ b/m-snapshot.h @@ -0,0 +1,200 @@ +/* + * MLIB - SNAPSHOT Module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_SNAPSHOT +#define __M_SNAPSHOT + +#include +#include +#include + +#include "m-core.h" + +/* Public interface of a snapshot */ +#define SNAPSHOT_DEF(name, ...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (SNAPSHOTI_DEF2(name, __VA_ARGS__, () ), \ + SNAPSHOTI_DEF2(name, __VA_ARGS__ )) + +/* Define the oplist of a snapshot */ +#define SNAPSHOT_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (SNAPSHOTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \ + SNAPSHOTI_OPLIST(__VA_ARGS__ )) + +/********************************** INTERNAL ************************************/ + +/* Define the oplist of a snapshot */ +#define SNAPSHOTI_OPLIST(type, oplist) \ + (INIT(CAT3(snapshot_, type, _init)), \ + INIT_SET(CAT3(snapshot_, type, _init_set)), \ + SET(CAT3(snapshot_, type, _set)), \ + CLEAR(CAT3(snapshot_, type, _clear)), \ + TYPE(CAT3(snapshot_, type, _t)), \ + SUBTYPE(CAT3(snapshot_type_, name, _t)) \ + ,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(CAT3(snapshot_, name, _init_move)),) \ + ,M_IF_METHOD(MOVE, oplist)(MOVE(CAT3(snapshot_, name, _move)),) \ + ) + +#define SNAPSHOTI_FLAG(r, w, f, b) \ + ( ( (r) << 4) | ((w) << 2) | ((f)) | ((b) << 6)) +#define SNAPSHOTI_R(flags) \ + (((flags) >> 4) & 0x03u) +#define SNAPSHOTI_W(flags) \ + (((flags) >> 2) & 0x03u) +#define SNAPSHOTI_F(flags) \ + (((flags) >> 0) & 0x03u) +#define SNAPSHOTI_B(flags) \ + (((flags) >> 6) & 0x01u) + +#define SNAPSHOTI_FLAGS_CONTRACT(flags) \ + assert(SNAPSHOTI_R(flags) != SNAPSHOTI_W(flags) \ + && SNAPSHOTI_R(flags) != SNAPSHOTI_F(flags) \ + && SNAPSHOTI_W(flags) != SNAPSHOTI_F(flags)) +#define SNAPSHOTI_CONTRACT(snap) do { \ + assert((snap) != NULL); \ + SNAPSHOTI_FLAGS_CONTRACT((snap)->flags); \ + } while (0) + +#define SNAPSHOTI_DEF2(name, type, oplist) \ + typedef struct CAT3(snapshot_, name, _s) { \ + type data[3]; \ + atomic_uint_fast8_t flags; \ + } CAT3(snapshot_, name, _t)[1]; \ + typedef type CAT3(snapshot_type_, name, _t); \ + \ + static inline void CAT3(snapshot_, name, _init)(CAT3(snapshot_, name, _t) snap) \ + { \ + assert(snap != NULL); \ + for(int i = 0; i < 3; i++) { \ + M_GET_INIT oplist(snap->data[i]); \ + } \ + snap->flags = ATOMIC_VAR_INIT (SNAPSHOTI_FLAG(0, 1, 2, 0)); \ + } \ + \ + static inline void CAT3(snapshot_, name, _clear)(CAT3(snapshot_, name, _t) snap) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + for(int i = 0; i < 3; i++) { \ + M_GET_CLEAR oplist(snap->data[i]); \ + } \ + } \ + \ + static inline void CAT3(snapshot_, name, _init_set)(CAT3(snapshot_, name, _t) snap, \ + const CAT3(snapshot_, name, _t) org) \ + { \ + SNAPSHOTI_CONTRACT(org); \ + assert(snap != NULL && snap != org); \ + for(int i = 0; i < 3; i++) { \ + M_GET_INIT_SET oplist(snap->data[i], org->data[i]); \ + } \ + snap->flags = org->flags; \ + SNAPSHOTI_CONTRACT(snap); \ + } \ + \ + static inline void CAT3(snapshot_, name, _set)(CAT3(snapshot_, name, _t) snap, \ + const CAT3(snapshot_, name, _t) org) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + SNAPSHOTI_CONTRACT(org); \ + for(int i = 0; i < 3; i++) { \ + M_GET_SET oplist(snap->data[i], org->data[i]); \ + } \ + snap->flags = org->flags; \ + SNAPSHOTI_CONTRACT(snap); \ + } \ + \ + M_IF_METHOD(INIT_MOVE, oplist)( \ + static inline void CAT3(snapshot_, name, _init_move)(CAT3(snapshot_, name, _t) snap, \ + CAT3(snapshot_, name, _t) org) \ + { \ + SNAPSHOTI_CONTRACT(org); \ + assert(snap != NULL && snap != org); \ + for(int i = 0; i < 3; i++) { \ + M_GET_INIT_MOVE oplist(snap->data[i], org->data[i]); \ + } \ + snap->flags = org->flags; \ + org->flags = 0; \ + SNAPSHOTI_CONTRACT(snap); \ + } \ + ,) /* IF_METHOD (INIT_MOVE) */ \ + \ + M_IF_METHOD(MOVE, oplist)( \ + static inline void CAT3(snapshot_, name, _move)(CAT3(snapshot_, name, _t) snap, \ + CAT3(snapshot_, name, _t) org) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + SNAPSHOTI_CONTRACT(org); \ + assert(snap != org); \ + for(int i = 0; i < 3; i++) { \ + M_GET_MOVE oplist(snap->data[i], org->data[i]); \ + } \ + snap->flags = org->flags; \ + org->flags = 0; \ + SNAPSHOTI_CONTRACT(snap); \ + } \ + ,) /* IF_METHOD (MOVE) */ \ + \ + static inline type *CAT3(snapshot_, name, _take)(CAT3(snapshot_, name, _t) snap) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + uint_fast8_t nextFlags, origFlags = atomic_load(&snap->flags); \ + do { \ + nextFlags = SNAPSHOTI_FLAG(SNAPSHOTI_R(origFlags), \ + SNAPSHOTI_F(origFlags), \ + SNAPSHOTI_W(origFlags), 1); \ + } while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \ + nextFlags)); \ + return &snap->data[SNAPSHOTI_W(nextFlags)]; \ + } \ + \ + static inline const type *CAT3(snapshot_, name, _look)(const CAT3(snapshot_, name, _t) snap) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + uint_fast8_t nextFlags, origFlags = atomic_load(&snap->flags); \ + do { \ + if (!SNAPSHOTI_B(origFlags)) \ + break; \ + nextFlags = SNAPSHOTI_FLAG(SNAPSHOTI_F(origFlags), \ + SNAPSHOTI_W(origFlags), \ + SNAPSHOTI_R(origFlags), 0); \ + } while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \ + nextFlags)); \ + return &snap->data[SNAPSHOTI_R(snap->flags)]; \ + } \ + \ + static inline type *CAT3(snapshot_, name, _get_produced)(CAT3(snapshot_, name, _t) snap) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + return &snap->data[SNAPSHOTI_W(snap->flags)]; \ + } \ + \ + static inline const type *CAT3(snapshot_, name, _get_consummed)(const CAT3(snapshot_, name, _t) snap) \ + { \ + SNAPSHOTI_CONTRACT(snap); \ + return &snap->data[SNAPSHOTI_R(snap->flags)]; \ + } \ + + +#endif diff --git a/m-string.h b/m-string.h new file mode 100644 index 00000000..64558265 --- /dev/null +++ b/m-string.h @@ -0,0 +1,685 @@ +/* + * MLIB - Dynamic Size String Module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_STRING_H +#define __M_STRING_H + +#include +#include +#include +#include +#include +#include +#include + +#include "m-core.h" + +/********************************** INTERNAL ************************************/ + +// This macro defines the contract of a string. +// Note: A ==> B is represented as not(A) or B +#define STRING_CONTRACT(v) \ + M_ASSUME (v != NULL); \ + M_ASSUME (v->ptr != NULL || (v->size == 0 && v->alloc == 0)); \ + M_ASSUME (v->ptr == NULL || strlen(v->ptr) == v->size); \ + M_ASSUME (v->ptr == NULL || v->ptr[v->size] == 0); \ + M_ASSUME (v->ptr == NULL || v->size < v->alloc); + + +/********************************** EXTERNAL ************************************/ + +#define STRING_FAILURE ((size_t)-1) + +typedef struct string_s { + size_t size, alloc; + char *ptr; +} string_t[1]; + +typedef enum { + STRING_READ_LINE = 0, STRING_READ_PURE_LINE = 1, STRING_READ_FILE = 2 +} string_fgets_t; + +static inline void +string_init(string_t v) +{ + v->size = v->alloc = 0; + v->ptr = NULL; + STRING_CONTRACT(v); +} + +static inline void +string_clear(string_t v) +{ + STRING_CONTRACT(v); + M_MEMORY_FREE(v->ptr); + v->size = v->alloc = 0; + v->ptr = NULL; +} + +static inline void string_clear2(string_t *v) { string_clear(*v); } + +static inline char * +string_clear_get_str(string_t v) +{ + STRING_CONTRACT(v); + char *p = v->ptr; + v->size = v->alloc = 0; + v->ptr = NULL; + return p; +} + +static inline void +string_clean(string_t v) +{ + STRING_CONTRACT (v); + v->size = 0; + if (M_LIKELY (v->ptr)) + v->ptr[0] = 0; + STRING_CONTRACT (v); +} + +static inline size_t +string_get_length(const string_t v) +{ + STRING_CONTRACT (v); + return v->size; +} + +static inline char +string_get_char(const string_t v, size_t index) +{ + STRING_CONTRACT (v); + M_ASSUME( index < v->size); + return v->ptr[index]; +} + +static inline bool +string_empty_p(const string_t v) +{ + STRING_CONTRACT (v); + return v->size == 0; +} + +static inline void +stringi_fit2size (string_t v, size_t size) +{ + // Note: this function may be called in context where the contract + // is not fullfilled. + if (size > v->alloc) { + size_t alloc = size < 16 ? 16 : (size + size / 2); + char *ptr = M_MEMORY_REALLOC (char, v->ptr, alloc); + if (ptr == NULL) { + M_MEMORY_FULL(sizeof (char) * alloc); + return; + } + v->ptr = ptr; + v->alloc = alloc; + } +} + +static inline void +string_shrink2fit(string_t v) +{ + STRING_CONTRACT (v); + char *ptr = M_MEMORY_REALLOC (char, v->ptr, v->size+1); + M_ASSUME (ptr != NULL); + v->ptr = ptr; // Can be != ptr if v->ptr was NULL before. + v->alloc = v->size+1; + STRING_CONTRACT (v); +} + +static inline void +string_set_str(string_t v, const char str[]) +{ + STRING_CONTRACT (v); + M_ASSUME(str != NULL); + size_t size = strlen(str); + stringi_fit2size(v, size+1); + memcpy(v->ptr, str, size+1); + v->size = size; + STRING_CONTRACT (v); +} + +static inline void +string_set_strn(string_t v, const char str[], size_t n) +{ + STRING_CONTRACT (v); + M_ASSUME(str != NULL); + size_t size = M_MIN (strlen(str), n); + stringi_fit2size(v, size+1); + memcpy(v->ptr, str, size); + v->ptr[size] = 0; + v->size = size; + STRING_CONTRACT (v); +} + +static inline const char* +string_get_cstr(const string_t v) +{ + STRING_CONTRACT (v); + return M_UNLIKELY (v->ptr == NULL) ? "" : v->ptr; +} + +static inline void +string_set (string_t v1, const string_t v2) +{ + STRING_CONTRACT (v2); + string_set_str (v1, string_get_cstr(v2)); +} + +static inline void +string_init_set(string_t v1, const string_t v2) +{ + string_init(v1); + string_set(v1,v2); +} + +static inline void +string_init_set_str(string_t v1, const char str[]) +{ + string_init(v1); + string_set_str(v1, str); +} + +static inline void +string_init_move(string_t v1, string_t v2) +{ + STRING_CONTRACT (v2); + v1->size = v2->size; + v1->alloc = v2->alloc; + v1->ptr = v2->ptr; + v2->alloc = 0; + v2->ptr = NULL; + STRING_CONTRACT (v1); +} + +static inline void +string_move(string_t v1, string_t v2) +{ + string_clear(v1); + string_init_move(v1,v2); +} + +static inline void +string_push_back (string_t v, char c) +{ + STRING_CONTRACT (v); + stringi_fit2size(v, v->size+2); + v->ptr[v->size++] = c; + v->ptr[v->size] = 0; + STRING_CONTRACT (v); +} + +static inline void +string_cat_str(string_t v, const char str[]) +{ + STRING_CONTRACT (v); + size_t size = strlen(str); + stringi_fit2size(v, v->size + size + 1); + memcpy(&v->ptr[v->size], str, size + 1); + v->size += size; + STRING_CONTRACT (v); +} + +static inline void +string_cat(string_t v, const string_t v2) +{ + STRING_CONTRACT (v2); + string_cat_str(v, string_get_cstr(v2)); +} + +static inline int +string_cmp_str(const string_t v1, const char str[]) +{ + STRING_CONTRACT (v1); + return strcmp(string_get_cstr(v1), str); +} + +static inline int +string_cmp(const string_t v1, const string_t v2) +{ + STRING_CONTRACT (v1); + STRING_CONTRACT (v2); + return strcmp(string_get_cstr(v1), string_get_cstr(v2)); +} + +static inline bool +string_equal_str_p(const string_t v1, const char str[]) +{ + STRING_CONTRACT(v1); + M_ASSUME (str != NULL); + return string_cmp_str(v1, str) == 0; +} + +static inline bool +string_equal_p(const string_t v1, const string_t v2) +{ + STRING_CONTRACT(v1); + STRING_CONTRACT(v2); + return v1->size == v2->size && string_cmp(v1, v2) == 0; +} + +// Note: doesn't work with UTF-8 strings... +static inline int +string_cmpi(const string_t v1, const string_t v2) +{ + STRING_CONTRACT (v1); + STRING_CONTRACT (v2); + // strcasecmp is POSIX + const char *p1 = string_get_cstr(v1), *p2 = string_get_cstr(v2); + int c1, c2; + do { + // To avoid locale without 1 per 1 mapping. + c1 = tolower((unsigned char) toupper((unsigned char) *p1++)); + c2 = tolower((unsigned char) toupper((unsigned char) *p2++)); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} + +static inline size_t +string_search_char (const string_t v, char c) +{ + STRING_CONTRACT (v); + const char *p = (const char*) strchr(string_get_cstr(v), c); + return p == NULL ? STRING_FAILURE : (size_t) (p-string_get_cstr(v)); +} + +static inline size_t +string_search_rchar (const string_t v, char c) +{ + STRING_CONTRACT (v); + const char *p = (const char*) strrchr(string_get_cstr(v), c); + return p == NULL ? STRING_FAILURE : (size_t) (p-string_get_cstr(v)); +} + +static inline size_t +string_search_str (const string_t v, const char str[]) +{ + STRING_CONTRACT (v); + const char *p = (const char*) strstr(string_get_cstr(v), str); + return p == NULL ? STRING_FAILURE : (size_t) (p-string_get_cstr(v)); +} + +static inline size_t +string_search_string (const string_t v1, const string_t v2) +{ + STRING_CONTRACT (v2); + return string_search_str(v1, string_get_cstr(v2)); +} + +static inline int +string_strcoll_str(const string_t v, const char str[]) +{ + STRING_CONTRACT (v); + return strcoll(string_get_cstr(v), str); +} + +static inline int +string_strcoll (const string_t v1, const string_t v2) +{ + STRING_CONTRACT (v2); + return string_strcoll_str(v1, string_get_cstr(v2)); +} + +static inline size_t +string_spn(const string_t v1, const char accept[]) +{ + STRING_CONTRACT (v1); + M_ASSUME (accept != NULL); + return strspn(string_get_cstr(v1), accept); +} + +static inline size_t +string_cspn(const string_t v1, const char accept[]) +{ + STRING_CONTRACT (v1); + M_ASSUME (accept != NULL); + return strcspn(string_get_cstr(v1), accept); +} + +static inline void +string_left(string_t v, size_t index) +{ + STRING_CONTRACT (v); + if (index >= v->size) + return; + M_ASSUME (v->ptr != NULL); + v->ptr[index] = 0; + v->size = index; + STRING_CONTRACT (v); +} + +// Note: this is an index, not the size to keep from the right ! +static inline void +string_right(string_t v, size_t index) +{ + STRING_CONTRACT (v); + if (index >= v->size) { + if (v->ptr != NULL) { + v->ptr[0] = 0; + v->size = 0; + } + STRING_CONTRACT (v); + return; + } + M_ASSUME (v->ptr != NULL); + size_t s2 = v->size - index; + memmove (&v->ptr[0], &v->ptr[index], s2+1); + v->size = s2; + STRING_CONTRACT (v); +} + +static inline void +string_mid (string_t v, size_t index, size_t size) +{ + string_right(v, index); + string_left(v, size); +} + +static inline bool +string_replace_str (string_t v, const char str1[], const char str2[]) +{ + STRING_CONTRACT (v); + M_ASSUME (str1 != NULL && str2 != NULL); + size_t i = string_search_str(v, str1); + if (i != STRING_FAILURE) { + size_t str1_l = strlen(str1); + size_t str2_l = strlen(str2); + stringi_fit2size (v, v->size + str2_l - str1_l + 1); + if (str1_l != str2_l) + memmove(&v->ptr[i+str2_l], &v->ptr[i+str1_l], v->size - i - str1_l + 1); + memcpy (&v->ptr[i], str2, str2_l); + v->size += str2_l - str1_l; + STRING_CONTRACT (v); + return true; + } + return false; +} + +static inline bool +string_replace_string (string_t v, const string_t v1, const string_t v2) +{ + STRING_CONTRACT (v); + STRING_CONTRACT (v1); + STRING_CONTRACT (v2); + return string_replace_str(v, string_get_cstr(v1), string_get_cstr(v2)); +} + +static inline int +string_printf (string_t v, const char format[], ...) +{ + STRING_CONTRACT (v); + va_list args; + int size; + va_start (args, format); + size = vsnprintf (v->ptr, v->alloc, format, args); + if (size > 0 && ((size_t) size+1 >= v->alloc) ) { + // We have to realloc our string to fit the needed size + stringi_fit2size (v, (size_t) size + 1); + // and redo the parsing. + va_end (args); + va_start (args, format); + size = vsnprintf (v->ptr, v->alloc, format, args); + } + if (size >= 0) { + v->size = (size_t) size; + } + va_end (args); + STRING_CONTRACT (v); + return size; +} + +static inline int +string_cat_printf (string_t v, const char format[], ...) +{ + STRING_CONTRACT (v); + va_list args; + int size; + va_start (args, format); + size = vsnprintf (&v->ptr[v->size], v->alloc - v->size, format, args); + if (size > 0 && (v->size+(size_t)size+1 >= v->alloc) ) { + // We have to realloc our string to fit the needed size + stringi_fit2size (v, v->size + (size_t) size + 1); + // and redo the parsing. + va_end (args); + va_start (args, format); + size = vsnprintf (&v->ptr[v->size], v->alloc - v->size, format, args); + } + if (size >= 0) { + v->size += (size_t) size; + } + va_end (args); + STRING_CONTRACT (v); + return size; +} + +static inline void +string_fgets(string_t v, FILE *f, string_fgets_t arg) +{ + STRING_CONTRACT(v); + stringi_fit2size (v, 100); + M_ASSUME(v->ptr != NULL); + v->size = 0; + v->ptr[0] = 0; + while (!feof (f) && !ferror(f)) { + fgets(&v->ptr[v->size], v->alloc - v->size, f); + char *p = strchr(&v->ptr[v->size], '\n'); + v->size += strlen(&v->ptr[v->size]); + STRING_CONTRACT(v); + if (arg != STRING_READ_FILE && p != NULL) { + if (arg == STRING_READ_PURE_LINE) { + *p = 0; + v->size --; + } + STRING_CONTRACT(v); + return; + } else if (p == NULL) + stringi_fit2size (v, v->alloc + v->alloc/2); + } + STRING_CONTRACT (v); +} + +static inline void +string_fputs(FILE *f, const string_t v) +{ + STRING_CONTRACT (v); + fputs(string_get_cstr(v), f); +} + +static inline bool +string_start_with_str_p(const string_t v, const char *str) +{ + STRING_CONTRACT (v); + M_ASSUME (str != NULL); + const char *v_str = string_get_cstr(v); + while (*str){ + if (*str != *v_str) + return false; + str++; + v_str++; + } + return true; +} + +static inline bool +string_start_with_string_p(const string_t v, const string_t v2) +{ + STRING_CONTRACT (v2); + return string_start_with_str_p (v, string_get_cstr(v2)); +} + +static inline size_t +string_hash(const string_t v) +{ + STRING_CONTRACT (v); + const char *p = string_get_cstr(v); + size_t hash = 0; + char c; + do { + c = *p++; + hash = hash * 31421 + (unsigned int) c + 6297; + } while (c != 0); + return hash; +} + +static inline void +string_strim(string_t v) +{ + STRING_CONTRACT (v); + char *b = v->ptr; + size_t size = v->size; + while (size > 0 && (b[size-1] == ' ' || b[size-1] == '\n' || b[size-1] == '\r' || b[size-1] == '\t')) + size --; + if (size == 0) { + v->ptr[0] = 0; + v->size = size; + return; + } + while (*b == ' ' || *b == '\n' || *b == '\r' || *b == '\t') + b++; + M_ASSUME (b >= v->ptr && size >= (size_t) (b - v->ptr) ); + size -= (b - v->ptr); + memmove (v->ptr, b, size); + v->ptr[size] = 0; + v->size = size; + STRING_CONTRACT (v); +} + +/* I/O */ +static inline void +string_get_str(string_t v, const string_t v2, bool append) +{ + /* Output: "string" with quote around + Replace " by \" within the string */ + STRING_CONTRACT(v2); + STRING_CONTRACT(v); + M_ASSUME (v != v2); // Limitation + if(append==false) v->size = 0; + size_t targetSize = v->size + v2->size + 3; + stringi_fit2size(v, targetSize); + v->ptr[v->size ++] = '"'; + for(size_t i = 0 ; i < v2->size; i++) { + const char c = v2->ptr[i]; + if (c == '"') { + targetSize ++ ; + stringi_fit2size(v, targetSize); + v->ptr[v->size ++] = '\\'; + } + v->ptr[v->size ++] = c; + } + v->ptr[v->size ++] = '"'; + v->ptr[v->size] = 0; + M_ASSUME (v->size <= targetSize); + STRING_CONTRACT (v); +} + +static inline void +string_out_str(FILE *f, const string_t v) +{ + /* Output: "string" with quote around + Replace " by \" within the string */ + STRING_CONTRACT(v); + M_ASSUME (f != NULL); + fputc('"', f); + for(size_t i = 0 ; i < v->size; i++) { + const char c = v->ptr[i]; + if (c == '"') { + fputc('\\', f); + } + fputc(c, f); + } + fputc('"', f); +} + +static inline bool +string_in_str(string_t v, FILE *f) +{ + STRING_CONTRACT(v); + M_ASSUME (f != NULL); + char c = fgetc(f); + if (c != '"') return false; + string_clean(v); + c = fgetc(f); + while (c != '"' && !feof(f) && !ferror(f)) { + if (c == '\\') { + c = getc(f); + if (c != '"') string_push_back (v, '\\'); + } + string_push_back (v, c); + c = fgetc(f); + } + return c == '"'; +} + +#define STRING_SPLIT(name, oplist, type_oplist) \ + static inline void CAT(name, _split)(M_GET_TYPE oplist cont, \ + const string_t str, const char sep) \ + { \ + size_t begin = 0; \ + string_t tmp; \ + string_init(tmp); \ + M_GET_CLEAN oplist (cont); \ + for(size_t i = 0 ; i < string_get_length(str); i++) { \ + char c = string_get_char(str, i); \ + if (c == sep) { \ + string_set_strn(tmp, &str->ptr[begin], i - begin); \ + M_GET_PUSH oplist (cont, tmp); \ + begin = i + 1; \ + } \ + } \ + string_set_strn(tmp, &str->ptr[begin], string_get_length(str) - begin); \ + M_GET_PUSH oplist (cont, tmp); \ + /* HACK: if method reverse is defined, it is likely that we have */ \ + /* inserted the items in the wrong order (aka for a list) */ \ + M_IF_METHOD(REVERSE, oplist) (M_GET_REVERSE oplist (cont);, ) \ + string_clear(tmp); \ + } \ + +/* Use of Compound Literals to init a constant string */ +#define STRING_CTE(s) \ + ((const string_t){{.size = sizeof(s)-1, .alloc = sizeof(s), \ + .ptr = s}}) + +#define STRING_INIT_PRINTF(v, format, ...) do { \ + string_init (v); \ + string_printf (v, format, __VA_ARGS__) ; \ + } while (0) + +#define STRING_DECL_INIT(v) \ + string_t v __attribute__((cleanup(string_clear2))) = {{ 0, 0, NULL}} + +#define STRING_DECL_INIT_PRINTF(v, format, ...) \ + STRING_DECL_INIT(v); \ + string_printf (v, format, __VA_ARGS__) + +#define STRING_OPLIST \ + (INIT(string_init),INIT_SET(string_init_set), SET(string_set), \ + INIT_MOVE(string_init_move), MOVE(string_move), \ + CLEAR(string_clear), HASH(string_hash), EQUAL(string_equal_p), \ + CMP(string_cmp), TYPE(string_t), GET_STR(string_get_str), \ + OUT_STR(string_out_str), IN_STR(string_in_str), \ + EXT_ALGO(STRING_SPLIT) \ + ) + +#endif diff --git a/m-tuple.h b/m-tuple.h new file mode 100644 index 00000000..4d66b926 --- /dev/null +++ b/m-tuple.h @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __M_TUPLE_H +#define __M_TUPLE_H + +#include +#include +#include +#include + +#include "m-core.h" + +/********************************** INTERNAL ************************************/ + +#define TUPLE_GET_FIELD(f,t,o) f +#define TUPLE_GET_TYPE(f,t,o) t +#define TUPLE_GET_INIT(f,t,o) M_GET_INIT o +#define TUPLE_GET_INIT_SET(f,t,o) M_GET_INIT_SET o +#define TUPLE_GET_INIT_MOVE(f,t,o) M_GET_INIT_MOVE o +#define TUPLE_GET_MOVE(f,t,o) M_GET_MOVE o +#define TUPLE_GET_SET(f,t,o) M_GET_SET o +#define TUPLE_GET_CLEAR(f,t,o) M_GET_CLEAR o +#define TUPLE_GET_CMP(f,t,o) M_GET_CMP o +#define TUPLE_GET_HASH(f,t,o) M_GET_HASH o +#define TUPLE_GET_EQUAL(f,t,o) M_GET_EQUAL o +#define TUPLE_GET_STR(f,t,o) M_GET_GET_STR o +#define TUPLE_GET_OUT_STR(f,t,o) M_GET_OUT_STR o +#define TUPLE_GET_IN_STR(f,t,o) M_GET_IN_STR o + + +#define TUPLE_DEFINE_TYPE(name, ...) \ + typedef struct CAT(name, _s) { \ + M_MAP(TUPLE_DEFINE_RECUR_TYPE_ELE , __VA_ARGS__) \ + } CAT(name,_t)[1]; \ + typedef struct CAT(name, _s) *CAT(name, _ptr); \ + typedef const struct CAT(name, _s) *CAT(name, _srcptr); +#define TUPLE_DEFINE_RECUR_TYPE_ELE(a) \ + TUPLE_GET_TYPE a TUPLE_GET_FIELD a ; + + +#define TUPLE_DEFINE_INIT(name, ...) \ + static inline void CAT(name, _init)(CAT(name,_t) my) { \ + M_MAP(TUPLE_DEFINE_INIT_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_INIT_FUNC(a) \ + TUPLE_GET_INIT a ( my -> TUPLE_GET_FIELD a ); + + +#define TUPLE_DEFINE_INIT_SET(name, ...) \ + static inline void CAT(name, _init_set)(CAT(name,_t) my , CAT(name,_t) const org) { \ + M_MAP(TUPLE_DEFINE_INIT_SET_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_INIT_SET_FUNC(a) \ + TUPLE_GET_INIT_SET a ( my -> TUPLE_GET_FIELD a , org -> TUPLE_GET_FIELD a ); + + +#define TUPLE_DEFINE_INIT_SET2(name, ...) \ + static inline void CAT(name, _init_set2)(CAT(name,_t) my \ + M_MAP(TUPLE_DEFINE_INIT_SET2_PROTO, __VA_ARGS__) \ + ) { \ + M_MAP(TUPLE_DEFINE_INIT_SET2_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_INIT_SET2_PROTO(a) \ + , TUPLE_GET_TYPE a const TUPLE_GET_FIELD a +#define TUPLE_DEFINE_INIT_SET2_FUNC(a) \ + TUPLE_GET_INIT_SET a ( my -> TUPLE_GET_FIELD a , TUPLE_GET_FIELD a ); + + +#define TUPLE_DEFINE_SET(name, ...) \ + static inline void CAT(name, _set)(CAT(name,_t) my , \ + CAT(name,_t) const org) { \ + M_MAP(TUPLE_DEFINE_SET_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_SET_FUNC(a) \ + TUPLE_GET_SET a ( my -> TUPLE_GET_FIELD a , org -> TUPLE_GET_FIELD a ); + + +#define TUPLE_DEFINE_CLEAR(name, ...) \ + static inline void CAT(name, _clear)(CAT(name,_t) my) { \ + M_MAP(TUPLE_DEFINE_CLEAR_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_CLEAR_FUNC(a) \ + TUPLE_GET_CLEAR a ( my -> TUPLE_GET_FIELD a ); + + +#define TUPLE_DEFINE_GETTER(name, ...) \ + M_MAP2(TUPLE_DEFINE_GETTER_PROTO, name, __VA_ARGS__) +#define TUPLE_DEFINE_GETTER_PROTO(name, a) \ + static inline TUPLE_GET_TYPE a const * CAT3(name, _get_, TUPLE_GET_FIELD a) \ + (CAT(name,_t) const my) { \ + return (TUPLE_GET_TYPE a const * ) &(my->TUPLE_GET_FIELD a);} + + +#define TUPLE_DEFINE_SETTER(name, ...) \ + M_MAP2(TUPLE_DEFINE_SETTER_PROTO, name, __VA_ARGS__) +#define TUPLE_DEFINE_SETTER_PROTO(name, a) \ + static inline void CAT3(name, _set_, TUPLE_GET_FIELD a) \ + (CAT(name,_t) my, TUPLE_GET_TYPE a TUPLE_GET_FIELD a) { \ + TUPLE_GET_SET a (my ->TUPLE_GET_FIELD a, TUPLE_GET_FIELD a);} + + +#define TUPLE_DEFINE_CMP(name, ...) \ + static inline int CAT(name, _cmp)(CAT(name,_t) const e1 , \ + CAT(name,_t) const e2) { \ + int i; \ + M_MAP(TUPLE_DEFINE_CMP_FUNC , __VA_ARGS__) \ + return 0; \ + } +#define TUPLE_DEFINE_CMP_FUNC(a) \ + i = TUPLE_GET_CMP a ( e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a );\ + if (i != 0) return i; + + +#define TUPLE_DEFINE_EQUAL(name, ...) \ + static inline bool CAT(name, _equal_p)(CAT(name,_t) const e1 , \ + CAT(name,_t) const e2) { \ + bool b; \ + M_MAP(TUPLE_DEFINE_EQUAL_FUNC , __VA_ARGS__) \ + return true; \ + } +#define TUPLE_DEFINE_EQUAL_FUNC(a) \ + b = TUPLE_GET_EQUAL a ( e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a ); \ + if (!b) return false; + + +#define TUPLE_DEFINE_HASH(name, ...) \ + static inline unsigned long CAT(name, _hash)(CAT(name,_t) const e1) { \ + unsigned long hash = 0; \ + M_MAP(TUPLE_DEFINE_HASH_FUNC , __VA_ARGS__) \ + return hash; \ + } +#define TUPLE_DEFINE_HASH_FUNC(a) \ + hash = hash * 31421ul + TUPLE_GET_HASH a ( e1 -> TUPLE_GET_FIELD a); + + +#define TUPLE_DEFINE_GET_STR(name, ...) \ + static inline void CAT(name, _get_str)(string_t str, \ + CAT(name,_t) const el, \ + bool append) { \ + bool comma = false; \ + assert (str != NULL && el != NULL); \ + (append ? string_cat_str : string_set_str) (str, "("); \ + M_MAP(TUPLE_DEFINE_GET_STR_FUNC , __VA_ARGS__) \ + string_push_back (str, ')'); \ + } +#define TUPLE_DEFINE_GET_STR_FUNC(a) \ + if (comma) string_push_back (str, ','); \ + comma = true; \ + TUPLE_GET_STR a (str, el -> TUPLE_GET_FIELD a, true); \ + + +#define TUPLE_DEFINE_OUT_STR(name, ...) \ + static inline void CAT(name, _out_str)(FILE *f, \ + CAT(name,_t) const el) { \ + bool comma = false; \ + assert (f != NULL && el != NULL); \ + fputc('(', f); \ + M_MAP(TUPLE_DEFINE_OUT_STR_FUNC , __VA_ARGS__) \ + fputc (')', f); \ + } +#define TUPLE_DEFINE_OUT_STR_FUNC(a) \ + if (comma) fputc (',', f); \ + comma = true; \ + TUPLE_GET_OUT_STR a (f, el -> TUPLE_GET_FIELD a); \ + + +#define TUPLE_DEFINE_IN_STR(name, ...) \ + static inline bool CAT(name, _in_str)(CAT(name,_t) el, FILE *f) { \ + bool comma = false; \ + assert (f != NULL && el != NULL); \ + char c = fgetc(f); \ + if (c != '(') return false; \ + M_MAP(TUPLE_DEFINE_IN_STR_FUNC , __VA_ARGS__) \ + c = fgetc(f); \ + return (c == ')'); \ + } +#define TUPLE_DEFINE_IN_STR_FUNC(a) \ + if (comma) { \ + c = fgetc (f); \ + if (c != ',' || feof(f) || ferror(f)) return false; \ + } \ + comma = true; \ + if (TUPLE_GET_IN_STR a (el -> TUPLE_GET_FIELD a, f) == false) \ + return false ; \ + +#define TUPLE_DEFINE_INIT_MOVE(name, ...) \ + static inline void CAT(name, _init_move)(CAT(name,_t) el, CAT(name,_t) org) { \ + M_MAP(TUPLE_DEFINE_INIT_MOVE_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_INIT_MOVE_FUNC(a) \ + TUPLE_GET_INIT_MOVE a (el -> TUPLE_GET_FIELD a, org -> TUPLE_GET_FIELD a); + +#define TUPLE_DEFINE_MOVE(name, ...) \ + static inline void CAT(name, _move)(CAT(name,_t) el, CAT(name,_t) org) { \ + M_MAP(TUPLE_DEFINE_MOVE_FUNC , __VA_ARGS__) \ + } +#define TUPLE_DEFINE_MOVE_FUNC(a) \ + TUPLE_GET_MOVE a (el -> TUPLE_GET_FIELD a, org -> TUPLE_GET_FIELD a); + + +/* Macros for testing for method presence */ +#define TUPLE_TEST_METHOD2_P(method, f, t, op) \ + M_TEST_METHOD_P(method, op) +#define TUPLE_TEST_METHOD_P(method, trio) \ + M_APPLY(TUPLE_TEST_METHOD2_P, method, M_OPLIST_FLAT trio) + +#define TUPLE_ALL_CMP(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, CMP, __VA_ARGS__) +#define TUPLE_ALL_EQUAL(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, EQUAL, __VA_ARGS__) +#define TUPLE_ALL_HASH(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, HASH, __VA_ARGS__) +#define TUPLE_ALL_GET_STR(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, GET_STR, __VA_ARGS__) +#define TUPLE_ALL_OUT_STR(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, OUT_STR, __VA_ARGS__) +#define TUPLE_ALL_IN_STR(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, IN_STR, __VA_ARGS__) +#define TUPLE_ALL_INIT_MOVE(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, INIT_MOVE, __VA_ARGS__) +#define TUPLE_ALL_MOVE(...) \ + M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, MOVE, __VA_ARGS__) + +#define TUPLEI_OPLIST(name, ...) \ + (INIT(CAT(name,_init)), \ + INIT_SET(CAT(name, _init_set)), \ + SET(CAT(name,_set)), \ + CLEAR(CAT(name, _clear)), \ + TYPE(CAT(name,_t)), \ + M_IF_METHOD_ALL(CMP, __VA_ARGS__)(CMP(CAT(name, _cmp)),), \ + M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(CAT(name, _hash)),), \ + M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(CAT(name, _equal_p)),), \ + ) + +/********************** External interface *************************/ + +#define TUPLE_DEF2(name, ...) \ + TUPLE_DEFINE_TYPE(name, __VA_ARGS__) \ + TUPLE_DEFINE_INIT(name, __VA_ARGS__) \ + TUPLE_DEFINE_INIT_SET(name, __VA_ARGS__) \ + TUPLE_DEFINE_INIT_SET2(name, __VA_ARGS__) \ + TUPLE_DEFINE_SET(name, __VA_ARGS__) \ + TUPLE_DEFINE_CLEAR(name, __VA_ARGS__) \ + TUPLE_DEFINE_GETTER(name, __VA_ARGS__) \ + TUPLE_DEFINE_SETTER(name, __VA_ARGS__) \ + M_IF(TUPLE_ALL_CMP(__VA_ARGS__)) \ + (TUPLE_DEFINE_CMP(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_HASH(__VA_ARGS__)) \ + (TUPLE_DEFINE_HASH(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_EQUAL(__VA_ARGS__)) \ + (TUPLE_DEFINE_EQUAL(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_GET_STR(__VA_ARGS__)) \ + (TUPLE_DEFINE_GET_STR(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_OUT_STR(__VA_ARGS__)) \ + (TUPLE_DEFINE_OUT_STR(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_IN_STR(__VA_ARGS__)) \ + (TUPLE_DEFINE_IN_STR(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_INIT_MOVE(__VA_ARGS__)) \ + (TUPLE_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \ + M_IF(TUPLE_ALL_MOVE(__VA_ARGS__)) \ + (TUPLE_DEFINE_MOVE(name, __VA_ARGS__),) + +/* Define the oplist of a tuple. + TYPLE_OPLIST(name[, oplist of the first type, ...]) */ +#define TUPLE_OPLIST(...) \ + M_IF_NARGS_EQ1(__VA_ARGS__) \ + (TUPLEI_OPLIST(__VA_ARGS__, () ), \ + TUPLEI_OPLIST(__VA_ARGS__ )) + +#endif diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..7c5aa0c2 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,63 @@ +CC=gcc -std=c99 +CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function +CPPFLAGS=-I.. +RM=rm -f +VALGRIND= +EXE=./a.exe + +all: + @echo "Nothing to be done." + +check: core array list i-list string buffer shared tuple snapshot algo dict rbtree + +valgrind: + make check VALGRIND=valgrind + +core: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mcore.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +array: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-marray.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +list: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mlist.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +i-list: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-milist.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +string: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mstring.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +buffer: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mbuffer.c -lpthread -o $(EXE) && $(VALGRIND) $(EXE) + +shared: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mshared.c -lpthread -o $(EXE) && $(VALGRIND) $(EXE) + +algo: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-malgo.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +tuple: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mtuple.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +snapshot: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-msnapshot.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +dict: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mdict.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +rbtree: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mrbtree.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +bitset: + $(CC) $(CFLAGS) $(CPPFLAGS) -g $(COVERAGE_CFLAGS) test-mbitset.c -lgmp -o $(EXE) && $(VALGRIND) $(EXE) + +gene-list: + $(CC) $(CFLAGS) $(CPPFLAGS)-O3 -DNDEBUG -S tgen-mlist.c && less tgen-mlist.s + +gene-array: + $(CC) $(CFLAGS) $(CPPFLAGS)-O3 -DNDEBUG -S tgen-marray.c && less tgen-marray.s + +clean: + $(RM) $(EXE) *.s *~ *.o ./a.dat + $(RM) -r coverage *.gcda *.gcno all.info test.tar README.html diff --git a/tests/dict.txt b/tests/dict.txt new file mode 100644 index 00000000..648be27e --- /dev/null +++ b/tests/dict.txt @@ -0,0 +1,18 @@ +hello:salut +README:lisez-moi +dictionary:dictionnaire +vector:vecteur +list:liste +buffer:tableau +circular:circulaire +gnu:gnu is not unix +README:LISEZ-MOI +file:fichier +string:chaine de caractere +reference:reference +const:constante +error:erreur +char:caractere +key:cle +value:valeur +print:afficher diff --git a/tests/test-malgo.c b/tests/test-malgo.c new file mode 100644 index 00000000..abe03044 --- /dev/null +++ b/tests/test-malgo.c @@ -0,0 +1,77 @@ +#include "m-list.h" +#include "m-array.h" +#include "m-string.h" +#include "m-algo.h" + +ARRAY_DEF(int, int) + +ALGO_DEF(algo_array, ARRAY_OPLIST(int)) + +LIST_DEF(int, int) + +ALGO_DEF(algo_list, LIST_OPLIST(int)) + +LIST_DEF(string, string_t, STRING_OPLIST) + +ALGO_DEF(algo_string, LIST_OPLIST(string, STRING_OPLIST)) + +static void test_list(void) +{ + list_int_t l; + list_int_init(l); + for(int i = 0; i < 100; i++) + list_int_push_back (l, i); + assert( algo_list_contains(l, 62) == true); + assert( algo_list_contains(l, -1) == false); + + assert( algo_list_count(l, 1) == 1); + list_int_push_back (l, 17); + assert( algo_list_count(l, 17) == 2); + assert( algo_list_count(l, -1) == 0); + + list_int_clear(l); +} + +static void test_array(void) +{ + array_int_t l; + array_int_init(l); + for(int i = 0; i < 100; i++) + array_int_push_back (l, i); + assert( algo_array_contains(l, 62) == true); + assert( algo_array_contains(l, -1) == false); + + assert( algo_array_count(l, 1) == 1); + array_int_push_back (l, 17); + assert( algo_array_count(l, 17) == 2); + assert( algo_array_count(l, -1) == 0); + + array_int_clear(l); +} + +static void test_string(void) +{ + list_string_t l; + list_string_init(l); + string_t s; + string_init_set_str(s, "Hello, World, John"); + algo_string_split(l, s, ','); + list_string_pop_back (&s, l); + assert (string_equal_str_p(s, "Hello")); + list_string_pop_back (&s, l); + assert (string_equal_str_p(s, " World")); + list_string_pop_back (&s, l); + assert (string_equal_str_p(s, " John")); + assert (list_string_empty_p(l)); + string_clear(s); + + list_string_clear(l); +} + +int main(void) +{ + test_list(); + test_array(); + test_string(); + exit(0); +} diff --git a/tests/test-marray.c b/tests/test-marray.c new file mode 100644 index 00000000..a4369b42 --- /dev/null +++ b/tests/test-marray.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include "m-array.h" + +ARRAY_DEF(uint, unsigned int) +#define ARRAY_UINT_OPLIST ARRAY_OPLIST(uint) + +static void my_mpz_out_str(FILE *f, const mpz_t z) +{ + mpz_out_str (f, 10, z); +} + +static bool my_mpz_in_str(mpz_t z, FILE *f) +{ + return mpz_inp_str (z, f, 10) > 0; +} + +static bool my_mpz_equal_p(const mpz_t z1, const mpz_t z2) +{ + return mpz_cmp (z1, z2) == 0; +} + +#include "m-string.h" +static void my_mpz_str(string_t str, const mpz_t z, bool append) +{ + char *s = mpz_get_str(NULL, 10, z); + assert (s != NULL); + if (append) string_cat_str (str, s); + else string_set_str (str, s); + free(s); +} + +ARRAY_DEF(mpz, mpz_t, + (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), \ + OUT_STR(my_mpz_out_str), IN_STR(my_mpz_in_str), EQUAL(my_mpz_equal_p), \ + GET_STR(my_mpz_str) )) + +static void test_uint(void) +{ + array_uint_t v; + array_uint_init(v); + + assert (array_uint_empty_p(v) == true); + + array_uint_push_back(v, 2); + assert (array_uint_empty_p(v) == false); + unsigned int x; + array_uint_pop_back(&x, v); + assert( x == 2); + + for(unsigned int i = 0; i < 100; i++) + array_uint_push_back(v, i); + + unsigned int s = 0; + for(size_t i = 0; i < array_uint_size(v) ; i++) + s += *array_uint_cget(v, i); + assert( s == 100 * 99 /2); + + s = 0; + for M_EACH(item, v, ARRAY_UINT_OPLIST) { + s += *item; + } + assert( s == 100 * 99 /2); + + array_uint_pop_at (NULL, v, 0); + assert(array_uint_size(v) == 99); + s= 0; + for(size_t i = 0; i < array_uint_size(v) ; i++) + s += *array_uint_cget(v, i); + assert( s == 100 * 99 /2); + + unsigned int *p = array_uint_get(v, 1); + *p += 999; + s= 0; + for(size_t i = 0; i < array_uint_size(v) ; i++) + s += *array_uint_cget(v, i); + assert( s == 100 * 99 /2 + 999); + + array_uint_swap(v, 2, 3); + assert(*array_uint_cget(v, 2) == 4); + assert(*array_uint_cget(v, 3) == 3); + + array_uint_shrink_to_fit(v); + assert(array_uint_capacity(v) == array_uint_size(v)); + + const unsigned int *src = array_uint_cget(v, 4); + assert (*src == 5); + + array_uint_remove_v(v, 1, array_uint_size(v) -1); + assert(array_uint_size(v) == 2); + assert(*array_uint_cget(v, 0) == 1); + assert(*array_uint_cget(v, 1) == 99); + + for(unsigned int i = 3; i < 100; i++) + array_uint_push_back(v, i); + + array_uint_resize(v, 10); + assert(array_uint_size(v) == 10); + assert(*array_uint_cget(v, 9) == 10); + array_uint_resize(v, 1000); + assert(array_uint_size(v) == 1000); + assert(*array_uint_cget(v, 999) == 0); + + array_uint_t v2; + array_uint_init_set(v2,v); + array_uint_clear(v); + assert(array_uint_size(v2) == 1000); + assert(*array_uint_cget(v2, 999) == 0); + array_uint_push_at (v2, 0, 1742); + assert (array_uint_size(v2) == 1001); + assert(*array_uint_cget(v2, 0) == 1742); + assert(*array_uint_cget(v2, 1000) == 0); + + array_uint_insert_v (v2, 500, 504); + assert (array_uint_size(v2) == 1005); + assert(*array_uint_cget(v2, 0) == 1742); + assert(*array_uint_cget(v2, 500) == 0); + assert(array_uint_size(v2) == 1005); + assert(*array_uint_cget(v2, 1004) == 0); + + array_uint_clear(v2); +} + +static void test_mpz(void) +{ + array_mpz_t array1, array2; + mpz_t z; + + array_mpz_init (array1); + array_mpz_init (array2); + mpz_init (z); + + for(int n = 0; n < 1000; n++) { + mpz_set_ui (z, n); + array_mpz_push_back (array1, z); + } + + FILE *f = fopen ("a.dat", "wt"); + if (!f) abort(); + array_mpz_out_str(f, array1); + fclose (f); + + f = fopen ("a.dat", "rt"); + if (!f) abort(); + bool b = array_mpz_in_str (array2, f); + assert (b == true); + assert (array_mpz_equal_p (array1, array2)); + fclose(f); + + array_mpz_clean(array1); + + M_LET(str, STRING_OPLIST) { + array_mpz_get_str(str, array1, false); + assert (string_cmp_str (str, "[]") == 0); + mpz_set_ui (z, 17); + array_mpz_push_back (array1, z); + array_mpz_get_str(str, array1, false); + assert (string_cmp_str (str, "[17]") == 0); + mpz_set_ui (z, 42); + array_mpz_push_back (array1, z); + array_mpz_get_str(str, array1, true); + assert (string_cmp_str (str, "[17][17,42]") == 0); + // NOTE: Not the same order than list... + } + + mpz_clear (z); + array_mpz_clear(array2); + array_mpz_clear(array1); +} + +int main(void) +{ + test_uint(); + test_mpz(); +} diff --git a/tests/test-mbitset.c b/tests/test-mbitset.c new file mode 100644 index 00000000..fa113a02 --- /dev/null +++ b/tests/test-mbitset.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include + +#include "m-bitset.h" + +static void test1(void) +{ + bitset_t set; + + bitset_init(set); + for(int i = 0; i < 100; i ++) { + bitset_push_back(set, i%2); + assert (bitset_back(set) == (i%2)); + } + for(int i = 0; i < 100; i ++) { + assert (bitset_get(set, i) == (i%2)); + } + assert(bitset_size(set) == 100); + assert(bitset_empty_p(set) == false); + + bitset_t set2; + bitset_init_set(set2, set); + + bitset_clean(set); + assert(bitset_size(set) == 0); + assert(bitset_empty_p(set) == true); + + assert(bitset_size(set2) == 100); + assert(bitset_empty_p(set2) == false); + bitset_resize(set2, 50); + assert(bitset_size(set2) == 50); + assert(bitset_empty_p(set2) == false); + for(int i = 0; i < 50; i ++) { + assert (bitset_get(set2, i) == (i%2)); + } + + // Push from 0 + bitset_push_at(set2, 0, 1); + assert (bitset_get(set2, 0) == 1); + for(int i = 1; i < 51; i ++) { + assert (bitset_get(set2, i) == ((i-1)%2)); + } + // Push from not aligned bit + bitset_push_at(set2, 9, 1); + for(int i = 1; i < 9; i ++) { + assert (bitset_get(set2, i) == ((i-1)%2)); + } + assert (bitset_get(set2, 9) == 1); + for(int i = 10; i < 52; i ++) { + assert (bitset_get(set2, i) == ((i)%2)); + } + + bool b; + bitset_pop_back(NULL, set2); + bitset_pop_back(&b, set2); + assert(b == false); + assert(bitset_size(set2) == 50); + + for(int i = 0; i < 200; i++) + bitset_push_back(set, (i%2)); + bitset_pop_at(&b, set, 0); + assert (b == false); + assert (bitset_size(set) == 199); + bitset_pop_at(&b, set, 0); + assert (b == true); + assert (bitset_size(set) == 198); + bitset_pop_at(&b, set, 9); + assert (b == true); + assert (bitset_size(set) == 197); + bitset_pop_at(&b, set, 9); + assert (b == false); + assert (bitset_size(set) == 196); + + bitset_clean(set); + for(int i = 0; i < 200; i++) + bitset_push_back(set, (i*17547854547UL)&1) ; + bitset_it_t it; + int n = 0; + for(bitset_it(it, set); ! bitset_end_p(it); bitset_next(it)) { + bool v = (n*17547854547UL)&1; + bool c = bitset_ref(it); + assert (v == c); + n++; + } + + bitset_clear(set); + bitset_clear(set2); +} + +int main(void) +{ + test1(); + exit(0); +} diff --git a/tests/test-mbuffer.c b/tests/test-mbuffer.c new file mode 100644 index 00000000..41016308 --- /dev/null +++ b/tests/test-mbuffer.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "m-buffer.h" + +// Define a fixed queue of unsigned int +BUFFER_DEF(uint, unsigned int, 10, BUFFER_QUEUE|BUFFER_BLOCKING) + +// Define a variable stack of float +BUFFER_DEF(floats, float, 0, BUFFER_STACK|BUFFER_BLOCKING) + +// Define a fixed stack of char +BUFFER_DEF(char, char, 10, BUFFER_STACK|BUFFER_UNBLOCKING) + +// Define a fixed queue of long long +BUFFER_DEF(llong, long long, 16, BUFFER_QUEUE|BUFFER_THREAD_UNSAFE|BUFFER_UNBLOCKING) + +#include +BUFFER_DEF(mpz, mpz_t, 32, BUFFER_QUEUE, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear))) + +buffer_uint_t g_buff; + +static void *conso(void *arg) +{ + unsigned int j; + assert (arg == NULL); + for(int i = 0; i < 1000;i++) { + buffer_uint_pop(&j, g_buff); + assert (j < 1000); + } + return NULL; +} + +static void *prod(void *arg) +{ + assert (arg == NULL); + for(unsigned int i = 0; i < 1000;i++) + buffer_uint_push(g_buff, i); + return NULL; +} + +static void test_global(void) +{ + M_THREAD_T idx_p[100]; + M_THREAD_T idx_c[100]; + + buffer_uint_init(g_buff, 10); + + for(int i = 0; i < 100; i++) { + M_THREAD_CREATE (idx_p[i], conso, NULL); + M_THREAD_CREATE (idx_c[i], prod, NULL); + } + for(int i = 0; i < 100;i++) { + M_THREAD_JOIN(idx_p[i]); + M_THREAD_JOIN(idx_c[i]); + } + + buffer_uint_clear(g_buff); +} + +static void test_stack(void) +{ + buffer_floats_t buff; + buffer_floats_init(buff, 100); + for(unsigned int i = 0; i < 100; i++) { + float f = i; + buffer_floats_push(buff, f); + assert (buffer_floats_empty_p(buff) == false); + } + assert (buffer_floats_full_p(buff) == true); + for(unsigned int i = 0; i < 100; i++) { + float j; + buffer_floats_pop(&j, buff); + assert(j == 99-i); + assert (buffer_floats_full_p(buff) == false); + } + assert (buffer_floats_empty_p(buff) == true); + buffer_floats_clear(buff); +} + +static void test_stack2(void) +{ + buffer_char_t buff; + bool b; + char c; + buffer_char_init(buff, 10); + for(int i = 0; i < 10; i++) { + c = i; + b = buffer_char_push(buff, c); + assert (b == true); + assert (buffer_char_empty_p(buff) == false); + } + b = buffer_char_push(buff, c); + assert (b == false); + assert (buffer_char_full_p(buff) == true); + for(int i = 0; i < 10; i++) { + buffer_char_pop(&c, buff); + assert(c == 9-i); + assert (buffer_char_full_p(buff) == false); + } + b = buffer_char_pop(&c, buff); + assert (b == false); + assert (buffer_char_empty_p(buff) == true); + buffer_char_clear(buff); +} + +static void test_no_thread(void) +{ + buffer_llong_t buff; + bool b; + long long c; + buffer_llong_init(buff, 16); + for(int i = 0; i < 16; i++) { + c = i; + b = buffer_llong_push(buff, c); + assert (b == true); + assert (buffer_llong_empty_p(buff) == false); + } + b = buffer_llong_push(buff, c); + assert (b == false); + assert (buffer_llong_full_p(buff) == true); + for(int i = 0; i < 16; i++) { + buffer_llong_pop(&c, buff); + assert(c == i); + assert (buffer_llong_full_p(buff) == false); + } + b = buffer_llong_pop(&c, buff); + assert (b == false); + assert (buffer_llong_empty_p(buff) == true); + buffer_llong_clear(buff); +} + +/* Test intrusive shared pointer + buffer */ +#include "m-i-shared.h" +// Tiny test structure +typedef struct test_s { + char buffer[52]; + char bigbuffer[1000000]; + ISHARED_PTR_INTERFACE(itest, struct test_s); +} test_t; + +static void test_init(test_t *p) { memset(p->buffer, 0x00, 52); } +static void test_clear(test_t *p) { memset(p->buffer, 0xFF, 52); } +ISHARED_PTR_DEF(itest, test_t, + (INIT(test_init M_IPTR), CLEAR(test_clear M_IPTR), DEL(free))) +static test_t *test_new(void) +{ + test_t *p = malloc (sizeof (test_t)); + assert (p != NULL); + test_init (p); + return ishared_itest_init(p); +} +BUFFER_DEF(itest, test_t *, 16, BUFFER_PUSH_INIT_POP_MOVE, + ISHARED_PTR_OPLIST(itest)) +static buffer_itest_t comm1; +static buffer_itest_t comm2; + +static void *test_conso1(void *arg) +{ + test_t *p; + assert (arg == NULL); + for(int i = 0; i < 10;i++) { + buffer_itest_pop(&p, comm1); + for(int j = 0; j < 52; j++) + assert (p->buffer[j] == (char) ((j * j * 17) + j * 42 + 1)); + ishared_itest_release(p); + } + return NULL; +} +static void *test_conso2(void *arg) +{ + test_t *p; + assert (arg == NULL); + for(int i = 0; i < 10;i++) { + buffer_itest_pop(&p, comm2); + for(int j = 0; j < 52; j++) + assert (p->buffer[j] == (char) ((j * j * 17) + j * 42 + 1)); + ishared_itest_release(p); + } + return NULL; +} +static void *test_prod(void *arg) +{ + assert (arg == NULL); + for(unsigned int i = 0; i < 10;i++) { + test_t *p = test_new(); + for(int j = 0; j < 52; j++) + p->buffer[j] = (j * j * 17) + j * 42 + 1; + buffer_itest_push(comm1, p); + buffer_itest_push(comm2, p); + ishared_itest_release(p); + } + return NULL; +} + +static void test_global_ishared(void) +{ + M_THREAD_T idx_p[100]; + M_THREAD_T idx_c1[100]; + M_THREAD_T idx_c2[100]; + + buffer_itest_init(comm1, 16); + buffer_itest_init(comm2, 16); + + for(int i = 0; i < 100; i++) { + M_THREAD_CREATE (idx_c1[i], test_conso1, NULL); + M_THREAD_CREATE (idx_c2[i], test_conso2, NULL); + M_THREAD_CREATE (idx_p[i], test_prod, NULL); + } + for(int i = 0; i < 100;i++) { + M_THREAD_JOIN(idx_p[i]); + M_THREAD_JOIN(idx_c1[i]); + M_THREAD_JOIN(idx_c2[i]); + } + + buffer_itest_clear(comm1); + buffer_itest_clear(comm2); +} + +int main(void) +{ + unsigned int x, y; + buffer_uint_t v; + buffer_uint_init(v, 10); + + assert (buffer_uint_empty_p(v) == true); + assert (buffer_uint_full_p(v) == false); + + x = 10; + buffer_uint_push (v, x); + + assert (buffer_uint_empty_p(v) == false); + assert (buffer_uint_full_p(v) == false); + + buffer_uint_pop (&y, v); + + assert (buffer_uint_empty_p(v) == true); + assert (buffer_uint_full_p(v) == false); + assert (y == 10); + + buffer_uint_clear(v); + + test_global(); + test_stack(); + test_stack2(); + test_no_thread(); + test_global_ishared(); +} diff --git a/tests/test-mcore.c b/tests/test-mcore.c new file mode 100644 index 00000000..f38bcb9d --- /dev/null +++ b/tests/test-mcore.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include "m-core.h" + +static void test_cat(void) +{ +#define AandB true +#define AandBandC true + assert (CAT(A,andB)); + assert (CAT3(A,and,B)); + assert (CAT4(A,and,B,andC)); +} + +static void test_power2(void) +{ + assert (M_POWEROF2_P(1)); + assert (M_POWEROF2_P(2)); + assert (!M_POWEROF2_P(3)); + assert (M_POWEROF2_P(4)); + assert (!M_POWEROF2_P(5)); + assert (!M_POWEROF2_P(6)); + assert (!M_POWEROF2_P(7)); + assert (M_POWEROF2_P(8)); +} + +static void test_incdec(void) +{ + assert (M_INC(0) == 1); + assert (M_INC(1) == 2); + assert (M_INC(2) == 3); + assert (M_INC(3) == 4); + assert (M_INC(4) == 5); + assert (M_INC(5) == 6); + assert (M_INC(6) == 7); + assert (M_INC(7) == 8); + assert (M_INC(8) == 9); + assert (M_INC(9) == 10); + assert (M_INC(10) == 11); + assert (M_INC(11) == 12); + assert (M_INC(12) == 13); + assert (M_INC(13) == 14); + assert (M_INC(14) == 15); + assert (M_INC(15) == 16); + assert (M_INC(16) == 17); + assert (M_INC(17) == 18); + assert (M_INC(18) == 19); + assert (M_INC(19) == 20); + assert (M_INC(20) == 21); + assert (M_INC(21) == 22); + assert (M_INC(22) == 23); + assert (M_INC(23) == 24); + assert (M_INC(24) == 25); + assert (M_INC(25) == 26); + assert (M_INC(26) == 27); + assert (M_INC(27) == 28); + assert (M_INC(28) == 29); + + assert ((0) == M_DEC(1)); + assert ((1) == M_DEC(2)); + assert ((2) == M_DEC(3)); + assert ((3) == M_DEC(4)); + assert ((4) == M_DEC(5)); + assert ((5) == M_DEC(6)); + assert ((6) == M_DEC(7)); + assert ((7) == M_DEC(8)); + assert ((8) == M_DEC(9)); + assert ((9) == M_DEC(10)); + assert ((10) == M_DEC(11)); + assert ((11) == M_DEC(12)); + assert ((12) == M_DEC(13)); + assert ((13) == M_DEC(14)); + assert ((14) == M_DEC(15)); + assert ((15) == M_DEC(16)); + assert ((16) == M_DEC(17)); + assert ((17) == M_DEC(18)); + assert ((18) == M_DEC(19)); + assert ((19) == M_DEC(20)); + assert ((20) == M_DEC(21)); + assert ((21) == M_DEC(22)); + assert ((22) == M_DEC(23)); + assert ((23) == M_DEC(24)); + assert ((24) == M_DEC(25)); + assert ((25) == M_DEC(26)); + assert ((26) == M_DEC(27)); + assert ((27) == M_DEC(28)); + assert ((28) == M_DEC(29)); + + assert( M_ADD(2,5) == M_SUB(10,3)); + assert( M_ADD(5,15) == M_SUB(25,5)); +} + +static void test_return(void) +{ + assert (M_RET_ARG1(1, 0)); + assert (M_RET_ARG2(0, 1, 0)); + assert (M_RET_ARG3(0, 0, 1, 0)); + assert (M_RET_ARG27(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0)); +} + +static void test_invert(void) +{ + assert (M_RET_ARG1 M_INVERT(0,1)); + assert (M_RET_ARG1 M_INVERT(0,0,1)); + assert (M_RET_ARG1 M_INVERT(0,0,0,1)); + assert (M_RET_ARG1 M_INVERT(0,0,0,0,1)); +} + +static void test_min(void) +{ + assert (M_MIN(1,2) == 1); + assert (M_MIN(1,-2) == -2); + assert (M_MAX(1,2) == 2); + assert (M_MAX(1,-2) == 1); +} + +int main(void) +{ + test_cat(); + test_power2(); + test_incdec(); + test_return(); + test_invert(); + test_min(); + return 0; +} + + + diff --git a/tests/test-mdict.c b/tests/test-mdict.c new file mode 100644 index 00000000..37c5d4bf --- /dev/null +++ b/tests/test-mdict.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include "m-dict.h" +#include "m-array.h" +#include "m-string.h" + +DICT_DEF2(str, string_t, STRING_OPLIST, string_t, STRING_OPLIST) +ARRAY_DEF(string, string_t, STRING_OPLIST) + +array_string_t v; + +static void init_data(int data_size) +{ + uint32_t x = 11; + string_t s; + string_init (s); + printf("--- generating data\n"); + for (int i = 0; i < data_size; ++i) { + int j = (unsigned)(data_size * ((double)x / UINT_MAX) / 4) * 271828183u; + string_printf(s, "%x", j); + array_string_push_back (v, s); + x = 1664525L * x + 1013904223L; + } + string_clear(s); + printf("--- done!\n"); +} + +static void clear_data(void) +{ + array_string_clear(v); +} + +static void test_data(void) +{ + dict_str_t dict; + int data_size = (int) array_string_size(v); + + dict_str_init (dict); + for (int i = 0; i < data_size; ++i) { + dict_str_set_at(dict, *array_string_cget(v, i), *array_string_cget(v, (i+1) % data_size)); + } + assert(dict_str_size(dict) == 1227176); + dict_str_clear (dict); +} + +static void timing(void (*f)(void)) +{ + clock_t t = clock(); + (*f)(); + printf("timing %.3lf sec\n", (double)(clock() - t) / CLOCKS_PER_SEC); +} + +static void check_io(void) +{ + M_LET(str, STRING_OPLIST) + M_LET(dict, DICT_OPLIST(str)) { + dict_str_get_str(str, dict, false); + assert (string_cmp_str (str, "{}") == 0); + dict_str_set_at (dict, STRING_CTE("LICENCE"), STRING_CTE("BSD3")); + dict_str_get_str(str, dict, false); + assert (string_cmp_str (str, "{\"LICENCE\":\"BSD3\"}") == 0); + dict_str_set_at (dict, STRING_CTE("AUTHOR"), STRING_CTE("PP")); + dict_str_get_str(str, dict, false); + assert (string_cmp_str (str, "{\"AUTHOR\":\"PP\",\"LICENCE\":\"BSD3\"}") == 0); + FILE *f = fopen ("a.dat", "wt"); + if (!f) abort(); + dict_str_out_str(f, dict); + fclose (f); + f = fopen ("a.dat", "rt"); + if (!f) abort(); + M_LET(dict2, DICT_OPLIST(str)) { + bool b = dict_str_in_str(dict2, f); + assert (b == true); + /* assert (dict_str_equal_p (dict, dict2)); */ + } + fclose (f); + } +} + +int main(void) +{ + dict_str_t dict; + string_t key, ref; + + dict_str_init(dict); + string_init(key); + string_init(ref); + + FILE *f = fopen("dict.txt", "rt"); + if (!f) abort(); + dict_str_set_at(dict, STRING_CTE("LICENCE"), STRING_CTE("BSD3")); + while (!feof(f) && !ferror(f)) { + string_fgets(key, f, STRING_READ_PURE_LINE); + size_t idx = string_search_char(key, ':'); + string_set(ref, key); + string_right(ref, idx+1); + string_left(key, idx); + dict_str_set_at (dict, key, ref); + } + fclose(f); + assert(dict_str_size(dict) == 19); + + f = fopen("dict.txt", "rt"); + if (!f) abort(); + while (!feof(f) && !ferror(f)) { + string_fgets(key, f, STRING_READ_PURE_LINE); + size_t idx = string_search_char(key, ':'); + string_set(ref, key); + string_right(ref, idx+1); + string_left(key, idx); + string_t *ptr = dict_str_get (dict, key); + assert(ptr != NULL); + assert(string_equal_p (*ptr, ref) || string_cmp_str(ref, "lisez-moi") == 0); + } + fclose(f); + + dict_str_set_at(dict, STRING_CTE("LICENCE"), STRING_CTE("BSD3")); + assert(dict_str_size(dict) == 19); + dict_str_set_at(dict, STRING_CTE("LICENCE"), STRING_CTE("BSD3")); + assert(dict_str_size(dict) == 19); + + size_t s = 0; + bool check1 = false, check2= false; + for M_EACH(r, dict, DICT_OPLIST(str)) { + if (string_cmp_str((*r)->key, "README") == 0) + check1 = true; + if (string_cmp_str((*r)->value, "BSD3") == 0) + check2 = true; + //printf ("key=%s value=%s\n", string_get_cstr((*r)->key), string_get_cstr((*r)->value)); + s++; + } + assert(s == 19); + assert(check1); + assert(check2); + string_clear(key); + string_clear(ref); + dict_str_clear(dict); + + check_io(); + + init_data(5000000); + timing(test_data); + clear_data(); + return 0; +} + +/* + [ key1: value1, + key2: value2, + key3: value3] + + a;b;"c;e";d;e;f + + TOKEN: + * separator ; : , AND . + * ignore ' ' '\n' + * remove blanck + * handle " or ' or ( ) or [ ] + * recursive + */ diff --git a/tests/test-milist.c b/tests/test-milist.c new file mode 100644 index 00000000..aeeb7acf --- /dev/null +++ b/tests/test-milist.c @@ -0,0 +1,85 @@ +/* + * MLIB - TEST module + * + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include + +#include "m-i-list.h" + +typedef struct test_s { + int n; + ILIST_INTERFACE (tname, struct test_s); +} test_t; + +ILIST_DEF(tname, test_t) + +static void test(void) +{ + test_t x1, x2, x3; + ilist_tname_t list; + + x1.n = 1; + x2.n = 2; + x3.n = 3; + + ilist_tname_init(list); + + assert (ilist_tname_size (list) == 0); + ilist_tname_push_back (list, &x3); + assert (ilist_tname_size (list) == 1); + ilist_tname_push_front (list, &x1); + assert (ilist_tname_size (list) == 2); + ilist_tname_push_after (&x1, &x2); + assert (ilist_tname_size (list) == 3); + assert (ilist_tname_front (list)->n == 1); + assert (ilist_tname_back (list)->n == 3); + + int n = 1; + for M_EACH(item, list, ILIST_OPLIST(tname)) { + assert (n == item->n); + n++; + } + for M_EACH(item, list, ILIST_OPLIST(tname)) { + if (item->n == 2) + ilist_tname_unlink(item); + } + assert (ilist_tname_size(list) == 2); + assert (&x1 == ilist_tname_pop_front(list)); + assert (ilist_tname_size(list) == 1); + assert (&x3 == ilist_tname_pop_back(list)); + assert (ilist_tname_size(list) == 0); + assert (ilist_tname_empty_p(list)); + + for M_EACH(item, list, ILIST_OPLIST(tname)) { + (void)item; + assert(false); + } + + ilist_tname_clear(list); +} + +int main(void) +{ + test(); + exit(0); +} diff --git a/tests/test-mlist.c b/tests/test-mlist.c new file mode 100644 index 00000000..bc8be738 --- /dev/null +++ b/tests/test-mlist.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include // For testing purpose only. +#include "m-list.h" + +LIST_DEF(uint, unsigned int) +#define LIST_UINT_OPLIST LIST_OPLIST(uint) + +static void my_mpz_out_str(FILE *f, const mpz_t z) +{ + mpz_out_str (f, 10, z); +} + +static bool my_mpz_in_str(mpz_t z, FILE *f) +{ + return mpz_inp_str (z, f, 10) > 0; +} + +static bool my_mpz_equal_p(const mpz_t z1, const mpz_t z2) +{ + return mpz_cmp (z1, z2) == 0; +} + +#include "m-string.h" +static void my_mpz_str(string_t str, const mpz_t z, bool append) +{ + char *s = mpz_get_str(NULL, 10, z); + if (append) string_cat_str (str, s); + else string_set_str (str, s); + free(s); +} + +LIST_DEF(mpz, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), \ + OUT_STR(my_mpz_out_str), IN_STR(my_mpz_in_str), EQUAL(my_mpz_equal_p), \ + GET_STR(my_mpz_str))) + +static void test_uint(void) +{ + list_uint_t v; + list_uint_init(v); + + assert (list_uint_empty_p(v) == true); + + list_uint_push_back(v, 2); + assert (list_uint_empty_p(v) == false); + unsigned int x ; + list_uint_pop_back(&x, v); + assert( x == 2); + assert (list_uint_empty_p(v) == true); + assert (list_uint_size(v) == 0); + + for(unsigned int i = 0; i < 1000; i ++) { + list_uint_push_back(v, i); + } + assert (list_uint_size(v) == 1000); + assert (*list_uint_cget(v, 500) == 500); + assert (*list_uint_cget(v, 999) == 999); + + list_it_uint_t u; + unsigned int s = 0; + for(list_uint_it(u, v); !list_uint_end_p(u); list_uint_next(u)) { + s += *list_uint_cref(u); + } + assert (s == 1000*999/2); + s = 0; + for M_EACH(item, v, LIST_UINT_OPLIST) { + s += *item; + } + assert (s == 1000*999/2); + + list_uint_insert(v, u, 1996); // Insert in back since u is empty + assert (list_uint_size(v) == 1001); + list_uint_it(u, v); + list_uint_insert(v, u, 1997); // Insert in second place + assert (list_uint_size(v) == 1002); + list_uint_pop_back(&x, v); + assert (x == 1996); + list_uint_pop_back(&x, v); + assert (x == 1997); + assert (list_uint_size(v) == 1000); + + list_uint_push_back (v, x); + list_uint_pop_back (NULL, v); + + list_uint_t v2; + list_it_uint_t u2; + list_uint_init(v2); + list_uint_set(v2,v); + for(list_uint_it(u, v), list_uint_it(u2, v2); !list_uint_end_p(u); list_uint_next(u), list_uint_next(u2)) { + assert(list_uint_end_p(u2) == false); + assert(*list_uint_cref(u) == *list_uint_cref(u2)); + } + + list_uint_it(u2, v2); //v2 is the first el + assert (*list_uint_cref(u2) == 999); + list_uint_next(u2); //v2 is the second el + assert (*list_uint_cref(u2) == 998); + list_uint_remove(v2, u2);// the second el is deleted + list_uint_it(u2, v2); //v2 is the first el + assert (*list_uint_cref(u2) == 999); + list_uint_next(u2); //v2 is the second el + assert (*list_uint_cref(u2) == 997); + + list_uint_it(u2, v2); //v2 is the first el + list_uint_remove(v2, u2);// the second el is deleted + list_uint_it(u2, v2); //v2 is the first el + assert (*list_uint_cref(u2) == 997); + + list_uint_clean(v); + assert (list_uint_empty_p(v) == true); + assert (list_uint_empty_p(v2) == false); + assert (list_uint_size(v2) == 998); + + list_uint_set (v, v2); + list_uint_slice (v, v2); + assert (list_uint_empty_p(v) == false); + assert (list_uint_empty_p(v2) == true); + assert (list_uint_size(v) == 998*2); + + list_uint_reverse(v2); + assert (list_uint_empty_p(v2) == true); + list_uint_push_back(v2, 12); + list_uint_push_back(v2, 17); + list_uint_reverse(v2); + list_uint_pop_back(&x, v2); + assert (x == 12); + list_uint_pop_back(&x, v2); + assert (x == 17); + assert (list_uint_empty_p(v2) == true); + + list_uint_clear(v); + list_uint_clear(v2); +} + +static void test_mpz(void) +{ + list_mpz_t v; + mpz_t z, x, s; + + mpz_init (z); + mpz_init (x); + mpz_init (s); + + list_mpz_init(v); + assert (list_mpz_empty_p(v) == true); + + mpz_set_ui (z, 2); + list_mpz_push_back(v, z); + assert (list_mpz_empty_p(v) == false); + + list_mpz_pop_back(&x, v); + assert( mpz_cmp (x, z) == 0); + assert (list_mpz_empty_p(v) == true); + assert (list_mpz_size(v) == 0); + + for(unsigned int i = 0; i < 1000; i ++) { + mpz_set_ui (z, i); + list_mpz_push_back(v, z); + } + assert (list_mpz_size(v) == 1000); + + list_it_mpz_t u; + mpz_set_ui (s, 0); + for(list_mpz_it(u, v); !list_mpz_end_p(u); list_mpz_next(u)) { + mpz_add (s, s, *list_mpz_cref(u)); + } + assert (mpz_get_ui(s) == 1000*999/2); + + FILE *f = fopen ("a.dat", "wt"); + if (!f) abort(); + list_mpz_out_str(f, v); + fclose (f); + + list_mpz_t list2; + list_mpz_init(list2); + f = fopen ("a.dat", "rt"); + bool b = list_mpz_in_str (list2, f); + assert (b == true); + fclose(f); + assert (list_mpz_equal_p (v, list2)); + list_mpz_clear(list2); + + list_mpz_clean(v); + M_LET(str, STRING_OPLIST) { + list_mpz_get_str(str, v, false); + assert (string_cmp_str (str, "[]") == 0); + mpz_set_ui (z, 17); + list_mpz_push_back (v, z); + list_mpz_get_str(str, v, false); + assert (string_cmp_str (str, "[17]") == 0); + mpz_set_ui (z, 42); + list_mpz_push_back (v, z); + list_mpz_get_str(str, v, true); + assert (string_cmp_str (str, "[17][42,17]") == 0); + } + + list_mpz_clear(v); + + mpz_clear (z); + mpz_clear (x); + mpz_clear (s); +} + +int main(void) +{ + test_uint(); + test_mpz(); +} diff --git a/tests/test-mrbtree.c b/tests/test-mrbtree.c new file mode 100644 index 00000000..579a1c13 --- /dev/null +++ b/tests/test-mrbtree.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include // For testing purpose only. +#include "m-rbtree.h" + +RBTREE_DEF(uint, unsigned int) +#define UINT_OPLIST RBTREE_OPLIST(uint) + +RBTREE_DEF(float, float) +#define FLOAT_OP RBTREE_OPLIST(float) + +static void test_uint(void) +{ + rbtree_uint_t tree, tree2; + rbtree_uint_init(tree); + assert (rbtree_uint_empty_p(tree)); + rbtree_uint_init_set(tree2, tree); + assert (rbtree_uint_empty_p(tree2)); + rbtree_uint_clear(tree); + + const unsigned int max = 1001; + for(unsigned int num = 1; num < max; num++) { + rbtree_uint_init(tree); + for(unsigned int i = 0; i < num; i++) + rbtree_uint_push(tree, i); + assert(rbtree_uint_size(tree) == num); + assert(rbtree_uint_empty_p(tree) == false); + unsigned int count = 0; + for M_EACH(item, tree, UINT_OPLIST) { + assert (count == *item); + count ++; + } + assert (*rbtree_uint_min(tree) == 0); + assert (*rbtree_uint_max(tree) == num-1); + assert (count == num); + rbtree_uint_set(tree2, tree); + unsigned int *ptr = rbtree_uint_get(tree2, num/2); + assert (ptr != NULL); + assert (*ptr == num/2); + rbtree_uint_clear(tree); + + bool b = rbtree_uint_pop(NULL, tree2, num+1); + assert (b == false); + for(unsigned int i = 0; i < num; i+=3) { + b = rbtree_uint_pop(NULL, tree2, i); + assert(b); + ptr = rbtree_uint_get(tree2, i); + assert (ptr == NULL); + } + for(unsigned int i = 1; i < num; i+=3) { + b = rbtree_uint_pop(NULL, tree2, i); + assert(b); + ptr = rbtree_uint_get(tree2, i); + assert (ptr == NULL); + } + } + + rbtree_uint_clear(tree2); +} + +static void test_float(void) +{ + M_LET(tree, FLOAT_OP) { + for(float f = -17.42; f < 17.42; f+=0.01) + rbtree_float_push (tree, f); + bool p = false; + float g; + for M_EACH(item, tree, FLOAT_OP) { + if (p) { + assert (g < *item); + } + g = *item; + p = true; + } + } +} + +int main(void) +{ + test_uint(); + test_float(); + return 0; +} diff --git a/tests/test-mregister.c b/tests/test-mregister.c new file mode 100644 index 00000000..09cc2802 --- /dev/null +++ b/tests/test-mregister.c @@ -0,0 +1,58 @@ +#include "gmp.h" +#include "m-array.h" +#include "m-list.h" +#include "m-register.h" + +ARRAY_DEF (mpz, mpz_t, (INIT(mpz_init), CLEAR(mpz_clear), SET(mpz_set), INIT_SET(mpz_init_set))) +LIST_DEF (uint, unsigned int, M_DEFAULT_OPLIST) + +// Register new class: mpz_t +#define TYPE mpz_t +#define OPLIST (INIT(mpz_init), CLEAR(mpz_clear), SET(mpz_set), INIT_SET(mpz_init_set)) +#include M_REGISTER_TYPE + +void mpq_init_set(mpq_t q, const mpq_t p) +{ + mpq_init(q); + mpq_set(q, p); +} + +// Register new class: mpq_t +#define TYPE mpq_t +#define OPLIST (INIT(mpq_init), CLEAR(mpq_clear), SET(mpq_set), INIT_SET(mpq_init_set)) +#include M_REGISTER_TYPE + +#define TYPE array_mpz_t +#define OPLIST ARRAY_OPLIST(mpz) +//#include M_REGISTER_TYPE + +//#define TYPE list_uint_t +//#define OPLIST LIST_OPLIST(unsigned int) +//#include M_REGISTER_TYPE + +bool is42(array_mpz_t array) +{ + bool retval = false; + mpz_t *z = new(mpz_t); + mpz_set_ui (*z, 42); + for_each (item, array) { + if (equal(*item, *z)) { + retval = true; + break; + } + } + delete(z); + return retval; +} + +void example(void) +{ + list_uint_t list; + init(list); + push(list, 42); + push(list, 17); + for_each (item, list) { + print(*item, "\n"); + } + clear(list); +} diff --git a/tests/test-mshared.c b/tests/test-mshared.c new file mode 100644 index 00000000..16397655 --- /dev/null +++ b/tests/test-mshared.c @@ -0,0 +1,59 @@ +#include + +#include "m-shared.h" + +SHARED_PTR_DEF(int, int) + +#include + +SHARED_PTR_DEF(mpz, mpz_t, (CLEAR(mpz_clear))) + +static int f(const shared_int_t p) +{ + return *shared_int_cref(p); +} + +int main(void) +{ + shared_int_t p1, p2; + + shared_int_init2(p1, malloc(sizeof(int))); + assert(shared_int_ref(p1) != NULL); + + *shared_int_ref(p1) = 12; + + shared_int_init_set(p2, p1); + assert(f(p2) == 12); + + shared_int_release(p1); + assert(shared_int_NULL_p(p1)); + assert(f(p2) == 12); + + shared_int_set(p1, p2); + + shared_int_release(p2); + assert(shared_int_NULL_p(p2)); + + assert(f(p1) == 12); + shared_int_set(p2, p1); + assert(f(p2) == 12); + + shared_int_release(p2); + shared_int_release(p1); + + assert(shared_int_NULL_p(p1)); + assert(shared_int_NULL_p(p2)); + + shared_int_clear(p2); + shared_int_clear(p1); + + shared_int_init2(p1, NULL); + assert(shared_int_NULL_p(p1)); + shared_int_clear(p1); + + shared_int_init(p1); + assert(shared_int_NULL_p(p1)); + shared_int_clear(p1); + + return 0; +} diff --git a/tests/test-msnapshot.c b/tests/test-msnapshot.c new file mode 100644 index 00000000..e2eb587e --- /dev/null +++ b/tests/test-msnapshot.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include // For testing purpose only. +#include "m-snapshot.h" + +SNAPSHOT_DEF(uint, unsigned int) +SNAPSHOT_DEF(mpz, mpz_t, + (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear))) + +static void test_uint(void) +{ + unsigned int *p_w; + const unsigned int *p_r; + snapshot_uint_t t; + snapshot_uint_init(t); + + p_r = snapshot_uint_look(t); + assert (p_r == snapshot_uint_get_consummed(t)); + assert(*p_r == 0); + assert(snapshot_uint_look(t) == p_r); + + p_w = snapshot_uint_take(t); + assert (p_w != p_r); + *p_w = 1; + assert (*p_r == 0); + assert (snapshot_uint_look(t) != p_r); + assert (*snapshot_uint_look(t) == 0); + p_r = snapshot_uint_look(t); + + p_w = snapshot_uint_take(t); + *p_w = 2; + assert (*snapshot_uint_look(t) == 1); + + p_w = snapshot_uint_take(t); + assert (p_w == snapshot_uint_get_produced(t)); + *p_w = 3; + p_w = snapshot_uint_take(t); + *p_w = 4; + assert (*snapshot_uint_look(t) == 3); + + snapshot_uint_t t2; + snapshot_uint_init_set (t2, t); + snapshot_uint_clear(t); + assert (*snapshot_uint_look(t) == 3); + snapshot_uint_init(t); + snapshot_uint_set (t, t2); + assert (*snapshot_uint_look(t) == 3); +} + +int main(void) +{ + test_uint(); +} + diff --git a/tests/test-mstring.c b/tests/test-mstring.c new file mode 100644 index 00000000..ecb83f15 --- /dev/null +++ b/tests/test-mstring.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "m-string.h" + +int main(void) +{ + string_t s1; + STRING_DECL_INIT (s2); + size_t i, j; + bool b; + + string_init (s1); + + string_set_str (s1, "Hello, world!"); + assert (string_get_length(s1) == 13); + assert (string_get_char(s1, 1) == 'e'); + + string_clean(s1); + assert (string_get_length(s1) == 0); + assert (string_empty_p(s1) == true); + assert (strcmp(string_get_cstr(s1), "") == 0); + + string_set_strn(s1, "Hello, world!", 14); + assert (strcmp(string_get_cstr(s1), "Hello, world!") == 0); + string_clean(s1); + string_set_strn(s1, "Hello, world!", 5); + assert (strcmp(string_get_cstr(s1), "Hello") == 0); + string_clean(s1); + + string_set_str (s1, "Hello beautiful world!"); + assert (string_get_length(s1) == 22); + assert (strcmp(string_get_cstr(s1), "Hello beautiful world!") == 0); + + string_clean(s1); + string_set_str(s1, "Hello"); + string_cat_str(s1, ", world!"); + assert (string_get_length(s1) == 13); + assert (string_cmp_str(s1, "Hello, world!") == 0); + string_set_str(s2, "Hello, world!"); + assert (string_cmp(s1, s2) == 0); + + string_set_str(s2, "hELLO, world!"); + assert (string_cmpi(s1, s2) == 0); + + i = string_search_char (s1, ','); + assert (i == 5); + i = string_search_char (s1, 'Z'); + assert (i == STRING_FAILURE); + + i = string_search_rchar (s1, 'o'); + assert (i == 8); + i = string_search_rchar (s1, 'Z'); + assert (i == STRING_FAILURE); + + i = string_search_str (s1, "world"); + assert (i == 7); + i = string_search_str (s1, "World"); + assert (i == STRING_FAILURE); + + string_set (s2, s1); + + j = string_search_char (s1, ','); + string_left (s1, j); + assert (string_cmp_str (s1, "Hello") == 0); + + string_set (s1, s2); + j = string_search_char (s1, ','); + string_right (s1, j); + assert (string_cmp_str (s1, ", world!") == 0); + + string_set (s1, s2); + j = string_search_char (s1, ','); + string_mid (s1, j, 2); + assert (string_cmp_str (s1, ", ") == 0); + + string_set (s1, s2); + b = string_replace_str (s1, "world", "Paul"); + assert (b == true); + assert (string_cmp_str (s1, "Hello, Paul!") == 0); + + b = string_replace_str (s1, "world", "Paul"); + assert (b == false); + assert (string_cmp_str (s1, "Hello, Paul!") == 0); + + b = string_replace_str (s1, "Paul", "Juliette"); + assert (b == true); + assert (string_cmp_str (s1, "Hello, Juliette!") == 0); + + string_printf (s1, "There is %d Paul!", 2); + assert (string_cmp_str (s1, "There is 2 Paul!") == 0); + assert (string_get_length (s1) == 16); + + string_set_str(s1, "Hello, world()\n"); + i = string_cspn(s1, ",()"); + assert (i == 5); + string_set_str(s1, "Hello (world)\n"); + i = string_cspn(s1, ",()"); + assert (i == 6); + string_set_str(s1, "Hello (world)\n"); + i = string_spn(s1, "Hel"); + assert (i == 4); + + string_clear (s1); + string_init (s1); + + assert (string_cmp_str (s1, "") == 0); + assert (string_cmpi (s1, s1) == 0); + + string_set_str(s2, "Hello"); + string_cat (s2, s1); + assert (string_cmp_str (s2, "Hello") == 0); + + string_set_str(s2, "Hello"); + string_cat (s1, s2); + assert (string_cmp_str (s1, "Hello") == 0); + + string_clear (s1); + string_init (s1); + + string_left(s1, 100); + assert (string_empty_p (s1)); + assert (string_get_length (s1) == 0); + assert (string_cmp_str (s1, "") == 0); + + string_right(s1, 100); + assert (string_empty_p (s1)); + assert (string_get_length (s1) == 0); + assert (string_cmp_str (s1, "") == 0); + + string_set_str(s2, "Hello"); + string_set (s2, s1); + assert (string_empty_p (s2)); + assert (string_get_length (s2) == 0); + assert (string_cmp_str (s2, "") == 0); + + string_set_str(s1, "Hello, world!"); + assert(string_start_with_str_p(s1, "Hello") == true); + assert(string_start_with_str_p(s1, "Help") == false); + string_set_str(s2, "H"); + assert(string_start_with_string_p(s1, s2) == true); + + string_cat_printf(s1, " %d little %s.", 10, "suns"); + string_set_str(s2, "Hello, world! 10 little suns."); + assert(string_equal_p(s1, s2) == true); + + string_set_str(s1, " \r\n\t HELLO \n\r\t"); + string_strim(s1); + assert (string_cmp_str (s1, "HELLO") == 0); + + string_set_str(s1, " \r\n\t \n\r\t"); + string_strim(s1); + assert (string_cmp_str (s1, "") == 0); + + string_set_str(s1, "Hello world"); + string_get_str(s2, s1, false); + assert (string_cmp_str (s2, "\"Hello world\"") == 0); + string_set_str(s1, "Hello \"world\""); + string_get_str(s2, s1, true); + assert (string_cmp_str (s2, "\"Hello world\"\"Hello \\\"world\\\"\"") == 0); + + string_set_str(s1, "Hello \"world\""); + FILE *f = fopen ("a.dat", "wt"); + assert (f != NULL); + string_out_str (f, s1); + fclose (f); + f = fopen("a.dat", "rt"); + assert (f != NULL); + b = string_in_str(s2, f); + assert(b); + fclose(f); + assert (string_equal_p(s1, s2)); + + string_clear (s1); +} diff --git a/tests/test-mtuple.c b/tests/test-mtuple.c new file mode 100644 index 00000000..6aa8edab --- /dev/null +++ b/tests/test-mtuple.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "m-string.h" +#include "m-tuple.h" + +TUPLE_DEF2(pair, + (key, string_t, (INIT(string_init), INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear))), + (value, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear) ))) + +// No cmp defined +TUPLE_DEF2(triple, + (key, string_t, (INIT(string_init), INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear), CMP(string_cmp))), + (value, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear) )), + (data, const char *, ())) + +// cmp shall be defined +TUPLE_DEF2(pair3, + (key, string_t, (INIT(string_init), INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear), CMP(string_cmp), HASH(string_hash), EQUAL(string_equal_p))), + (value, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), CMP(mpz_cmp) ))) + +TUPLE_DEF2(pair_str, + (vala, string_t, STRING_OPLIST), + (valb, string_t, STRING_OPLIST)) + +static void check_io(void) +{ + M_LET(s, STRING_OPLIST) + M_LET(pair, TUPLE_OPLIST(pair_str, STRING_OPLIST, STRING_OPLIST)) + M_LET(pair2, TUPLE_OPLIST(pair_str, STRING_OPLIST, STRING_OPLIST)) { + string_set_str(pair->vala, "Hello"); + string_set_str(pair->valb, "World"); + pair_str_get_str(s, pair, false); + assert(string_cmp_str(s, "(\"Hello\",\"World\")") == 0); + + FILE *f = fopen("a.dat", "wt"); + if (!f) abort(); + pair_str_out_str(f, pair); + fclose(f); + + f = fopen("a.dat", "rt"); + if (!f) abort(); + bool b = pair_str_in_str(pair2, f); + assert (b); + fclose(f); + assert (pair_str_equal_p(pair, pair2) == true); + } +} + +int main(void) +{ + pair_t p1, p2; + + pair_init (p1); + string_set_str(p1->key, "HELLO"); + mpz_set_str(p1->value, "1742", 10); + pair_init_set (p2, p1); + assert(mpz_cmp_ui(p2->value, 1742) == 0); + assert(string_equal_str_p(p2->key, "HELLO")); + string_set_str(p2->key, "HELLO WORLD"); + mpz_set_str(p2->value, "174217", 10); + pair_set(p1, p2); + assert(mpz_cmp_ui(p1->value, 174217) == 0); + assert(string_equal_str_p(p1->key, "HELLO WORLD")); + pair_clear(p1); + pair_clear(p2); + + string_t s; + mpz_t z; + pair3_t p3, p4; + string_init_set_str(s, "HELLO"); + mpz_init_set_ui (z, 1442); + pair3_init_set2(p3, s, z); + pair3_init_set2(p4, s, z); + int i = pair3_cmp (p3, p4); + assert (i == 0); + mpz_set_ui (z, 1443); + pair3_set_value(p4, z); + i = pair3_cmp (p3, p4); + assert (i < 0); + mpz_set_ui (z, 1442); + string_set_str(s, "HELLN"); + pair3_set_key(p4, s); + i = pair3_cmp (p3, p4); + assert (i > 0); + pair3_clear(p3); + pair3_clear(p4); + string_clear(s); + mpz_clear(z); + + check_io(); + return 0; +} diff --git a/tests/tgen-mlist.c b/tests/tgen-mlist.c new file mode 100644 index 00000000..10d719c9 --- /dev/null +++ b/tests/tgen-mlist.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "m-list.h" + +LIST_DEF(dbl, double) + +void f_push_back(list_dbl_t list) +{ + list_dbl_push_back(list, 2.0); +} + +double f_iterator(list_dbl_t list) +{ + list_it_dbl_t it; + double s = 0.0; + for(list_dbl_it(it, list); !list_dbl_end_p(it); list_dbl_next(it)) + s += *list_dbl_cref(it); + return s; +} + +struct tata { + int x; +}; +struct tutu { + void (* const init) (void); + void (* const add) (int *t); + struct tata *data; +}; + +int f(int p) +{ + struct tata el_data; + inline void __attribute__((always_inline)) el_init (void) { el_data.x = 0; }; + inline void __attribute__((always_inline)) el_add (int *t) { el_data.x += *t; }; + const struct tutu el = { .init = el_init, .add = el_add, .data = &el_data }; + + el.init(); + el.add (&p); + return 0; +} + diff --git a/tests/tgen-mvector.c b/tests/tgen-mvector.c new file mode 100644 index 00000000..df8f2932 --- /dev/null +++ b/tests/tgen-mvector.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "m-array.h" + +ARRAY_DEF(unsigned int, uint) + +unsigned int add_two(const array_uint_t tab, size_t i, size_t j) +{ + return *array_uint_cref(tab, i) + *array_uint_cref(tab, j); +} + +void size_to_exactly(array_uint_t tab, size_t n) +{ + array_uint_resize(tab, 0); + array_uint_resize(tab, n); +} diff --git a/tests/tgen-openmp.c b/tests/tgen-openmp.c new file mode 100644 index 00000000..fa8cfeb9 --- /dev/null +++ b/tests/tgen-openmp.c @@ -0,0 +1,18 @@ +#include + +#include "m-array.h" +ARRAY_DEF(double, double) + +int main(void) { + const int n = 250000000; + const double x = 0.001; + M_LET(v, ARRAY_OPLIST(double)) { + array_double_resize(v, n); +#pragma omp parallel for + for (int i = 0; i < array_double_size(v); ++i) { + array_double_set_at(v, i, cos(i * x)); + } + } + + return 0; +}