M\*LIB: Generic type-safe Container Library for C language ========================================================== Overview -------- M\*LIB (M star lib) is a C library enabling to use **generic and type safe container** in pure C language, aka handling generic [containers](https://en.wikipedia.org/wiki/Container_%28abstract_data_type%29). The objects within the containers can still have proper constructor, destructor (and other methods): this is handled by the library. This makes it possible to construct fully recursive objects (container-of[...]-container-of-type-T), without erasing type information (typically using void pointers or resorting to C macro to access the container). This is an equivalent of the [C++](https://en.wikipedia.org/wiki/C%2B%2B) [Standard Library](https://en.wikipedia.org/wiki/C%2B%2B_Standard_Library) but for standard ISO C99 / C11. There is not a strict mapping as both the STL and M\*LIB have their exclusive containers: See [here](https://github.com/P-p-H-d/mlib/wiki/STL-to-M*LIB-mapping) for details. M\*LIB is portable to any systems that support [ISO C99](https://en.wikipedia.org/wiki/C99). Some optional features need at least [ISO C11](https://en.wikipedia.org/wiki/C11_(C_standard_revision)). M\*LIB is **only** composed of a set of headers. There is no C file: you just have to put the header in the search path of your compiler, and it will work. There is no dependency (except some other headers of M\*LIB and the LIBC). One of M\*LIB's design key is to ensure safety. This is done by multiple means: * in debug mode, defensive programming is extensively used: the contracts of the function are checked, ensuring that the data are not corrupted. For example, [Buffer overflow](https://en.wikipedia.org/wiki/Buffer_overflow) are checked in this mode through [bound checking](https://en.wikipedia.org/wiki/Bounds_checking) or the intrinsic properties of a Red-Black tree (for example) are verified. * as few cast as possible are used within the library (as cast are the evil of safety). Still the library can be used with the greatest level of warnings by a C compiler without any aliasing warning. * the genericity is not done directly by macro, but indirectly by making them define inline functions with the proper prototypes: this enables the user calls to have proper warning checks. Other key designs are: * do not rewrite the C library and just wrap around it (for example don't rewrite sort but stable sort), * do not make users pay the cost of what they don't need. Due to the [weak](https://en.wikipedia.org/wiki/Strong_and_weak_typing#Pointers) nature of the C language for pointers, [type safe](https://en.wikipedia.org/wiki/Type_safety) means that at least a warning is generated by the compiler in case of wrong type passed as container arguments to the functions. M\*LIB is still be quite-efficient: even if the implementation may not always be state of the art, there is no overhead in using this library rather than using direct C low-level access: the compiler is able to **fully** optimize the library calls since all the functions are declared as inline. In [fact](https://github.com/P-p-H-d/mlib/wiki/performance), M\*LIB is one of the fastest generic C library you can find. M\*LIB uses internally the 'malloc', 'realloc' and 'free' functions to handle the memory pool. This behavior can be overridden at different level. M\*LIB default policy is to abort the program if there is a memory error. However, this behavior can also be customized globally. M\*LIB may use a lot of assertions in its implementation to ensure safety: it is highly recommended to properly define NDEBUG for released programs. M\*LIB is distributed under BSD-2 simplified license. It is strongly advised not to read the source to know how to use the library but rather read the examples or the tests. In this documentation, 'shall' will be used to indicate a user constraint that is mandatory to follow under penalty of undefined behavior. 'should' will be used to indicate a recommendation to the user. All pointers expect non-null argument except if indicated. Components ---------- The available containers that doesn't require the user structure to be modified are: * [m-array.h](#m-array): header for creating array of generic type and of variable size, * [m-list.h](#m-list): header for creating singly-linked list of generic type, * [m-deque.h](#m-deque): header for creating double-ended queue of generic type and of variable size, * [m-dict.h](#m-dict): header for creating generic dictionary or set of generic type (and of variable kind), * [m-rbtree.h](#m-rbtree): header for creating binary sorted tree of generic type, * [m-bptree.h](#m-bptree): header for creating B+TREE of generic type, * [m-tuple.h](#m-tuple): header for creating arbitrary tuple of generic type, * [m-variant.h](#m-variant): header for creating arbitrary variant of generic type, * [m-prioqueue.h](#m-prioqueue): header for creating priority queue of generic type and of variable size, The available containers of M\*LIB for thread synchronization are: * [m-buffer.h](#m-buffer): header for creating fixed-size queue (or stack) of generic type (multiple producer / multiple consumer), * [m-snapshot](#m-snapshot): header for creating 'snapshot' buffer for sharing synchronously big data (thread safe). * [m-shared.h](#m-shared): header for creating shared pointer of generic type. * [m-concurrent.h](#m-concurrent): header for transforming a container into a concurrent container. * [m-c-mempool.h]: WIP header for creating fast concurrent memory allocation. The following containers are intrusive (You need to modify your structure to add fields needed by the container): * [m-i-list.h](#m-i-list): header for creating doubly-linked intrusive list of generic type, * [m-i-shared.h](#m-i-shared): header for creating intrusive shared pointer of generic type (Thread Safe), Other headers offering other functionality are: * [m-string.h](#m-string): header for creating dynamic variable-length string, * [m-bitset.h](#m-bitset): header for creating bit set (or "packed array of bool"), * [m-algo.h](#m-algo): header for providing various generic algorithms to the previous containers. * [m-funcobj.h](#m-funcobj): header for creating function object (used by algorithm generation). * [m-mempool.h](#m-mempool): header for creating specialized & fast memory allocator. * [m-worker.h](#m-worker): header for providing an easy pool of workers on separated threads to handle work orders, used for parallelism tasks. * [m-serial-json.h](#m-serial-json): header for importing / exporting the containers in [JSON format](https://en.wikipedia.org/wiki/JSON). * [m-serial-bin.h](#m-serial-bin): header for importing / exporting the containers in an adhoc fast binary format. * [m-genint.h]: internal header for generating unique integers in a concurrent context. * [m-core.h](#m-core): header for meta-programming with the C preprocessor (used by all other headers). Finally headers for compatibility with non C11 compilers: * [m-atomic.h](#m-atomic): header for ensuring compatibility between C's stdatomic.h and C++'s atomic header. Provide also an implementation over mutex if none is available. * [m-mutex.h](#m-mutex): header for providing a very thin layer across multiple implementation of mutex/threads (C11/PTHREAD/WIN32). Each containers define their iterators. All containers try to expose the same interface: if the method name is the same, then it does the same thing and is used in the same way. In some rare case, the method has to be adapted to the container. Each header can be used separately from others: dependency between headers have been kept to the minimum. ![Dependence between headers](https://raw.githubusercontent.com/P-p-H-d/mlib/master/doc/depend.png) Build & Installation -------------------- M\*LIB is **only** composed of a set of headers, as such there is no build for the library. The library doesn't depend on any other library than the LIBC. To run the test suite, run: make check To generate the documentation, run: make doc To install the headers, run: make install PREFIX=/my/directory/where/to/install Other targets exist. Mainly for development purpose. How to use ---------- To use these data structures, you include the desired header, instantiate the definition of the structure and its associated methods by using a macro \_DEF for the needed type. Then you use the defined functions. Let's see a first simple example that creates a list of unsigned int: ```C #include #include "m-list.h" LIST_DEF(list_uint, unsigned int) /* Define struct list_uint_t and its methods */ int main(void) { list_uint_t list ; /* list_uint_t has been define above */ list_uint_init(list); /* All type needs to be initialized */ list_uint_push_back(list, 42); /* Push 42 in the list */ list_uint_push_back(list, 17); /* Push 17 in the list */ list_uint_it_t it; /* Define an iterator to scan each one */ for(list_uint_it(it, list) /* Start iterator on first element */ ; !list_uint_end_p(it) /* Until the end is not reached */ ; list_uint_next(it)) { /* Set the iterator to the next element*/ printf("%d\n", /* Get a reference to the underlying */ *list_uint_cref(it)); /* data and print it */ } list_uint_clear(list); /* Clear all the list */ } ``` [ Do not forget to add -std=c99 (or c11) to your compile command to request a C99 compatible build ] This looks like a typical C program except the line with 'LIST\_DEF' that doesn't have any semi-colon at the end. And in fact, except this line, everything is typical C program and even macro free! The only macro is in fact LIST\_DEF: this macro expands to the good type for the list of the defined type and to all the necessary functions needed to handle such type. It is heavily context dependent and can generate different code depending on it. You can use it as many times as needed to defined as many lists as you want. The first argument of the macro is the name to use, e.g. the prefix that is added to all generated functions and types. The second argument of the macro is the type to embed within the container. It can be any C type. The third argument of the macro is optional and is the oplist to use. See below for more information. You could replace LIST\_DEF by ARRAY\_DEF to change the kind of container (an array instead of a linked list) without changing the code below: the generated interface of a list or of an array is very similar. Yet the performance remains the same as hand-written code for both the list variant and the array variant. This is equivalent to this C++ program using the STL: ```C #include #include typedef std::list list_uint_t; typedef std::list::iterator list_uint_it_t; int main(void) { list_uint_t list ; /* list_uint_t has been define above */ list.push_back(42); /* Push 42 in the list */ list.push_back(17); /* Push 17 in the list */ for(list_uint_it_t it = list.begin() /* Iterator is first element*/ ; it != list.end() /* Until the end is not reached */ ; ++it) { /* Set the iterator to the next element*/ std::cout << *it << '\n'; /* Print the underlying data */ } } ``` As you can see, this is rather equivalent with the following remarks: * M\*LIB requires an explicit definition of the instance of the list, * M\*LIB code is more verbose in the method name, * M\*LIB needs explicit construction and destruction (as plain old C requests), * M\*LIB doesn't return a value to the underlying data but a pointer to this value: - this was done for performance (it avoids copying all the data within the stack) - and for generality reasons (some structure may not allow copying data). Note: list\_uint\_t is internally defined as an array of structure of size 1. This has the following advantages: * you effectively reserve the data whenever you declare a variable, * you pass automatically the variable per reference for function calls, * you can not copy the variable by an affectation (you have to use the API instead). You can also condense the M\*LIB code by using the M\_EACH & M\_LET macros if you are not afraid of using syntactic macros: ```C #include #include "m-list.h" LIST_DEF(list_uint, unsigned int) /* Define struct list_uint_t and its methods */ int main(void) { M_LET(list, LIST_OPLIST(uint)) { /* Define & init list as list_uint_t */ list_uint_push_back(list, 42); /* Push 42 in the list */ list_uint_push_back(list, 17); /* Push 17 in the list */ for M_EACH(item, list, LIST_OPLIST(uint)) { printf("%d\n", *item); /* Print the item */ } } /* Clear of list will be done now */ } ``` Here is another example with a complete type (with proper initialization & clear function) by using the [GMP](https://gmplib.org/) library: ```C #include #include #include "m-array.h" ARRAY_DEF(array_mpz, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear)) ) int main(void) { array_mpz_t array ; /* array_mpz_t has been define above */ array_mpz_init(array); /* All type needs to be initialized */ mpz_t z; /* Define a mpz_t type */ mpz_init(z); /* Initialize the z variable */ mpz_set_ui (z, 42); array_mpz_push_back(array, z); /* Push 42 in the array */ mpz_set_ui (z, 17); array_mpz_push_back(array, z); /* Push 17 in the array */ array_it_mpz_t it; /* Define an iterator to scan each one */ for(array_mpz_it(it, array) /* Start iterator on first element */ ; !array_mpz_end_p(it) /* Until the end is not reached */ ; array_mpz_next(it)) { /* Set the iterator to the next element*/ gmp_printf("%Zd\n", /* Get a reference to the underlying */ *array_mpz_cref(it)); /* data and print it */ } mpz_clear(z); /* Clear the z variable */ array_mpz_clear(array); /* Clear all the array */ } ``` As the mpz\_t type needs proper initialization, copy and destroy functions we need to tell to the container how to handle such a type. This is done by giving it the oplist associated to the type. An oplist is an associative array where the operators are associated to methods. In the example, we tell to the container to use the mpz\_init function for the INIT operator of the type, the mpz\_clear function for the CLEAR operator of the type, the mpz\_set function for the SET operator of the type, the mpz\_init\_set function for the INIT\_SET operator of the type. See oplist chapter for more information. We can also write the same example shorter: ```C #include #include #include "m-array.h" // Register the oplist of a mpz_t. It is a classic oplist. #define M_OPL_mpz_t() M_CLASSIC_OPLIST(mpz) // Define an instance of an array of mpz_t (both type and function) ARRAY_DEF(array_mpz, mpz_t) // Register the oplist of the created instance of array of mpz_t #define M_OPL_array_mpz_t() ARRAY_OPLIST(array_mpz, M_OPL_mpz_t()) int main(void) { // Let's define 'array' as an 'array_mpz_t' & initialize it. M_LET(array, array_mpz_t) // Let's define 'z1' and 'z2' to be 'mpz_t' & initialize it M_LET (z1, z2, mpz_t) { mpz_set_ui (z1, 42); array_mpz_push_back(array, z1); /* Push 42 in the array */ mpz_set_ui (z2, 17); array_mpz_push_back(array, z2); /* Push 17 in the array */ // Let's iterate over all items of the container for M_EACH(item, array, array_mpz_t) { gmp_printf("%Zd\n", *item); } } // All variables are cleared with the proper method beyond this point. return 0; } ``` Or even shorter when you're comfortable enough with the library: ```C #include #include #include "m-array.h" // Register the oplist of a mpz_t. It is a classic oplist. #define M_OPL_mpz_t() M_OPEXTEND(M_CLASSIC_OPLIST(mpz), INIT_WITH(mpz_init_set_ui) ) // Define an instance of an array of mpz_t (both type and function) ARRAY_DEF(array_mpz, mpz_t) // Register the oplist of the created instance of array of mpz_t #define M_OPL_array_mpz_t() ARRAY_OPLIST(array_mpz, M_OPL_mpz_t()) int main(void) { // Let's define & init 'z1=42' and 'z2=17' to be 'mpz_t' M_LET ((z1,42), (z2,17), mpz_t) // Let's define 'array' as an 'array_mpz_t' with 'z1' and 'z2' M_LET((array,z1,z2), array_mpz_t) { // Let's iterate over all items of the container for M_EACH(item, array, array_mpz_t) { gmp_printf("%Zd\n", *item); } } // All variables are cleared with the proper method beyond this point. return 0; } ``` There are two ways a container can known what is the oplist of a type: * either the oplist is passed explicitly for each definition of container and for the LET & EACH macros, * or the oplist is registered globally by defining a new macro starting with the prefix M\_OPL\_ and finishing with the name of type (don't forget the parenthesis). The macros performing the definition of container and the LET & EACH will test if such macro is defined. If it is defined, it will be used. Otherwise default methods are used. Here we can see that we register the mpz\_t type into the container with the minimum information of its interface needed. We can also see in this example so the container ARRAY provides also a macro to define the oplist of the array itself. This is true for all containers and this enables to define proper recursive container like in this example which reads from a text file a definition of sections: ```C #include #include "m-array.h" #include "m-tuple.h" #include "m-dict.h" #include "m-string.h" TUPLE_DEF2(symbol, (offset, long), (value, long)) #define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) ARRAY_DEF(array_symbol, symbol_t) #define M_OPL_array_symbol_t() ARRAY_OPLIST(array_symbol, M_OPL_symbol_t()) DICT_DEF2(sections, string_t, array_symbol_t) #define M_OPL_sections_t() DICT_OPLIST(sections, STRING_OPLIST, M_OPL_array_symbol_t()) int main(int argc, const char *argv[]) { if (argc < 2) abort(); FILE *f = fopen(argv[1], "rt"); if (!f) abort(); M_LET(sc, sections_t) { sections_in_str(sc, f); array_symbol_t *a = sections_get(sc, STRING_CTE(".text")); if (a == NULL) { printf("There is no .text section."); } else { printf("Section .text is :"); array_symbol_out_str(stdout, *a); printf("\n"); } } return 0; } ``` This example reads the data from a file and outputs the .text section if it finds it on the terminal. Other examples are available in the example folder. Internal fields of the structure are subject to change even for small revision of the library. The final goal of the library is to be able to write code like this in pure C while keeping type safety and compile time name resolution: ```C let(list, list_uint_t) { push(list, 42); push(list, 17); for each (item, list) { print(item, "\n"); } } ``` OPLIST ------ An OPLIST is a fundamental notion of M\*LIB that hasn't be used in any other library. In order to know how to operate on a type, M\*LIB needs additional information as the compiler doesn't know how to handle properly any type (contrary to C++). This is done by giving an operator list (or oplist in short) to any macro that needs to handle the type. As such, an oplist as only meaning within a macro expansion. Fundamentally, this is the exposed interface of a type with documented operators using an associative array implemented with the only C preprocessor where the operators are the predefined keys and the methods are the values. An oplist is an associative array of operator over methods in the following format: (OPERATOR1(method1), OPERATOR2(method2), ...) The function 'method1' is used to handle 'OPERATOR1'. The function 'method2' is used to handle 'OPERATOR2'. etc. The order of the operator in this list is the priority order: in case the same operator appear multiple times in the list, the first one is the priority. The method of an operator in an oplist is a preprocessor expression that shall not contain a comma. It is used to perform the association between the operation on a type and the function that performs this operation. Without an oplist, M\*LIB has no way to known how to deal with your type and will deal with it like a classic C type. When you define an instance of a new container, you give the type oplist you want to use as the base of the container. This type oplist performs the association between the operators and the methods for the type. In function of the available interface of the oplist, the container definition macro function generates the interface of the container. You can then use this interface directly. You can also use the oplist of the container to chain this new interface with another container, creating container-of-container. ![oplist and definition](https://raw.githubusercontent.com/P-p-H-d/mlib/master/doc/oplist.png) A function name can be followed by the token M\_IPTR in the oplist (for example: (INIT(init\_func M\_IPTR)) ) to specify that the function accept as its *first* argument a pointer to the type rather than the type itself (aka the prototype is init\_func(type `*`) instead of init\_func(type)). If you use the '[1]' trick (see below), you won't need this. See also the API\_\* transformation call below for further transformation means of the calls. An oplist has no real form from a C language point of view. It is just an abstraction that disappears after the macro expansion step of the preprocessing. For each object / container, an oplist is needed. The following operators are usually expected for an object: * INIT(obj): initialize the object 'obj' into a valid state (constructor). * INIT\_SET(obj, org): initialize the object 'obj' into the same state as the object 'org' (constructor). * SET(obj, org): set the initialized object 'obj' into the same state as the initialized object org (operator). * CLEAR(obj): destroy the initialized object 'obj', releasing any attached memory (destructor). This method shall never fail. INIT, INIT\_SET & SET methods shall only fail due to ***memory errors***. Not all operators are needed within an oplist to handle a container. If some operator is missing, the associated default operator of the function is used. To use C integer or float types, the default constructors are perfectly fine: you may use M\_DEFAULT\_OPLIST to define the operator list of such types or you can just omit it. NOTE: An iterator doesn't have a constructor nor destructor methods. It should not allocate any memory. A reference to an object through an iterator is only valid until another reference is taken from the same container (potentially through another iterator), the iterator is moved. If the container is modified, all iterators of this container become invalid and shall not be used anymore except if the modifying operator provided itself an updated iterator. Some containers may lessen these constraints. Other documented operators are: * NAME() --> prefix: Return the base name (prefix) used to construct the container. * TYPE() --> type: Return the base type associated to this oplist. * SUBTYPE() --> type: Return the type of the element stored in the container (used to iterate over the container). * OPLIST() --> oplist: Return the oplist of the type of the elements stored in the container. * KEY\_TYPE() --> key\_t: Return the key type for associative containers. * VALUE\_TYPE() --> value\_t: Return the value type for associative containers. * KEY\_OPLIST() --> oplist: Return the oplist of the key type for associative containers. * VALUE\_OPLIST() --> oplist: Return the oplist of the value type for associative containers. * NEW (type) -> type pointer: allocate a new object (with suitable alignment and size) and return a pointer to it. The returned object is **not initialized** (a constructor operator shall be called afterward). The default method is M\_MEMORY\_ALLOC (that allocates from the heap). It returns NULL in case of failure. * DEL (&obj): free the allocated uninitialized object 'obj'. The object is not cleared before being free (A destructor operator shall be called before). The object shall have been allocated by the associated NEW method. The default method is M\_MEMORY\_DEL (that frees to the heap). * REALLOC(type, type pointer, number) --> type pointer: realloc the given array referenced by type pointer (either a NULL pointer or a pointer returned by the associated REALLOC method itself) to an array of the number of objects of this type and return a pointer to this new array. Previously objects pointed by the pointer are kept up to the minimum of the new size and old one. New objects are not initialized (a constructor operator shall be called afterward). Freed objects are not cleared (A destructor operator shall be called before). The default is M\_MEMORY\_REALLOC (that allocates from the heap). It returns NULL in case of failure in which case the original array is not modified. * FREE (&obj) : free the allocated uninitialized array object 'obj'. The objects are not cleared before being free (CLEAR operator has to be called before). The object shall have been allocated by the associated REALLOC method. The default is M\_MEMORY\_FREE (that frees to the heap). * INC\_ALLOC(size\_t s) -> size\_t: Define the growing policy of an array (or equivalent structure). It returns a new allocation size based on the old allocation size ('s'). Default policy is to get the maximum between '2*s' and 16. NOTE: It doesn't check for overflow: if the returned value is lower than the old one, the user shall raise an overflow error. * INIT\_MOVE(objd, objc): Initialize 'objd' to the same state than 'objc' by stealing as much resources as possible from 'objc', and then clear 'objc' (constructor of objd + destructor of objc). It is semantically equivalent to calling INIT\_SET(objd,objc) then CLEAR(objc) but is usually way faster. By default, all objects are assumed to be **trivially movable** (i.e. using memcpy to move an object is safe). Most C objects (even complex structure) are trivially movable. A notable exception are intrusive objects. If an object is not trivially movable, it shall provide an INIT\_MOVE method or disable the INIT\_MOVE method entirely (NOTE: Some containers always assume that the objects are trivially movable). An INIT\_MOVE operator shall not fail. * MOVE(objd, objc): Set 'objd' to the same state than 'objc' by stealing as resources as possible from 'objc' and then clear 'objc' (destructor of 'objc'). It is equivalent to calling SET(objd,objc) then CLEAR(objc) or CLEAR(objd) and then INIT\_MOVE(objd, objc). TBC if this operator is really needed as calling CLEAR then INIT\_MOVE is what do all known implementation, and is efficient. A MOVE operator shall not fail. * INIT\_WITH(obj, ...): Initialize the object 'obj' with the given variable set of arguments (constructor). Arguments can be of different types. It is up to the method of the objet to decide how to initialize the object based on this initialization array. This operator is used in the M\_LET macro to initialize objects with their given values. * SWAP(objd, objc): Swap the states of the object 'objc' and the object 'objd'. * RESET(obj): Reset the object to its init state (Emptying the object if it is an object). * EMPTY\_P(obj) --> bool: Test if the container object is empty (true) or not. * GET\_SIZE (container) --> size\_t: Return the number of elements in the container object. * HASH (obj) --> size\_t: return a hash of the object (not a secure hash but one that is usable for a hash table). Default is performing a hash of the memory representation of the object. This default implementation is invalid if the object holds pointer to other objects. * EQUAL(obj1, obj2) --> bool: Compare the two objects for equality. Return true if both objects are equal, false otherwise. Default is using the C comparison operator. 'obj1' may be an OOR object (Out of Representation) for the Open Addressing dictionary (see OOR\_* operators): in such cases, it shall return false. * CMP(obj1, obj2) --> int: Provide a complete order the objects. return a negative integer if obj1 < obj2, 0 if obj1 = obj2, a positive integer otherwise. Default is C comparison operator. NOTE: The equivalence between EQUAL(a,b) and CMP(a,b)==0 is not required, but usually welcome. * ADD(obj1, obj2, obj3) : Set obj1 to the sum of obj2 and obj3. Default is '+' C operator. * SUB(obj1, obj2, obj3) : Set obj1 to the difference of obj2 and obj3. Default is '-' C operator. * MUL(obj1, obj2, obj3) : Set obj1 to the product of obj2 and obj3. Default is '*' C operator. * DIV(obj1, obj2, obj3) : Set obj1 to the division of obj2 and obj3. Default is '/' C operator. * GET\_KEY (container, key) --> &value: Return a pointer to the value object within the container associated to the key 'key' or return NULL if no object is associated to this key. The pointer to the value object remains valid until any modification of the container. * SET\_KEY (container, key, value): Associate the key object 'key' to the value object 'value' in the given container. * SAFE\_GET\_KEY (container, key) --> &value: return a pointer to the value object within the container associated to the key 'key' if it exists, or create a new entry in the container and associate it to the key 'key' with the default initialization before returning its pointer. The pointer to the object remains valid until any modification of the container. The returned pointer is therefore never NULL. * ERASE\_KEY (container, key) --> bool: Erase the object associated to the key 'key' within the container. Return true if successful, false if the key is not found (nothing is done). * PUSH(obj, subobj) : Push 'subobj' (of type SUBTYPE() ) into the container 'obj'. How and where it is pushed is container dependent. * POP(&subobj, obj) : Pop an object from the container 'obj' and save it in the object '*subobj' (of type SUBTYPE()) if subobj is not NULL (giving back the ownership of the sub object to the caller). Which object is popped is container dependent. The container shall have at least one object. * PUSH\_MOVE(obj, &subobj) : Push and move the object '*subobj' (of type SUBTYPE()) into the container 'obj' (subobj destructor). How it is pushed is container dependent. '*subobj' is cleared afterward and shall not be used anymore. * POP\_MOVE(&subobj, obj) : Pop an object from the container'obj' and **init & move** it in the unitialized object '*subobj' (*subobj constructor). Which object is popped is container dependent. '*subobj' shall be uninitialized. The container shall have at least one object. * IT\_TYPE() --> type: Return the type of the iterator object of this container. * IT\_FIRST(it\_obj, obj): Set the iterator it\_obj to the first sub-element of the container 'obj'. What is the first element is container dependent (it may be front or back, or something else). However, iterating from FIRST to LAST (included) or END (excluded) through IT\_NEXT ensures going through all elements of the container. If there is no sub-element in the container, it references an end of the container. * IT\_LAST(it\_obj, obj): Set the iterator it\_obj to the last sub-element of the container 'obj'. What is the last element is container dependent (it may be front or back, or something else). However, iterating from LAST to FIRST (included) or END (excluded) through IT\_PREVIOUS ensures going through all elements of the container. If there is no sub-element in the container, it references an end of the container. * IT\_END(it\_obj, obj): Set the iterator it\_obj to an end of the container 'obj'. Once an iterator has reached an end, it can't use PREVIOUS or NEXT operators. If an iterator has reached an END, it means that there is no object referenced by the iterator (kind of NULL pointer). There can be multiple representation of the end of a container, but all of then share the same properties. * IT\_SET(it\_obj, it\_obj2): Set the iterator it\_obj to reference the same sub-element as it\_obj2. * IT\_END\_P(it\_obj)--> bool: Return true if the iterator it\_obj references an end of the container, false otherwise. * IT\_LAST\_P(it\_obj)--> bool: Return true if the iterator it\_obj references the last element of the container (just before reaching an end) or has reached an end of the container, false otherwise. * IT\_EQUAL\_P(it\_obj, it\_obj2) --> bool: Return true if both iterators reference the same element, false otherwise. * IT\_NEXT(it\_obj): Move the iterator to the next sub-element or an end of the container if there is no more sub-element. The direction of IT\_NEXT is container dependent. it\_obj shall not be an end of the container. * IT\_PREVIOUS(it\_obj): Move the iterator to the previous sub-element or an end of the container if there is no more sub-element. The direction of PREVIOUS is container dependent, but it is the reverse of the IT\_NEXT operator. it\_obj shall not be an end of the container. * IT\_CREF(it\_obj) --> &subobj: Return a constant pointer to the object referenced by the iterator (of type const SUBTYPE()). This pointer remains valid until any modifying operation on the container, or until another reference is taken from this container through an iterator (some containers may reduce theses constraints, for example a list). The iterator shall not be an end of the container. * IT\_REF(it\_obj) --> &subobj: Same as IT\_CREF, but return a modifiable pointer to the object referenced by the iterator. * IT\_INSERT(obj, it\_obj, subobj): Insert 'subobj' after 'it\_obj' in the container 'obj' and update it\_obj to point to the inserted object (as per IT\_NEXT semantics). All other iterators of the same container become invalidated. If 'it\_obj' is an end of the container, it inserts the object as the first one. * IT\_REMOVE(obj, it\_obj): Remove it\_obj from the container 'obj' (clearing the associated object) and update it\_obj to point to the next object (as per IT\_NEXT semantics). As it modifies the container, all other iterators of the same container become invalidated. it\_obj shall not be an end of the container. * SPLICE\_BACK(objd, objs, it\_obj): Move the object of the container 'objs' referenced by the iterator 'it\_obj' to the container 'objd'. Where it is moved is container dependent (it is recommended however to be like the PUSH method). Afterward 'it\_obj' references the next item in 'containerSrc' (as per IT\_NEXT semantics). 'it\_obj' shall not be an end of the container. Both objects shall use the same allocator. * SPLICE\_AT(objd, id\_objd, objs, it\_objs): Move the object referenced by the iterator 'it\_objs' from the container 'objs' just after the object referenced by the iterator 'it\_objd' in the container 'objd'. If 'it\_objd' references an end of the container, it is inserted as the first item of the container (See operator 'IT\_INSERT'). Afterward 'it\_objs' references the next item in the container 'objs', and 'it\_objd' references the moved item in the container 'objd'. 'it\_objs' shall not be an end of the container. Both objects shall use the same allocator. * OUT\_STR(FILE* f, obj): Output 'obj' as a custom formatted string into the FILE stream 'f'. Format is container dependent, but is human readable. * IN\_STR(obj, FILE* f) --> bool: Set 'obj' to the value associated to the string representation of the object in the FILE stream 'f'. Return true in case of success (in that case the stream 'f' has been advanced to the end of the parsing of the object), false otherwise (in that case, the stream 'f' is in an undetermined position but is likely where the parsing fails). It ensures that an object which is output in a FILE through OUT\_STR, and an object which is read from this FILE through IN\_STR are considered as equal. * GET\_STR(string\_t str, obj, bool append): Set 'str' to a formatted string representation of the object 'obj'. Append to the string if 'append' is true, set it otherwise. This operator requires the module m-string. * PARSE\_STR(obj, const char *str, const char **endp) --> bool: Set 'obj' to the value associated to the formatted string representation of the object in the char stream 'str'. Return true in case of success (in that case if endp is not NULL, it points to the end of the parsing of the object), false otherwise (in that case, if endp is not NULL, it points to an undetermined position but likely to be where the parsing fails). It ensures that an object which is output in a string through GET\_STR, and an object which is read from this string through GET\_STR are considered as equal. * OUT\_SERIAL(m\_serial\_write\_t *serial, obj) --> m\_serial\_return\_code\_t : Output 'obj' into the configurable serialization stream 'serial' (See #[m-serial-json.h](#m-serial-json) for details and example) as per the serial object semantics. Return M\_SERIAL\_OK\_DONE in case of success, or M\_SERIAL\_FAIL otherwise . * IN\_SERIAL(obj, m\_serial\_read\_t *serial) --> m\_serial\_return\_code\_t: Set 'obj' to its representation from the configurable serialization stream 'serial' (See #[m-serial-json.h](#m-serial-json) for details and example) as per the serial object semantics. M\_SERIAL\_OK\_DONE in case of success (in that case the stream 'serial' has been advanced up to the complete parsing of the object), or M\_SERIAL\_FAIL otherwise (in that case, the stream 'serial' is in an undetermined position but usually around the next characters after the first failure). * OOR\_SET(obj, int\_value): Some containers want to store some information within the uninitialized objects (for example Open Addressing Hash Table). This method stores the integer value 'int\_value' into an uninitialized object 'obj'. It shall be able to differentiate between uninitialized object and initialized object (How is type dependent). The way to store this information is fully object dependent. In general, you use out-of-range value for detecting such values. The object remains uninitialized but sets to of out-of-range value (OOR). int\_value can be 0 or 1. * OOR\_EQUAL(obj, int\_value): This method compares the object 'obj' (initialized or uninitialized) to the out-of-range value (OOR) representation associated to 'int\_value' and returns true if both objects are equal, false otherwise. See OOR\_SET. * REVERSE(obj) : Reverse the order of the items in the container 'obj'. * SEPARATOR() --> character: Return the character used to separate items for I/O methods (default is ',') (for internal use only). * EXT\_ALGO(name, container oplist, object oplist): Define additional algorithms functions specialized for the containers (for internal use only). * PROPERTIES() --> ( properties): Return internal properties of a container (for internal use only) in an oplist format. The operator names listed above shall not be defined as macro. More operators are expected. Example: (INIT(mpz_init),SET(mpz_set),INIT_SET(mpz_init_set),CLEAR(mpz_clear)) If there is two operations with the same name in an oplist, the left one has the priority. This enables partial overriding. Oplist can be registered globally by defining, for the type 'type', a macro named M\_OPL\_ ## type () that expands to the oplist of the type. Only type without space in their name can be registered. A typedef of the type can be used instead through. This can simplify a lot OPLIST usage. Example: #define M_OPL_mpz_t() M_CLASSIC_OPLIST(mpz) Within an OPLIST, you can also specify the needed low-level transformation to perform for the method. This is called API type. Assuming that the method to call is called 'method' and the first argument of the operator is 'output', then the following transformation are applied: * API\_0: method(output, ...) /\* Default transformation API \*/ * API\_1: method(oplist, output, ...) /\* Give oplist to the method \*/ * API\_2: method(&output, ...) /\* Pass by address the first argument (like with M\_IPTR) \*/ * API\_3: method(oplist, &output, ...) /\* Pass by address the first argument (like with M\_IPTR) and give the oplist of the type \*/ * API\_4 : output = method(...) /\* Pass by return value the first argument \*/ * API\_5: output = method(oplist, ...) /\* Pass by return value the first argument and give the oplist of the type \*/ * API\_6 : method(&output, &...) /\* Pass by address the two first arguments \*/ * API\_7: method(oplist, &output, &...) /\* Pass by address the two first argument and give the oplist of the type \*/ Example: (INIT(API_0(mpz_init)), SET(API_0(mpz_set)), INIT_SET(API_0(mpz_init_set)), CLEAR(API_0(mpz_clear))) An operator OP can be defined, omitted or disabled: * ( OP(f) ): the function f is the method of the operator OP * ( ): the operator NEW OP omitted, and the default global operation for OP is used. * ( OP(0) ): the operator OP is disabled, and it can never be used. ### Which OPLIST to use? My type is: * a C Boolean: M\_BOOL\_OPLIST (M\_DEFAULT\_OPLIST also works partially), * a C integer or a C float: M\_DEFAULT\_OPLIST (it can also be omitted), * a C enumerate: M\_ENUM\_OPLIST, * a pointer to something (the contained do nothing on the pointed object): M\_PTR\_OPLIST, * a plain structure that can be init/copy/compare with memset/memcpy/memcmp: M\_POD\_OPLIST, * a plain structure that is passed by reference using [1] and can be init,copy,compare with memset,memcpy,memcmp: M\_A1\_OPLIST, * a type that offers name\_init, name\_init\_set, name\_set, name\_clear methods: M\_CLASSIC\_OPLIST, * a const string (const char *) that is neither freed nor moved: M\_CSTR\_OPLIST, * a M\*LIB string\_t: STRING\_OPLIST, * a M\*LIB container: the OPLIST of the used container, * other things: you need to provide a custom OPLIST to your type. Note: The precise exported methods of the Oplist depend of the version of the C language used. Typically, in C11 mode, the M\_DEFAULT\_OPLIST exports all needed methods to handle generic input/output of int/floats (using \_Generic) whereas it is not possible in C99 mode. This explains why JSON import/export is only available in C11 mode (See below chapter). Memory Allocation ----------------- Memory Allocation functions can be globally set by overriding the following macros before using the definition \_DEF macros: * M\_MEMORY\_ALLOC (type): return a pointer to a new object of type 'type'. * M\_MEMORY\_DEL (ptr): free the single object pointed by 'ptr'. * M\_MEMORY\_REALLOC (type, ptr, number): return a pointer to an array of 'number' objects of type 'type', reusing the old array pointed by 'ptr'. 'ptr' can be NULL, in that case the array will be created. * M\_MEMORY\_FREE (ptr): free the array of objects pointed by 'ptr'. ALLOC & DEL operators are supposed to allocate fixed size single element object (no array). Theses objects are not expected to grow. REALLOC & FREE operators deal with allocated memory for growing objects. Do not mix pointers between both: a pointer allocated by ALLOC (resp. REALLOC) is supposed to be only freed by DEL (resp. FREE). There are separated 'free' operators to enable specialization in the allocator (a good allocator can take this hint into account). M\_MEMORY\_ALLOC and M\_MEMORY\_REALLOC are supposed to return NULL in case of memory allocation failure. The defaults are 'malloc', 'free', 'realloc' and 'free'. You can also override the methods NEW, DEL, REALLOC & DEL in the oplist given to a container so that only the container will use these memory allocation functions. Out-of-memory error ------------------- When a memory exhaustion is reached, the global macro "M\_MEMORY\_FULL" is called and the function returns immediately after. The object remains in a valid (if previously valid) and unchanged state in this case. By default, the macro prints an error message and aborts the program: handling non-trivial memory errors can be hard, testing them is even harder but still mandatory to avoid security holes. So the default behavior is rather conservative. Indeed, a good design should handle a process entire failure (using for examples multiple processes for doing the job) so that even if a process stops, it should be recovered. See [here](http://joeduffyblog.com/2016/02/07/the-error-model/) for more information about why abandonment is good software practice. It can however be overloaded to handle other policy for error handling like: * throwing an error (in that case, the user is responsible to free memory of the allocated objects - destructor can still be called), * set a global error and handle it when the function returns, * other policy. This function takes the size in bytes of the memory that has been tried to be allocated. If needed, this macro shall be defined ***prior*** to instantiate the structure. NOTE: Throwing an error is not fully supported yet. Some help from the library is needed to avoid losing memory. See issue #15. ERRORS & COMPILERS ------------------ M\*LIB implements internally some controls to reduce the list of errors/warnings generated by a compiler when it detects some violation in the use of oplist by use of static assertion. It can also transform some type warnings into proper errors. In C99 mode, it will produce illegal code with the name of the error as attribute. In C11 mode, it will use static assert and produce a detailed error message. The list of errors it can generate: * M\_LIB\_NOT\_AN\_OPLIST: something has been given (directly or indirectly) and it doesn't reduce as a proper oplist. You need to give an oplist for this definition. * M\_LIB\_ERROR(ARGUMENT\_OF\_\*\_OPLIST\_IS\_NOT\_AN\_OPLIST, name, oplist): sub error of the previous error one, identifying the root cause. The error is in the oplist construction of the given macro. You need to give an oplist for this oplist construction. * M\_LIB\_MISSING\_METHOD: a required operator doesn't define any method in the given oplist. You need to complete the oplist with the missing method. * M\_LIB\_TYPE\_MISTMACH: the given oplist and the type do not match each other. You need to give the right oplist for this type. * M\_LIB\_NOT\_A\_DEFAULT\_TYPE: The oplist M\_DEFAULT\_OPLIST (directly or indirectly) has been used with the given type, but the given type is not a default type. You need to give the right oplist for this type. You should focus mainly on the first reported error/warning even if the link between what the compiler report and what the error is is not immediate. The error is always in one of the **oplist definition**. Examples of typical errors: * lack of inclusion of an header, * overriding locally operator names by macros (like NEW, DEL, INIT, OPLIST, ...), * lack of ( ) or double level of ( ) around the oplist, * an unknown variable (example using DEFAULT\_OPLIST instead of M\_DEFAULT\_OPLIST or M\_STRING\_OPLIST instead of STRING\_OPLIST), * the name given to the oplist is not the same as the one used to define the methods, * use of a type instead of an oplist in the OPLIST definition, * a missing sub oplist in the OPLIST definition. A good way to avoid theses errors is to register the oplist globally as soon as you define the container. In case of difficulties, debugging can be done by generating the preprocessed file (by usually calling the compiler with the '-E' option instead of '-c') and check what's wrong in the preprocessed file: ```shell cc -std=c99 -E test-file.c > test-file.i perl -pi -e 's/;/;\n/g' test-file.i cc -std=c99 -c -Wall test-file.i ``` If there is a warning reported by the compiler in the generated code, then there is definitely an **error** you should fix (except if it reports shadowed variables), in particular cast evolving pointers. Benchmarks ---------- All bench codes are available in the bench directory. The results are available [in a separate page](https://github.com/P-p-H-d/mlib/wiki/performance). External Reference ------------------ Many other implementation of generic container libraries in C exist. For example: * [BKTHOMPS/CONTAINERS](https://github.com/bkthomps/Containers) * [BSD tree.h](http://fxr.watson.org/fxr/source/sys/tree.h) * [CDSA](https://github.com/MichaelJWelsh/cdsa) * [CELLO](http://libcello.org/) * [C-Macro-Collections](https://github.com/LeoVen/C-Macro-Collections) * [COLLECTIONS-C](https://github.com/srdja/Collections-C) * [CONCURRENCY KIT](https://github.com/concurrencykit/ck) * [CTL by glouw](https://github.com/glouw/ctl) or [by rurban](https://github.com/rurban/ctl) * [GDB internal library](https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/common/vec.h;h=41e41b5b22c9f5ec14711aac35ce4ae6bceab1e7;hb=HEAD) * [GLIB](https://wiki.gnome.org/Projects/GLib) * [KLIB](https://github.com/attractivechaos/klib) * [LIBCHASTE](https://github.com/mgrosvenor/libchaste) * [LIBCOLLECTION](https://bitbucket.org/manvscode/libcollections) * [LIBDICT](https://github.com/fmela/libdict) * [LIBDYNAMIC](https://github.com/fredrikwidlund/libdynamic) * [LIBLFDS](https://www.liblfds.org/) * [LIBSRT: Safe Real-Time library for the C programming language](https://github.com/faragon/libsrt) * [NEDTRIES](https://github.com/ned14/nedtries) * [QLIBC](http://wolkykim.github.io/qlibc/) * [SGLIB](http://sglib.sourceforge.net/) * [Smart pointer for GNUC](https://github.com/Snaipe/libcsptr) * [STB stretchy_buffer](https://github.com/nothings/stb) * [STC - C99 Standard Container library](https://github.com/tylov/C99Containers) * [TommyDS](https://github.com/amadvance/tommyds) * [UTHASH](http://troydhanson.github.io/uthash/index.html) Each can be classified into one of the following concept: * Each object is handled through a pointer to void (with potential registered callbacks to handle the contained objects for the specialized methods). From a user point of view, this makes the code harder to use (as you don't have any help from the compiler) and type unsafe with a lot of cast (so no formal proof of the code is possible). This also generally generate slower code (even if using link time optimization, this penalty can be reduced). Properly used, it can yet be the most space efficient for the code, but can consume a lot more for the data due to the obligation of using pointers. This is however the easiest to design & code. * Macros are used to access structures in a generic way (using known fields of a structure - typically size, number, etc.). From a user point of view, this can create subtitle bug in the use of the library (as everything is done through macro expansion in the user defined code) or hard to understand warnings. This can generates fully efficient code. From a library developer point of view, this can be quite limiting in what you can offer. * A known structure is put in an intrusive way in the type of all the objects you wan to handle. From a user point of view, he needs to modify its structure and has to perform all the allocation & deallocation code itself (which is good or bad depending on the context). This can generate efficient code (both in speed and size). From a library developer point of view, this is easy to design & code. You need internally a cast to go from a pointer to the known structure to the pointed object (a reverse of offsetof) that is generally type unsafe (except if mixed with the macro generating concept). This is quite limitation in what you can do: you can't move your objects so any container that has to move some objects is out-of-question (which means that you cannot use the most efficient container). * Header files are included multiple times with different contexts (some different values given to defined macros) in order to generate different code for each type. From a user point of view, this creates a new step before using the container: an instantiating stage that has to be done once per type and per compilation unit (The user is responsible to create only one instance of the container, which can be troublesome if the library doesn't handle proper prefix for its naming convention). The debug of the library is generally easy and can generate fully specialized & efficient code. Incorrectly used, this can generate a lot of code bloat. Properly used, this can even create smaller code than the void pointer variant. The interface used to configure the library can be quite tiresome in case of a lot of specialized methods to configure: it doesn't enable to chain the configuration from a container to another one easily. It also cannot have heavy customization of the code. * Macros are used to generate context-dependent C code enabling to generate code for different type. This is pretty much like the headers solution but with added flexibility. From a user point of view, this creates a new step before using the container: an instantiating stage that has to be done once per type and per compilation unit (The user is responsible to create only one instance of the container, which can be troublesome if the library doesn't handle proper prefix for its naming convention). This can generate fully specialized & efficient code. Incorrectly used, this can generate a lot of code bloat. Properly used, this can even create smaller code than the void pointer variant. From a library developer point of view, the library is harder to design and to debug: everything being expanded in one line, you can't step in the library (there is however a solution to overcome this limitation by adding another stage to the compilation process). You can however see the generated code by looking at the preprocessed file. You can perform heavy context-dependent customization of the code (transforming the macro preprocessing step into its own language). Properly done, you can also chain the methods from a container to another one easily, enabling expansion of the library. Errors within the macro expansion are generally hard to decipher, but errors in code using containers are easy to read and natural. M\*LIB's category is mainly the last one. Some macros are also defined to access structure in a generic way, but they are optional. There are also intrusive containers. M\*LIB main added value compared to other libraries is its oplist feature enabling it to chain the containers and/or use complex types in containers: list of array of dictionary of C++ objects are perfectly supported by M\*LIB. For the macro-preprocessing part, other libraries also exist. For example: * [P99](http://p99.gforge.inria.fr/p99-html/) * [C99 Lambda](https://github.com/Leushenko/C99-Lambda) * [MAP MACRO](https://github.com/swansontec/map-macro) * [C Preprocessor Tricks, Tips and Idioms](https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms) * [CPP MAGIC](http://jhnet.co.uk/articles/cpp_magic) * [Zlang](https://github.com/pfultz2/ZLang) * [Boost preprocessor](http://www.boost.org/doc/libs/1_63_0/libs/preprocessor/doc/index.html) * [OrderPP](https://github.com/rofl0r/order-pp) * [metalang99](https://github.com/Hirrolot/metalang99) For the string library, there is for example: * [The Better String Library](http://bstring.sourceforge.net/) with a page that lists a lot of other string libraries. * [VSTR](http://www.and.org/vstr/) with a [page](http://www.and.org/vstr/comparison) that lists a lot of other string libraries. * [SDS](https://github.com/antirez/sds) * [STC - C99 Standard Container library](https://github.com/tylov/C99Containers) API Documentation ----------------- The M*LIB reference card is available [here](http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Container.html). ### M-LIST This header is for creating [singly linked list](https://en.wikipedia.org/wiki/Linked_list). A linked list is a linear collection of elements, in which each element points to the next, all representing a sequence. #### LIST\_DEF(name, type, [, oplist]) #### LIST\_DEF\_AS(name, name\_t, name\_it\_t, type, [, oplist]) LIST\_DEF defines the singly linked list named 'name##\_t' that contains objects of type 'type' and the associated methods as "static inline" functions. 'name' shall be a C identifier that will be used to identify the list. It will be used to create all the types and functions to handle the container. This definition shall be done once per name and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. A fundamental property of a list is that the objects created within the list will remain at their initialized address, and won't moved due to operations done on the list (except if it is removed). The type oplist is expected to have at least the following operators (INIT\_SET, SET and CLEAR). If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used if there is one available. The created methods use the operators to init, set and clear the contained object. For this structure, the back is always the first element, and the front is the last element: the list grows from the back. Example: ```C #include #include #include "m-list.h" #define MPZ_OUT_STR(stream, x) mpz_out_str(stream, 0, x) LIST_DEF(list_mpz, mpz_t, \ (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), OUT_STR(MPZ_OUT_STR))) int main(void) { list_mpz_t a; list_mpz_init(a); mpz_t x; mpz_init_set_ui(x, 16); list_mpz_push_back (a, x); mpz_set_ui(x, 45); list_mpz_push_back (a, x); mpz_clear(x); printf ("LIST is: "); list_mpz_out_str(stdout, a); printf ("\n"); printf ("First element is: "); mpz_out_str(stdout, 0, *list_mpz_back(a)); printf ("\n"); list_mpz_clear(a); } ``` If the given oplist contain the method MEMPOOL, then LIST\_DEF macro will create a dedicated mempool that is named with the given value of the method MEMPOOL. This mempool (see mempool chapter) is optimized for this kind of list: * it creates a mempool named by the concatenation of "name" and "\_mempool", * it creates a variable named by the value of the method MEMPOOL with the linkage defined by the value of the method MEMPOOL\_LINKAGE (can be extern, static or none). This variable is shared by all lists of the same type. * it links the memory allocation of the list to use this mempool with this variable. mempool create heavily efficient list. However it is only worth the effort in some heavy performance context. The created mempool has to be explicitly initialized before using any methods of the created list by calling mempool\_list\_name\_init(variable) and cleared by calling mempool\_list\_name\_clear(variable). Example: ```C LIST_DEF(list_uint, unsigned int, (MEMPOOL(list_mpool), MEMPOOL_LINKAGE(static))) static void test_list (size_t n) { list_uint_mempool_init(list_mpool); M_LET(a1, LIST_OPLIST(uint)) { for(size_t i = 0; i < n; i++) list_uint_push_back(a1, rand_get() ); } list_uint_mempool_clear(list_mpool); } ``` LIST\_DEF\_AS is the same as LIST\_DEF except the name of the types name\_t, name\_it\_t are provided. #### LIST\_OPLIST(name [, oplist]) Return the oplist of the list defined by calling LIST\_DEF & LIST\_DUAL\_PUSH\_DEF with name & oplist. If there is no given oplist, the default oplist for standard C type is used. #### LIST\_INIT\_VALUE() Define an initial value that is suitable to initialize global variable(s) of type 'list' as created by LIST\_DEF or LIST\_DEF\_AS. It enables to create a list as a global variable and to initialize it. The list should still be cleared manually to avoid leaking memory. Example: ```C LIST_DEF(list_int_t, int) list_int_t my_list = LIST_INIT_VALUE(); ``` #### Created methods In the following methods, name stands for the name given to the macro that is used to identify the type. The following types are automatically defined by the previous definition macro: #### name\_t Type of the list of 'type'. #### name\_it\_t Type of an iterator over this list. The following methods are automatically created by the previous definition macro: ##### void name\_init(name\_t list) Initialize the list 'list' (aka constructor) to an empty list. ##### void name\_init\_set(name\_t list, const name\_t ref) Initialize the list 'list' (aka constructor) and set it to a copy of 'ref'. ##### void name\_set(name\_t list, const name\_t ref) Set the list 'list' to the a copy of 'ref'. ##### void name\_init\_move(name\_t list, name\_t ref) Initialize the list 'list' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared and can no longer be used. ##### void name\_move(name\_t list, name\_t ref) Set the list 'list' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared and can no longer be used. ##### void name\_clear(name\_t list) Clear the list 'list (aka destructor), calling the CLEAR method of all the objects of the list and freeing memory. The list can't be used anymore, except with a constructor. ##### void name\_reset(name\_t list) Reset the list (the list becomes empty but remains initialized and empty). ##### type *name\_back(const name\_t list) Return a pointer to the data stored in the back of the list. ##### void name\_push\_back(name\_t list, const type value) Push a new element within the list 'list' with the value 'value' contained within. ##### type *name\_push\_raw(name\_t list) Push a new element within the list 'list' without initializing it and returns a pointer to the **non-initialized** data. The first thing to do after calling this function is to initialize the data using the proper constructor. This enables using more specialized constructor than the generic one. Return a pointer to the **non-initialized** data. ##### type *name\_push\_new(name\_t list) Push a new element within the list 'list' and initialize it with the default constructor of the type. Return a pointer to the initialized object. This method is only defined if the type of the element defines an INIT method. ##### void name\_push\_move(name\_t list, type *value) Push a new element within the list 'list' with the value '\*value' contained within by stealing as much resources from '\*value' than possible. Afterward '\*value' is cleared and cannot longer be used. ##### void name\_pop\_back(type *data, name\_t list) Pop a element from the list 'list', and set *data to this value if data is not the NULL pointer (otherwise only pop the data). ##### void name\_pop\_move(type *data, name\_t list) Pop a element from the list 'list', and initialize and set *data to this value by stealing as much resources from the list as possible. data cannot be a NULL pointer. ##### bool name\_empty\_p(const name\_t list) Return true if the list is empty, false otherwise. ##### void name\_swap(name\_t list1, name\_t list2) Swap the list 'list1' and 'list2'. ##### void name\_it(name\_it\_t it, const name\_t list) Set the iterator 'it' to the back(=first) element of 'list'. There is no destructor associated to this initialization. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the iterator 'ref'. There is no destructor associated to this initialization. ##### void name\_it\_end(name\_it\_t it, const name\_t list) Set the iterator 'it' to a non valid element of the list. There is no destructor associated to this initialization. ##### bool name\_end\_p(const name\_it\_t it) Return true if the iterator doesn't reference a valid element anymore. ##### bool name\_last\_p(const name\_it\_t it) Return true if the iterator references the top(=last) element or if the iterator doesn't reference a valid element anymore. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it2) Return true if the iterator it1 references the same element than it2. ##### void name\_next(name\_it\_t it) Move the iterator 'it' to the next element of the list, i.e. from the back (=first) element to the front (=last) element. ##### type *name\_ref(name\_it\_t it) Return a pointer to the element pointed by the iterator. This pointer remains valid until the element is destroyed in the list. ##### const type *name\_cref(const name\_it\_t it) Return a constant pointer to the element pointed by the iterator. This pointer remains valid until the element is destroyed in the list. ##### type *name\_get(const name\_t list, size\_t i) Return a pointer to the element i-th of the list (from 0). It is assumed than i is within the size of the list. This function is slow and iterates linearly over the element of the elements until it reaches the desired element. ##### const type *name\_cget(const name\_t list, size\_t i) Return a constant pointer to the element i-th of the list (from 0). It is assumed than i is within the size of the list. This function is slow and iterates linearly over the element of the elements until it reaches the desired element. ##### size\_t name\_size(const name\_t list) Return the number elements of the list (aka size). Return 0 if there no element. This function is slow and iterates linearly over all the element to compute the size. ##### void name\_insert(name\_t list, const name\_it\_t it, const type x) Insert 'x' after the position pointed by 'it' (which is an iterator of the list 'list') or if 'it' doesn't point anymore to a valid element of the list, it is added as the back (=first) element of the 'list' ##### void name\_remove(name\_t list, name\_it\_t it) Remove the element 'it' from the list 'list'. After wise, 'it' points to the next element of the list. ##### void name\_splice\_back(name\_t list1, name\_t list2, name\_it\_t it) Move the element pointed by 'it' (which is an iterator of 'list2') from the list 'list2' to the back position of 'list1'. After wise, 'it' points to the next element of 'list2'. ##### void name\_splice\_at(name\_t list1, name\_it\_t it1, name\_t list2, name\_it\_t it2) Move the element pointed by 'it2' (which is an iterator of 'list2') from the list 'list2' to the position just after 'it1' in the list 'list1'. After wise, 'it2' points to the next element of 'list2' and 'it1' points to the inserted element in 'list1'. If 'it1' is the end position, it inserts it at the back (just like \_insert\_at). ##### void name\_splice(name\_t list1, name\_t list2) Move all the element of 'list2' into 'list1", moving the last element of 'list2' after the first element of 'list1'. After-wise, 'list2' remains initialized but is emptied. ##### void name\_reverse(name\_t list) Reverse the order of the list. ##### void name\_get\_str(string\_t str, const name\_t list, bool append) Set 'str' to the formatted string representation of the list 'list' (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t list, const char str[], const char **endp) Parse the formatted string 'str', that is assumed to be a string representation of a list and set 'list' to this representation. This method is only defined if the type of the element defines a PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t list) Generate a formatted string representation of the list 'list' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t list, FILE *file) Read from the file 'file' a formatted string representation of a list and set 'list' to this representation. This method is only defined if the type of the element defines a IN\_STR & INIT method itself. ##### bool name\_equal\_p(const name\_t list1, const name\_t list2) Return true if both lists 'list1' and 'list2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### size\_t name\_hash(const name\_t list) Return a hash value of 'list'. This method is only defined if the type of the element defines a HASH method itself. #### LIST\_DUAL\_PUSH\_DEF(name, type[, oplist]) #### LIST\_DUAL\_PUSH\_DEF\_AS(name, name\_t, name\_it\_t, type, [, oplist]) LIST\_DUAL\_PUSH\_DEF defines the singly linked list named 'name##\_t' that contains the objects of type 'type' and the associated methods as "static inline" functions. 'name' shall be a C identifier that will be used to identify the list. It will be used to create all the types and functions to handle the container. It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions too. The only difference with the list defined by LIST\_DEF is the support of the method PUSH\_FRONT in addition to PUSH\_BACK (therefore the DUAL PUSH name). There is still only POP method (POP\_BACK). The head of the list is a bit bigger to be able to handle such methods to work. This enables this list to be able to represent both stack (PUSH\_BACK + POP\_BACK) and queue (PUSH\_FRONT + POP\_BACK). A fundamental property of a list is that the objects created within the list will remain at their initialized address, and won't moved due to operations on the list. The object oplist is expected to have at least the following operators (INIT\_SET, SET and CLEAR). If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. For this structure, the back is always the first element, and the front is the last element. Example: ```C #include #include #include "m-list.h" #define MPZ_OUT_STR(stream, x) mpz_out_str(stream, 0, x) LIST_DUAL_PUSH_DEF(list_mpz, mpz_t, \ (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), OUT_STR(MPZ_OUT_STR))) int main(void) { list_mpz_t a; list_mpz_init(a); mpz_t x; mpz_init_set_ui(x, 16); list_mpz_push_back (a, x); mpz_set_ui(x, 45); list_mpz_push_back (a, x); mpz_clear(x); printf ("LIST is: "); list_mpz_out_str(stdout, a); printf ("\n"); printf ("First element is: "); mpz_out_str(stdout, 0, *list_mpz_back(a)); printf ("\n"); list_mpz_clear(a); } ``` If the given oplist contain the method MEMPOOL, then macro will create a dedicated mempool that is named with the given value of the method MEMPOOL, optimized for this kind of list: * it creates a mempool named by the concatenation of "name" and "\_mempool", * it creates a variable named by the value of the method MEMPOOL with linkage defined by the value of the method MEMPOOL\_LINKAGE (can be extern, static or none), this variable will be shared by all lists of the same type. * it overwrites memory allocation of the created list to use this mempool with this variable. mempool creates heavily efficient list but it will be only worth the effort in some heavy performance context. The created mempool has to be initialized before using any methods of the created list by calling mempool\_list\_name\_init(variable) and cleared by calling mempool\_list\_name\_clear(variable). The methods follow closely the methods defined by LIST\_DEF. LIST\_DUAL\_PUSH\_DEF\_AS is the same as LIST\_DUAL\_PUSH\_DEF except the name of the types name\_t, name\_it\_t are provided. #### LIST\_DUAL\_PUSH\_INIT\_VALUE() Define an initial value that is suitable to initialize global variable(s) of type 'list' as created by LIST\_DUAL\_PUSH\_DEF or LIST\_DUAL\_PUSH\_DEF\_AS. It enables to create a list as a global variable and to initialize it. The list should still be cleared manually to avoid leaking memory. Example: ```C LIST_DUAL_PUSH_DEF(list_int_t, int) list_int_t my_list = LIST_DUAL_PUSH_INIT_VALUE(); ``` #### Created methods In the following methods, name stands for the name given to the macro that is used to identify the type. The following types are automatically defined by the previous macro: #### name\_t Type of the list of 'type'. #### name\_it\_t Type of an iterator over this list. The following methods are automatically and properly created by the previous macro. ##### void name\_init(name\_t list) Initialize the list 'list' (aka constructor) to an empty list. ##### void name\_init\_set(name\_t list, const name\_t ref) Initialize the list 'list' (aka constructor) and set it to the value of 'ref'. ##### void name\_set(name\_t list, const name\_t ref) Set the list 'list' to the value of 'ref'. ##### void name\_init\_move(name\_t list, name\_t ref) Initialize the list 'list' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared and can no longer be used. ##### void name\_move(name\_t list, name\_t ref) Set the list 'list' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared and can no longer be used. ##### void name\_clear(name\_t list) Clear the list 'list (aka destructor). The list can't be used anymore, except with a constructor. ##### void name\_reset(name\_t list) Clean the list (the list becomes empty but remains initialized but is empty). ##### type *name\_back(const name\_t list) Return a pointer to the data stored in the back of the list. ##### void name\_push\_back(name\_t list, type value) Push a new element within the list 'list' with the value 'value' into the back of the list. ##### type *name\_push\_back\_raw(name\_t list) Push a new element within the list 'list' without initializing it into the back of the list and returns a pointer to the **non-initialized** data. The first thing to do after calling this function is to initialize the data using the proper constructor. This enables to use a more specialized constructor than the generic one. Return a pointer to the **non-initialized** data. ##### type *name\_push\_back\_new(name\_t list) Push a new element within the list 'list' into the back of the list and initialize it with the default constructor of the type. Return a pointer to the initialized object. This method is only defined if the type of the element defines an INIT method. ##### void name\_push\_back\_move(name\_t list, type *value) Push a new element within the list 'list' with the value '*value' ,by stealing as much resources from '*value' as possible, into the back of the list. Afterwards, *value is cleared and cannot be used anymore. value cannot be NULL. ##### const type *name\_front(const name\_t list) Return a constant pointer to the data stored in the front of the list. ##### void name\_push\_front(name\_t list, type value) Push a new element within the list 'list' with the value 'value' contained within into the front of the list. ##### type *name\_push\_front\_raw(name\_t list) Push a new element within the list 'list' without initializing it into the front of the list and returns a pointer to the **non-initialized** data. The first thing to do after calling this function is to initialize the data using the proper constructor. This enables to use a more specialized constructor than the generic one. Return a pointer to the **non-initialized** data. ##### type *name\_push\_front\_new(name\_t list) Push a new element within the list 'list' into the front of the list and initialize it with the default constructor of the type. Return a pointer to the initialized object. This method is only defined if the type of the element defines an INIT method. ##### void name\_push\_front\_move(name\_t list, type *value) Push a new element within the list 'list' with the value '*value' ,by stealing as much resources from '*value' as possible, into the front of the list. Afterwards, *value is cleared and cannot be used anymore. value cannot be NULL. ##### void name\_pop\_back(type *data, name\_t list) Pop a element from the list 'list' and set *data to this value if data is not NULL. ##### void name\_pop\_move(type *data, name\_t list) Pop a element from the list 'list' and initialize and set *data to this value, stealing as much resources from the list as possible. ##### bool name\_empty\_p(const name\_t list) Return true if the list is empty, false otherwise. ##### void name\_swap(name\_t list1, name\_t list2) Swap the list 'list1' and 'list2'. ##### void name\_it(name\_it\_t it, name\_t list) Set the iterator 'it' to the back(=first) element of 'list'. There is no destructor associated to this initialization. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the iterator 'ref'. There is no destructor associated to this initialization. ##### void name\_it\_end(name\_it\_t it, const name\_t list) Set the iterator 'it' to an invalid reference of 'list'. There is no destructor associated to this initialization. ##### bool name\_end\_p(const name\_it\_t it) Return true if the iterator doesn't reference a valid element anymore. ##### bool name\_last\_p(const name\_it\_t it) Return true if the iterator references the top(=last) element or if the iterator doesn't reference a valid element anymore. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it2) Return true if the iterator it1 references the same element than it2. ##### void name\_next(name\_it\_t it) Move the iterator 'it' to the next element of the list, ie. from the back (=first) element to the front(=last) element. ##### type *name\_ref(name\_it\_t it) Return a pointer to the element pointed by the iterator. This pointer remains valid until the object is destroyed. ##### const type *name\_cref(const name\_it\_t it) Return a constant pointer to the element pointed by the iterator. This pointer remains valid until the object is destroyed. ##### size\_t name\_size(const name\_t list) Compute and return the number elements of the list (aka size). Return 0 if there no element. ##### void name\_insert(name\_t list, const name\_it\_t it, const type x) Insert 'x' after the position pointed by 'it' (which is an iterator of the list 'list') or if 'it' doesn't point anymore to a valid element of the list, it is added as the back (=first) element of the 'list' ##### void name\_remove(name\_t list, name\_it\_t it) Remove the element 'it' from the list 'list'. After wise, 'it' points to the next element of the list. ##### void name\_splice\_back(name\_t list1, name\_t list2, name\_it\_t it) Move the element pointed by 'it' (which is an iterator of 'list2') from the list 'list2' to the back position of 'list1'. After wise, 'it' points to the next element of 'list2'. ##### void name\_splice(name\_t list1, name\_t list2) Move all the element of 'list2' into 'list1", moving the last element of 'list2' after the first element of 'list1'. After-wise, 'list2' is emptied. ##### void name\_reverse(name\_t list) Reverse the order of the list. ##### void name\_get\_str(string\_t str, const name\_t list, bool append) Generate a formatted string representation of the list 'list' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t list, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a list and set 'list' to this representation. This method is only defined if the type of the element defines PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t list) Generate a formatted string representation of the list 'list' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t list, FILE *file) Read from the file 'file' a formatted string representation of a list and set 'list' to this representation. This method is only defined if the type of the element defines a IN\_STR & INIT method itself. ##### bool name\_equal\_p(const name\_t list1, const name\_t list2) Return true if both lists 'list1' and 'list2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### size\_t name\_hash(const name\_t list) Return a hash value of 'list'. This method is only defined if the type of the element defines a HASH method itself. ### M-ARRAY An [array](https://en.wikipedia.org/wiki/Array_data_structure) is a growable collection of element that are individually indexable. #### ARRAY\_DEF(name, type [, oplist]) #### ARRAY\_DEF\_AS(name, name\_t, name\_it\_t, type, [, oplist]) ARRAY\_DEF defines the array 'name##\_t' that contains the objects of type 'type' and its associated methods as "static inline" functions. Compared to C arrays, the created methods handle automatically the size (aka growable array). 'name' shall be a C identifier that will be used to identify the container. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The object oplist is expected to have at least the following operators (CLEAR), and usually (INIT, INIT\_SET, SET and CLEAR) otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init-and-set, set and clear the contained object. Example: ```C #include #include #include "m-array.h" #define MPZ_OUT_STR(stream, x) mpz_out_str(stream, 0, x) ARRAY_DEF(array_mpz, mpz_t, \ (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), OUT_STR(MPZ_OUT_STR))) int main(void) { array_mpz_t a; array_mpz_init(a); mpz_t x; mpz_init_set_ui(x, 16); array_mpz_push_back (a, x); mpz_set_ui(x, 45); array_mpz_push_back (a, x); mpz_clear(x); printf ("ARRAY is: "); array_mpz_out_str(stdout, a); printf ("\n"); printf ("First element is: "); mpz_out_str(stdout, 0, *array_mpz_get(a, 0)); printf ("\n"); array_mpz_clear(a); } ``` ARRAY\_DEF\_AS is the same as ARRAY\_DEF except the name of the types name\_t, name\_it\_t are provided. #### ARRAY\_OPLIST(name [, oplist]) Return the oplist of the array defined by calling ARRAY\_DEF with name & oplist. If there is no given oplist, the default oplist for standard C type is used. #### ARRAY\_INIT\_VALUE() Define an initial value that is suitable to initialize global variable(s) of type 'array' as created by ARRAY\_DEF or ARRAY\_DEF\_AS. It enables to create an array as a global variable and to initialize it. The array should still be cleared manually to avoid leaking memory. Example: ```C ARRAY_DEF(array_int_t, int) array_int_t my_array = ARRAY_INIT_VALUE(); ``` #### Created methods In the following methods, name stands for the name given to the macro. This is used to identify the type. The following types are automatically defined by the previous macro: #### name\_t Type of the array of 'type'. #### name\_it\_t Type of an iterator over this array. The following methods are automatically and properly created by the previous macros: ##### void name\_init(name\_t array) Initialize the array 'array' (aka constructor) to an empty array. ##### void name\_init\_set(name\_t array, const name\_t ref) Initialize the array 'array' (aka constructor) and set it to the value of 'ref'. This method is created if the INIT\_SET & SET operators are provided. ##### void name\_set(name\_t array, const name\_t ref) Set the array 'array' to the value of 'ref'. This method is created if the INIT\_SET & SET operators are provided. ##### void name\_init\_move(name\_t array, name\_t ref) Initialize the array 'array' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared. ##### void name\_move(name\_t array, name\_t ref) Set the array 'array' by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared. ##### void name\_clear(name\_t array) Clear the array 'array (aka destructor). ##### void name\_reset(name\_t array) Reset the array (the array becomes empty but remains initialized). ##### type *name\_push\_raw(name\_t array) Push the needed storage of a new element into the back of the array 'array' without initializing it and return a pointer to the non-initialized data. The first thing to do after calling this function is to initialize the data using the proper constructor. This enables to use a more specialized constructor than the generic one. It is recommended to use other \_push function if possible rather than this one as it is low level and error prone. ##### void name\_push\_back(name\_t array, const type value) Push a new element into the back of the array 'array' with the value 'value' contained within. This method is created if the INIT\_SET operator is provided. ##### type *name\_push\_new(name\_t array) Push a new element into the back of the array 'array' and initialize it with the default constructor. Return a pointer to this created element. This method is only defined if the type of the element defines an INIT method. ##### void name\_push\_move(name\_t array, type *val) Push '*val' a new element into the back of the array 'array' by stealing as much resources as possible from '*val'. After-wise '*x' is cleared. This method is created if the INIT\_SET or INIT\_MOVE operator is provided. ##### void name\_push\_at(name\_t array, size\_t key, const type x) Push a new element into the position 'key' of the array 'array' with the value 'value' contained within. 'key' shall be a valid position of the array: from 0 to the size of array (included). This method is created if the INIT\_SET operator is provided. ##### void name\_pop\_back(type *data, name\_t array) Pop a element from the back of the array 'array' and set *data to this value if data is not NULL (if data is NULL, the popped data is cleared). This method is created if the SET or INIT\_MOVE operator is provided. ##### void name\_pop\_move(type *data, name\_t array) Pop a element from the back of the array 'array' and initialize *data with this value by stealing as much from the array as possible. This method is created if the INIT\_SET or INIT\_MOVE operator is provided. ##### void name\_pop\_until(name\_t array, array\_it\_t position) Pop all elements of the array 'array' from 'position' to the back of the array, clearing them. This method is only defined if the type of the element defines an INIT method. ##### void name\_pop\_at(type *dest, name\_t array, size\_t key) Set *dest to the value the element 'key' if dest is not NULL, then remove the element 'key' from the array. 'key' shall be within the size of the array. This method is created if the SET or INIT\_MOVE operator is provided. ##### type *name\_front(const name\_t array) Return a pointer to the first element of the array. ##### type *name\_back(const name\_t array) Return a pointer to the last element of the array. ##### void name\_set\_at(name\_t array, size\_t i, type value) Set the element 'i' of array 'array' to 'value'. 'i' shall be within 0 to the size of the array (excluded). This method is created if the INIT\_SET operator is provided. ##### type *name\_get(name\_t array, size\_t i) Return a pointer to the element 'i' of the array. 'i' shall be within 0 to the size of the array (excluded). The returned pointer cannot be NULL. This pointer remains valid until the array is modified by another method. ##### const type *name\_cget(const name\_t it, size\_t i) Return a constant pointer to the element 'i' of the array. 'i' shall be within 0 to the size of the array (excluded). The returned pointer cannot be NULL. This pointer remains valid until the array is modified by another method. ##### type *name\_safe\_get(name\_t array, size\_t i) Return a pointer to the element 'i' of array 'array', by increasing the size of the array if needed (creating new elements with INIT). The returned pointer cannot be NULL. This method is only defined if the type of the element defines an INIT method. This pointer remains valid until the array is modified by another method. ##### bool name\_empty\_p(const name\_t array) Return true if the array is empty, false otherwise. ##### size\_t name\_size(const name\_t array) Return the size of the array. ##### size\_t name\_capacity(const name\_t array) Return the capacity of the array. ##### void name\_resize(name\_t array, size\_t size) Resize the array 'array' to the size 'size' (initializing or clearing elements). This method is only defined if the type of the element defines an INIT method. ##### void name\_reserve(name\_t array, size\_t capacity) Extend or reduce the capacity of the 'array' to a rounded value based on 'capacity'. If the given capacity is below the current size of the array, the capacity is set to the size of the array. ##### void name\_remove(name\_t array, name\_it\_t it) Remove the element pointed by the iterator 'it' from the array 'array'. 'it' shall be a valid iterator. Afterward 'it' points to the next element, or points to the end. This method is created if the SET or INIT\_MOVE operator is provided. ##### void name\_remove\_v(name\_t array, size\_t i, size\_t j) Remove the element 'i' (included) to the element 'j' (excluded) from the array. 'i' and 'j' shall be within the size of the array, and i < j. ##### void name\_insert(name\_t array, name\_it\_t it, const type x) Insert the object 'x' at the position 'it' of the array. 'it' shall be a valid iterator of the array. This method is created if the INIT\_SET operator is provided. ##### void name\_insert\_v(name\_t array, size\_t i, size\_t j) Insert from the element 'i' (included) to the element 'j' (excluded) new empty elements to the array. 'i' and 'j' shall be within the size of the array, and i < j. This method is only defined if the type of the element defines an INIT method. ##### void name\_swap(name\_t array1, name\_t array2) Swap the array 'array1' and 'array2'. ##### void name\_swap\_at(name\_t array, size\_t i, size\_t j) Swap the elements 'i' and 'j' of the array 'array'. 'i' and 'j' shall reference valid elements of the array. This method is created if the INIT\_SET or INIT\_MOVE operator is provided. ##### void name\_it(name\_it\_t it, name\_t array) Set the iterator 'it' to the first element of 'array'. ##### void name\_it\_last(name\_it\_t it, name\_t array) Set the iterator 'it' to the last element of 'array'. ##### void name\_it\_end(name\_it\_t it, name\_t array) Set the iterator 'it' to the end of 'array'. ##### void name\_it\_set(name\_it\_t it1, name\_it\_t it2) Set the iterator 'it1' to 'it2'. ##### bool name\_end\_p(name\_it\_t it) Return true if the iterator doesn't reference a valid element anymore. ##### bool name\_last\_p(name\_it\_t it) Return true if the iterator references the last element of the array, or doesn't reference a valid element. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it2) Return true if both iterators reference the same element. ##### void name\_next(name\_it\_t it) Move the iterator 'it' to the next element of the array. ##### void name\_previous(name\_it\_t it) Move the iterator 'it' to the previous element of the array. ##### type *name\_ref(name\_it\_t it) Return a pointer to the element pointed by the iterator. This pointer remains valid until the array is modified by another method. ##### const type *name\_cref(const name\_it\_t it) Return a constant pointer to the element pointed by the iterator. This pointer remains valid until the array is modified by another method. ##### void name\_special\_sort(name\_t array) Sort the array 'array'. This method is defined if the type of the element defines CMP method. This method uses the qsort function of the C library. ##### void name\_special\_stable\_sort(name\_t array) Sort the array 'array' using a stable sort. This method is defined if the type of the element defines CMP and SWAP and SET methods. This method provides an ad-hoc implementation of the stable sort. In practice, it is faster than the \_sort method for small types and fast comparisons. ##### void name\_get\_str(string\_t str, const name\_t array, bool append) Generate a formatted string representation of the array 'array' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t array, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of an array and set 'array' to this representation. This method is only defined if the type of the element defines both PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t array) Generate a formatted string representation of the array 'array' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t array, FILE *file) Read from the file 'file' a formatted string representation of an array and set 'array' to this representation. This method is only defined if the type of the element defines both IN\_STR & INIT methods itself. ##### bool name\_equal\_p(const name\_t array1, const name\_t array2) Return true if both arrays 'array1' and 'array2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### size\_t name\_hash(const name\_t array) Return an hash value of 'array'. This method is only defined if the type of the element defines a HASH method itself. ##### void name\_splice(name\_t array1, name\_t array2) Merge the elements of 'array2' in 'array1' at its end. Afterwards, 'array2' is empty. ### M-DEQUE This header is for creating [double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue) (or deque). A deque is an abstract data type that generalizes a queue, for that elements can be added to or removed from either the front (head) or back (tail) #### DEQUE\_DEF(name, type, [, oplist]) #### DEQUE\_DEF\_AS(name, name\_t, name\_it\_t, type, [, oplist]) DEQUE\_DEF defines the deque 'name##\_t' that contains the objects of type 'type' and its associated methods as "static inline" functions. 'name' shall be a C identifier that will be used to identify the deque. It will be used to create all the types and functions to handle the container. It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. Example: ```C #include #include #include "m-deque.h" DEQUE_DEF(deque_mpz, mpz_t, \ (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear))) int main(void) { deque_mpz_t a; deque_mpz_init(a); mpz_t x; mpz_init_set_ui(x, 16); deque_mpz_push_back (a, x); mpz_set_ui(x, 45); deque_mpz_push_front (a, x); deque_mpz_pop_back(&x, a); mpz_set_ui(x, 5); deque_mpz_push_back (a, x); mpz_clear(x); printf ("First element is: "); mpz_out_str(stdout, 0, *deque_mpz_front(a)); printf ("\n"); printf ("Last element is: "); mpz_out_str(stdout, 0, *deque_mpz_back(a)); printf ("\n"); deque_mpz_clear(a); } ``` DEQUE\_DEF\_AS is the same as DEQUE\_DEF except the name of the types name\_t, name\_it\_t are provided. #### DEQUE\_OPLIST(name [, oplist]) Return the oplist of the deque defined by calling DEQUE\_DEF with name & oplist. #### Created methods In the following methods, name stands for the name given to the macro that is used to identify the type. The following types are automatically defined by the previous macro: #### name\_t Type of the deque of 'type'. #### name\_it\_t Type of an iterator over this deque. The following methods are automatically and properly created by the previous macro. ##### void name\_init(name\_t deque) Initialize the deque 'deque' (aka constructor) to an empty deque. ##### void name\_init\_set(name\_t deque, const name\_t ref) Initialize the deque 'deque' (aka constructor) and set it to the value of 'ref'. ##### void name\_set(name\_t deque, const name\_t ref) Set the deque 'deque' to the value of 'ref'. ##### void name\_init\_move(name\_t deque, name\_t ref) Initialize the deque 'deque' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared and can no longer be used. ##### void name\_move(name\_t deque, name\_t ref) Set the deque 'deque' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared and can no longer be used. ##### void name\_clear(name\_t deque) Clear the deque 'deque (aka destructor). The deque can't be used anymore, except with a constructor. ##### void name\_reset(name\_t deque) Reset the deque (the deque becomes empty but remains initialized but is empty). ##### type *name\_back(const name\_t deque) Return a pointer to the data stored in the back of the deque. ##### void name\_push\_back(name\_t deque, type value) Push a new element within the deque 'deque' with the value 'value' at the back of the deque. ##### type *name\_push\_back\_raw(name\_t deque) Push at the back a new element within the deque 'deque' without initializing it and returns a pointer to the **non-initialized** data. The first thing to do after calling this function is to initialize the data using the proper constructor. This enables to use a more specialized constructor than the generic one. Return a pointer to the **non-initialized** data. ##### type *name\_push\_back\_new(name\_t deque) Push at the back a new element within the deque 'deque' and initialize it with the default constructor of the type. Return a pointer to the initialized object. ##### void name\_pop\_back(type *data, name\_t deque) Pop a element from the deque 'deque' and set *data to this value. If data pointer is NULL, then the popped value is discarded. ##### type *name\_front(const name\_t deque) Return a pointer to the data stored in the front of the deque. ##### void name\_push\_front(name\_t deque, type value) Push at the front a new element within the deque 'deque' with the value 'value'. ##### type *name\_push\_front\_raw(name\_t deque) Push at the front a new element within the deque 'deque' without initializing it and returns a pointer to the **non-initialized** data. The first thing to do after calling this function is to initialize the data using the proper constructor. This enables to use a more specialized constructor than the generic one. Return a pointer to the **non-initialized** data. ##### type *name\_push\_front\_new(name\_t deque) Push at the front a new element within the deque 'deque' and initialize it with the default constructor of the type. Return a pointer to the initialized object. ##### void name\_pop\_front(type *data, name\_t deque) Pop a element from the deque 'deque' and set *data to this value. If data pointer is NULL, then the pop-ed value is discarded. ##### bool name\_empty\_p(const name\_t deque) Return true if the deque is empty, false otherwise. ##### void name\_swap(name\_t deque1, name\_t deque2) Swap the deque 'deque1' and 'deque2'. ##### void name\_it(name\_it\_t it, name\_t deque) Set the iterator 'it' to the first element of 'deque' (aka the front). There is no destructor associated to this initialization. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the iterator 'ref'. There is no destructor associated to this initialization. ##### bool name\_end\_p(const name\_it\_t it) Return true if the iterator doesn't reference a valid element anymore. ##### bool name\_last\_p(const name\_it\_t it) Return true if the iterator references the last element or if the iterator doesn't reference a valid element anymore. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it2) Return true if the iterator it1 references the same element than it2. ##### void name\_next(name\_it\_t it) Move the iterator 'it' to the next element of the deque, ie. from the front element to the back element. ##### void name\_previous(name\_it\_t it) Move the iterator 'it' to the previous element of the deque, ie. from the back element to the front element. ##### type *name\_ref(name\_it\_t it) Return a pointer to the element pointed by the iterator. This pointer remains valid until the deque is modified by another method. ##### const type *name\_cref(const name\_it\_t it) Return a constant pointer to the element pointed by the iterator. This pointer remains valid until the deque is modified by another method. ##### void name\_remove(name\_t deque, name\_it\_t it) Remove the element pointed by 'it' in the deque 'deque'. Afterwards 'it' references the next element or the end of the deque. 'it' shall reference a valid element. Removing an element may unbalance the deque, which breaks the promise of algorithm complexity for the _get method. ##### type *name\_get(const name\_t deque, size\_t i) Return a pointer to the element i-th of the deque (from 0). It is assumed than i is within the size of the deque. The algorithm complexity is in O(ln(n)) ##### const type *name\_cget(const name\_t deque, size\_t i) Return a constant pointer to the element i-th of the deque (from 0). It is assumed than i is within the size of the deque. The algorithm complexity is in O(ln(n)) ##### size\_t name\_size(const name\_t deque) Return the number elements of the deque (aka size). Return 0 if there no element. ##### void name\_get\_str(string\_t str, const name\_t deque, bool append) Generate a formatted string representation of the deque 'deque' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t deque, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a deque and set 'deque' to this representation. This method is only defined if the type of the element defines PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t deque) Generate a formatted string representation of the deque 'deque' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t deque, FILE *file) Read from the file 'file' a formatted string representation of a deque and set 'deque' to this representation. This method is only defined if the type of the element defines a IN\_STR method itself. ##### bool name\_equal\_p(const name\_t deque1, const name\_t deque2) Return true if both deque 'deque1' and 'deque2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### size\_t name\_hash(const name\_t deque) Return a hash value of 'deque'. This method is only defined if the type of the element defines a HASH method itself. ##### void name\_swap\_at(name\_t deque, size\_t i, size\_t j) Swap the values within the deque pointed by 'i' and by 'j'. 'i' & 'j' shall be valid index within the deque. This method is only defined if the type of the element defines a SWAP method itself. ### M-DICT A [dictionary](https://en.wikipedia.org/wiki/Associative_array) (or associative array, map, symbol table) is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears at most once in the collection. Several dictionaries are proposed. The "best" to use depends on the data type and in particular: * the size of the data, * the inner cost of copying the object, * the inner cost of computing the hash of the object, * the inner cost of comparing the objects, * the load factor. For small, fast types (integer, or floats, or pair of such types), DICT\_OA\_DEF2 may be the best to use. For medium type, DICT\_DEF2 with mempool activated may be better. For even larger object, DICT\_STOREHASH\_DEF2 may be better. #### DICT\_DEF2(name, key\_type[, key\_oplist], value\_type[, value\_oplist]) #### DICT\_DEF2\_AS(name, name\_t, name\_it\_t, name\_itref\_t, key\_type[, key\_oplist], value\_type[, value\_oplist]) DICT\_DEF2 defines the dictionary 'name##\_t' and its associated methods as "static inline" functions. 'name' shall be a C identifier that will be used to identify the container. Current implementation uses chained Hash-Table and as such, elements in the dictionary are **unordered**. It shall be done once per type and per compilation unit. It also define the iterator type name##\_it\_t and its associated methods as "static inline" functions and the iterated object type name##\_itref\_t that is a pair of key\_type and value\_type. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. The key\_oplist shall also define the additional operators (HASH and EQUAL). Example: ```C #include #include "m-string.h" #include "m-dict.h" DICT_DEF2(dict_string, string_t, unsigned) int main(void) { dict_string_t a; dict_string_init(a); string_t x; string_init_set_str(x, "This is an example"); dict_string_set_at (a, x, 1); string_set_str(x, "This is an another example"); dict_string_set_at (a, x, 2); string_set_str(x, "This is an example"); unsigned *val = dict_string_get(a, x); printf ("Value of %s is %u\n", string_get_cstr(x), *val); string_clear(x); printf ("Dictionnary is: "); dict_string_out_str(stdout, a); printf ("\n"); dict_string_clear(a); } ``` DICT\_DEF2\_AS is the same as DICT\_DEF2 except the name of the types name\_t, name\_it\_t, name\_itref\_t, are provided. #### DICT\_STOREHASH\_DEF2(name, key\_type[, key\_oplist], value\_type[, value\_oplist]) #### DICT\_STOREHASH\_DEF2\_AS(name, name\_t, name\_it\_t, name\_itref\_t, key\_type[, key\_oplist], value\_type[, value\_oplist]) DICT\_STOREHASH\_DEF2 defines the dictionary 'name##\_t' and its associated methods as "static inline" functions just like DICT\_DEF2. The only difference is that it stores (caches) the hash of each key alongside the key in the dictionary. This enables the container to avoid recomputing it in some occasions resulting in faster dictionary if the hash is costly to compute (which is usually the case for large object), or slower otherwise. DICT\_STOREHASH\_DEF2\_AS is the same as DICT\_STOREHASH\_DEF2 except the name of the types name\_t, name\_it\_t, name\_itref\_t are provided. #### DICT\_OA\_DEF2(name, key\_type[, key\_oplist], value\_type[, value\_oplist]) #### DICT\_OA\_DEF2\_AS(name, name\_t, name\_it\_t, name\_itref\_t, key\_type[, key\_oplist], value\_type[, value\_oplist]) DICT\_OA\_DEF2 defines the dictionary 'name##\_t' and its associated methods as "static inline" functions much like DICT\_DEF2. The difference is that it uses an Open Addressing Hash-Table as container. It shall be done once per type and per compilation unit. It also define the iterator type name##\_it\_t and its associated methods as "static inline" functions, and the iterated object type name##\_itref\_t that is a pair of key\_type and value\_type. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. The key\_oplist shall also define the additional operators : HASH and EQUAL and **OOR\_EQUAL** and **OOR\_SET** This implementation is in general faster for small types of keys (like integer or float) but slower for larger types. Example: ```C #include #include "m-string.h" #include "m-dict.h" // Define an Out Of Range equal function static inline bool oor_equal_p(unsigned k, unsigned char n) { return k == (unsigned)(-n-1); } // Define an Out Of Range setnction static inline void oor_set(unsigned *k, unsigned char n) { *k = (unsigned)(-n-1); } DICT_OA_DEF2(dict_unsigned, unsigned, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_2(oor_set))), long long, M_DEFAULT_OPLIST) unsigned main(void) { dict_unsigned_t a; dict_unsigned_init(a); dict_unsigned_set_at (a, 13566, 14890943049); dict_unsigned_set_at (a, 656, -2); long long *val = dict_unsigned_get(a, 458); printf ("Value of %d is %p\n", 458, val); // Not found value val = dict_unsigned_get(a, 656); printf ("Value of %d is %lld\n", 656, *val); dict_unsigned_clear(a); } ``` DICT\_OA\_DEF2\_AS is the same as DICT\_OA\_DEF2 except the name of the types name\_t, name\_it\_t, name\_itref\_t are provided. #### DICT\_OPLIST(name[, key\_oplist, value\_oplist]) Return the oplist of the dictionary defined by calling any DICT\_*\_DEF2 with name & key\_oplist & value\_oplist. #### DICT\_SET\_DEF(name, key\_type[, key\_oplist]) #### DICT\_SET\_DEF\_AS(name, name\_t, name\_it\_t, key\_type[, key\_oplist]) DICT\_SET\_DEF defines the dictionary set 'name##\_t' and its associated methods as "static inline" functions. A dictionary set is a specialized version of a dictionary with no value (only keys). It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET, CLEAR, HASH and EQUAL), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. Example: ```C #include #include "m-string.h" #include "m-dict.h" DICT_SET_DEF(set_string, double) unsigned main(void) { set_string_t a; set_string_init(a); set_string_push (a, 13566); set_string_push (a, 656); double *val = set_string_get(a, 458); printf ("Value of %d is %p\n", 458, val); // Not found value val = set_string_get(a, 656); printf ("Value of %d is %f\n", 656, *val); printf("Set is "); set_string_out_str(stdout, a); printf("\n"); set_string_clear(a); } ``` DICT\_SET\_DEF\_AS is the same as DICT\_SET\_DEF except the name of the types name\_t, name\_it\_t, are provided. #### DICT\_OASET\_DEF(name, key\_type[, key\_oplist]) #### DICT\_OASET\_DEF\_AS(name, name\_t, name\_it\_t, key\_type[, key\_oplist]) DICT\_OASET\_DEF defines the dictionary set 'name##\_t' and its associated methods as "static inline" functions. A dictionary set is a specialized version of a dictionary with no value. The difference is that it uses an Open Addressing Hash-Table as container. It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The key\_oplist shall therefore define the additional operators : HASH and EQUAL and **OOR\_EQUAL** and **OOR\_SET** This implementation is in general faster for small types of keys (like integer) but slower for larger types. DICT\_OASET\_DEF\_AS is the same as DICT\_OASET\_DEF except the name of the types name\_t, name\_it\_t, are provided. #### DICT\_SET\_OPLIST(name[, key\_oplist]) Return the oplist of the set defined by calling DICT\_SET\_DEF (or DICT\_OASET\_DEF) with name & key\_oplist. #### Created methods In the following methods, name stands for the name given to the macro that is used to identify the type. The following types/methods are automatically defined by the previous macro: ##### name\_t Type of the dictionary which either associate 'key\_type' to 'value\_type', or store element 'key\_type'. ##### name\_it\_t Type of an iterator over this dictionary. ##### name\_itref\_t Type of one item referenced in the dictionary for associative array. It is a structure composed of the key (field 'key') and the value (field 'value'). This type is created only for associative arrays (\_DEF2 suffix). ##### void name\_init(name\_t dict) Initialize the dictionary 'dict' to be empty. ##### void name\_clear(name\_t dict) Clear the dictionary 'dict'. ##### void name\_init\_set(name\_t dict, const name\_t ref) Initialize the dictionary 'dict' to be the same as 'ref'. ##### void name\_set(name\_t dict, const name\_t ref) Set the dictionary 'dict' to be the same as 'ref'. ##### void name\_init\_move(name\_t dict, name\_t ref) Initialize the dictionary 'dict' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_move(name\_t dict, name\_t ref) Set the dictionary 'dict' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_reset(name\_t dict) Reset the dictionary 'dict'. 'dict' becomes empty but remains initialized. ##### size\_t name\_size(const name\_t dict) Return the number of elements of the dictionary. ##### bool name\empty\_p(const name\_t dict) Return true if the dictionary is empty, false otherwise. ##### value\_type \*name\_get(const name\_t dict, const key\_type key) Return a pointer to the value associated to the key 'key' in dictionary 'dict' or NULL if the key is not found. This pointer remains valid until the array is modified by another method. ##### value\_type *name\_safe\_get(name\_t dict, const key\_type key) Return a pointer to the value associated to the key 'key' in dictionary 'dict' or create a new entry for the key 'key', in which case the associated 'value' is initialized with its default INIT operator. The returned pointer cannot be NULL. This method is only defined if the value type of the element defines an INIT method. This pointer remains valid until the array is modified by another method. ##### void name\_set\_at(name\_t dict, const key\_type key, const value\_type value) [for associative array] Set the value referenced by key 'key' in the dictionary 'dict' to 'value'. ##### void name\_push(name\_t dict, const key\_type key) [for dictionary set] Push the value referenced by key 'key' into the dictionary 'dict'. ##### void name\_erase(name\_t dict, const key\_type key) Remove the element referenced by key 'key' in the dictionary 'dict'. Do nothing if 'key' is no present in the dictionary. ##### void name\_it(name\_it\_t it, name\_t dict) Set the iterator 'it' to the first element of 'dict'. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the same element than 'ref'. ##### bool name\_end\_p(const name\_it\_t it) Return true if 'it' references no longer a valid element. ##### bool name\_last\_p(const name\_it\_t it) Return true if 'it' references the last element or is no longer valid. ##### void name\_next(name\_it\_t it) Update the iterator 'it' to the next element. ##### name\_itref\_t *name\_ref(name\_it\_t it) [for associative array] ##### key\_type *name\_ref(name\_it\_t it) [for dictionary set] Return a pointer to the pair composed by the key ('key' field) and its value ('value' field) if it is not a set, to the key type if it is a set. 'key' element shall not be modified. This pointer remains valid until the dictionary is modified by another method. ##### const name\_itref\_t *name\_cref(name\_it\_t it) [for associative array] ##### const key\_type *name\_cref(name\_it\_t it) [for dictionary set] Return a constant pointer to the pair composed by the key ('key' field) and its value ('value' field) if it is not a set, to the key type if it is a set. This pointer remains valid until the dictionary is modified by another method. ##### void name\_get\_str(string\_t str, const name\_t dict, bool append) Generate a formatted string representation of the dict 'dict' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t dict, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a dict and set 'dict' to this representation. This method is only defined if all types of the element defines PARSE\_STR methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t dict) Generate a formatted string representation of the dict 'dict' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t dict, FILE *file) Read from the file 'file' a formatted string representation of a dict and set 'dict' to this representation. This method is only defined if the type of the element defines a IN\_STR method itself. ##### bool name\_equal\_p(const name\_t dict1, const name\_t dict2) Return true if both dict 'dict1' and 'dict2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### void name\_splice(name\_t dict1, name\_t dict2) Move all items from 'dict2' into 'dict1'. If there is the same key between 'dict2' into 'dict1', then their values are added (as per the ADD method of the value type). Afterward 'dict2' is reseted. This method is only defined if the value type defines an ADD method. ### M-TUPLE A [tuple](https://en.wikipedia.org/wiki/Tuple) is a finite ordered list of elements of different types. #### TUPLE\_DEF2(name, (element1, type1[, oplist1]) [, ...]) #### TUPLE\_DEF2\_AS(name, name\_t, (element1, type1[, oplist1]) [, ...]) TUPLE\_DEF2 defines the tuple 'name##\_t' and its associated methods as "static inline" functions. Each parameter of the macro is expected to be an element of the tuple. Each element is defined by three parameters within parenthesis: the element name, the element type and the element oplist. 'name' and 'element' shall be a C identifier that will be used to identify the container. This is more or less a C structure. The main added value compared to using a C struct is that it generates also all the basic methods to handle it. It shall be done once per type and per compilation unit. The object oplist is expected to have at least the following operators (INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. Example: ```C #include #include #include "m-string.h" #include "m-tuple.h" #define MPZ_OUT_STR(stream, x) mpz_out_str(stream, 0, x) TUPLE_DEF2(pair, (key_field, string_t, STRING_OPLIST), (value_field, mpz_t, M_OPEXTEND(M_CLASSIC_OPLIST(mpz), OUT_STR(MPZ_OUT_STR)))) int main(void) { pair_t p1; pair_init (p1); string_set_str(p1->key_field, "HELLO"); mpz_set_str(p1->value_field, "1742", 10); printf("The pair is "); pair_out_str(stdout, p1); printf("\n"); pair_clear(p1); } ``` TUPLE\_DEF2\_AS is the same as TUPLE\_DEF2 except the name of the type name\_t is provided. #### TUPLE\_OPLIST(name, oplist1[, ...] ) Return the oplist of the tuple defined by calling TUPLE\_DEF2 with the given name & the Oplist. #### Created methods In the following methods, name stands for the name given to the macro that is used to identify the type. The following types are automatically defined by the previous macro: #### name\_t Type of the defined tuple. The following methods are automatically and properly created by the previous macros: ##### void name\_init(name\_t tuple) Initialize the tuple 'tuple' (aka constructor) to an empty tuple. This method is defined if all methods define an INIT method. ##### void name\_init\_set(name\_t tuple, const name\_t ref) Initialize the tuple 'tuple' (aka constructor) and set it to the value of 'ref'. ##### void name\_init\_emplace(name\_t tuple, const type1 element1[, ...]) Initialize the tuple 'tuple' (aka constructor) and set it to the value of the constructed tuple ('element1'[, ...]). ##### void name\_set(name\_t tuple, const name\_t ref) Set the tuple 'tuple' to the value of 'ref'. ##### void name\_emplace(name\_t tuple, const type1 element1[, ...]) Set the tuple 'tuple' to the value of the tuple constructed with ('element1'[,...]). ##### void name\_init\_move(name\_t tuple, name\_t ref) Initialize the tuple 'tuple' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared. This method is created only if all Oplist of the tuple define INIT\_MOVE method. ##### void name\_move(name\_t tuple, name\_t ref) Set the tuple 'tuple' by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared. This method is created only if all Oplist of the tuple define MOVE method. ##### void name\_clear(name\_t tuple) Clear the tuple 'tuple (aka destructor). ##### void name\_reset(name\_t tuple) Reset the tuple 'tuple' (applying the _reset method to all fields of the tuple). ##### const type1 *name\_cget\_at\_element1(const name\_t tuple) Return a constant pointer to the element 'element1' of the tuple. There is as many methods as there are elements. ##### type1 *name\_get\_at\_element1(const name\_t tuple) Return a pointer to the element 'element1' of the tuple. There is as many methods as there are elements. ##### void name\_set\_element1(name\_t tuple, type1 element1) Set the element of the tuple to 'element1' There is as many methods as there are elements. ##### int name\_cmp(const name\_t tuple1, const name\_t tuple2) Compare 'tuple1' to 'tuple2' using lexicographic order of the fields defining the CMP method. This method is created only if at least one Oplist of the tuple define CMP method. You can disable the use of a field for the comparaison of the tuple by disabling the CMP operator of such field ( with CMP(0) in its oplist ) ##### int name\_cmp\_order(const name\_t tuple1, const name\_t tuple2, const int order[]) Compare 'tuple1' to 'tuple2' using the given order. 'order' is a null terminated array of int that defines the order of comparison: an order of {1,4,2,0} indicates to compare first the first field, if it is equal, to compare the fourth and so on. The third field is not compared. A negative value indicates to revert the comparison. This method is created only if all Oplist of the tuple define CMP method. ##### int name\_cmp\_element1(const name\_t tuple1, const name\_t tuple2) Compare 'tuple1' to 'tuple2' using only the element element1 as reference. This method is created only if the oplist of element1 defines the CMP method. ##### size\_t name\_hash(const name\_t tuple) Return a hash associated to the tuple using all fields defining the HASH method. This method is created only if at least one Oplist of the tuple define HASH method. You can disable the use of a field for the hash computation of the tuple by disabling the HASH operator of such field ( with HASH(0) in its oplist ), in which case it is coherent to also disable the EQUAL operator too. ##### int name\_equal\_p(const name\_t tuple1, const name\_t tuple2) Return true if 'tuple1' and 'tuple2' are identical for all fields defining the EQUAL method. This method is created only if at least one Oplist of the tuple define EQUAL method. You can disable the use of a field for the equality of the tuple by disabling the EQUAL operator of such field ( with EQUAL(0) in its oplist ) ##### void name\_get\_str(string\_t str, const name\_t tuple, bool append) Generate a formatted string representation of the tuple 'tuple' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if all Oplist define a GET\_STR method. ##### bool name\_parse\_str(name\_t tuple, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a tuple and set 'tuple' to this representation. This method is only defined if all types of the element defines PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t tuple) Generate a formatted string representation of the tuple 'tuple' and outputs it into the FILE 'file'. This method is only defined if all Oplist define a OUT\_STR method. ##### void name\_in\_str(name\_t tuple, FILE *file) Read from the file 'file' a formatted string representation of a tuple and set 'tuple' to this representation. This method is only defined if all Oplist define a IN\_STR method. ### M-VARIANT A [variant](https://en.wikipedia.org/wiki/Variant_type) is a finite exclusive list of elements of different types : the variant can be only equal to one element at a time. #### VARIANT\_DEF2(name, (element1, type1[, oplist1]) [, ...]) #### VARIANT\_DEF2\_AS(name, name\_t, (element1, type1[, oplist1]) [, ...]) VARIANT\_DEF2 defines the variant 'name##\_t' and its associated methods as "static inline" functions. Each parameter of the macro is expected to be an element of the variant. Each element is defined by three parameters within parenthesis: the element name, the element type and the element oplist. 'name' and 'element' shall be a C identifier that will be used to identify the container. This is like a C union. The main added value compared to using a union is that it generates all the basic methods to handle it and it dynamically identifies which element is stored within. It shall be done once per type and per compilation unit. The object oplist is expected to have at least the following operators (INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. Example: ```C #include #include "m-string.h" #include "m-variant.h" VARIANT_DEF2(item, (name, string_t), (age, long)) int main(void) { item_t p1; item_init (p1); item_set_name(p1, STRING_CTE("HELLO")); printf("The variant is "); item_out_str(stdout, p1); printf("\n"); item_set_age(p1, 43); printf("The variant is now "); item_out_str(stdout, p1); printf("\n"); item_clear(p1); } ``` VARIANT\_DEF2\_AS is the same as VARIANT\_DEF2 except the name of the type name\_t is provided. #### VARIANT\_OPLIST(name, oplist1[, ...] ) Return the oplist of the variant defined by calling VARIANT\_DEF2 with the given name & the Oplist. #### Created methods In the following methods, name stands for the name given to the macro that is used to identify the type. The following types / methods are automatically defined by the previous macro: #### name\_t Type of the defined variant. ##### void name\_init(name\_t variant) Initialize the variant 'variant' (aka constructor) to be empty. ##### void name\_init\_set(name\_t variant, const name\_t ref) Initialize the variant 'variant' (aka constructor) and set it to the value of 'ref'. ##### void name\_set(name\_t variant, const name\_t ref) Set the variant 'variant' to the value of 'ref'. ##### void name\_init\_move(name\_t variant, name\_t ref) Initialize the variant 'variant' (aka constructor) by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared. This method is created only if all Oplist of the variant define INIT\_MOVE method. ##### void name\_move(name\_t variant, name\_t ref) ##### void name\_move\_elementN(name\_t variant, typeN ref) Set the variant 'variant' by stealing as many resources from 'ref' as possible. After-wise 'ref' is cleared. This method is created only if all Oplist of the variant define MOVE method. ##### void name\_clear(name\_t variant) Clear the variant 'variant (aka destructor). ##### void name\_reset(name\_t variant) Reset the variant 'variant and make it empty. ##### void name\_init\_elementN(name\_t variant) Initialize the variant 'variant' to the type of 'element1' This method is defined if all methods define an INIT method. ##### void name\_init\_set\_elementN(name\_t variant, const typeN elementN) Initialize and set the variant 'variant' to the type and value of 'elementN'. ##### void name\_set\_elementN(name\_t variant, const typeN elementN) Set the variant 'variant' to the type and value of 'elementN'. ##### const typeN * name\_cget\_at\_elementN(name\_t variant) Return a pointer to the 'variant' viewed as of type 'typeN'. If the variant isn't an object of such type, it returns NULL. ##### typeN * name\_get\_at\_elementN(name\_t variant) Return a pointer to the 'variant' viewed as of type 'typeN'. If the variant isn't an object of such type, it returns NULL. ##### bool name\_empty\_p(const name\_t variant) Return true if the variant is empty, false otherwise. ##### bool name\_elementN\_p(const name\_t variant) Return true if the variant is of the type of 'elementN'. ##### size\_t name\_hash(const name\_t variant) Return a hash associated to the variant. All types associated to the variant shall have a hash function for this function to be defined. ##### bool name\_equal\_p(const name\_t variant1, const name\_t variant2) Return true if both objects are equal, false otherwise. All types associated to the variant shall have a equal\_p function for this function to be defined. ##### void name\_swap(name\_t variant1, name\_t variant2) Swap both objects. ##### void name\_get\_str(string\_t str, name\_t variant, bool append) Convert the variant into a formatted string, appending it into 'str' or not. All types associated to the variant shall have a GET\_STR method for this function to be defined. ##### bool name\_parse\_str(name\_t variant, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a variant and set 'variant' to this representation. This method is only defined if all types of the element defines PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, name\_t variant) Convert the variant into a formatted string and send it to the stream 'file'. All types associated to the variant shall have a out\_str function for this function to be defined. ##### void name\_in\_str(name\_t variant, FILE *file) Read a formatted string representation of the variant from the stream 'file' and update the object variant with it. All types associated to the variant shall have a in\_str function for this function to be defined. This method is defined if all methods define an INIT method. ### M-RBTREE A binary tree is a tree data structure in which each node has at most two children, which are referred to as the left child and the right child. In this kind of tree, all elements of the tree are totally ordered. The current implementation is [RED-BLACK TREE](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree). It has not to be confused with a [B-TREE](https://en.wikipedia.org/wiki/B-tree). #### RBTREE\_DEF(name, type[, oplist]) #### RBTREE\_DEF\_AS(name, name\_t, name\_it\_t, type[, oplist]) RBTREE\_DEF defines the binary ordered tree 'name##\_t' and its associated methods as "static inline" functions. 'name' shall be a C identifier that will be used to identify the container. The CMP operator is used to perform the total ordering of the elements. It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET, CLEAR and CMP), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. Some methods may return a modifiable pointer to the found element (for example, _get). In this case, the user shall not modify the key ordre of the element, as there is no reordering of the tree in this case. Example: RBTREE_DEF(rbtree_uint, unsigned int) void f(unsigned int num) { rbtree_uint_t tree; rbtree_uint_init(tree); for(unsigned int i = 0; i < num; i++) rbtree_uint_push(tree, i); rbtree_uint_clear(tree); } RBTREE\_DEF\_AS is the same as RBTREE\_DEF2 except the name of the types name\_t, name\_it\_t are provided. #### RBTREE\_OPLIST(name [, oplist]) Return the oplist of the Red-Black defined by calling RBTREE\_DEF with name & oplist. If there is no given oplist, the default oplist for standard C type is used. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### name\_t Type of the Red Black Tree of 'type'. ##### name\_it\_t Type of an iterator over this Red Black Tree. ##### void name\_init(name\_t rbtree) Initialize the Red Black Tree 'rbtree' to be empty. ##### void name\_clear(name\_t rbtree) Clear the Red Black Tree 'rbtree'. ##### void name\_init\_set(name\_t rbtree, const name\_t ref) Initialize the Red Black Tree 'rbtree' to be the same as 'ref'. ##### void name\_set(name\_t rbtree, const name\_t ref) Set the Red Black Tree 'rbtree' to be the same as 'ref'. ##### void name\_init\_move(name\_t rbtree, name\_t ref) Initialize the Red Black Tree 'rbtree' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_move(name\_t rbtree, name\_t ref) Set the Red Black Tree 'rbtree' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_reset(name\_t rbtree) Reset the Red Black Tree 'rbtree' (It remains initialized but empty). ##### size\_t name\_size(const name\_t rbtree) Return the number of elements of the Red Black Tree. ##### void name\_push(name\_t rbtree, const type data) Push 'data' into the Red Black Tree 'rbtree' at its ordered place while keeping the tree balanced. ##### void name\_pop(type *dest, name\_t rbtree, const type data) Pop 'data' from the Red Black Tree 'rbtree' and save the popped value into 'dest' if the pointer is not null while keeping the tree balanced. Do nothing if 'data' is no present in the Red Black Tree. ##### type * name\_min(const name\_t rbtree) ##### const type * name\_cmin(const name\_t rbtree) Return a pointer to the minimum element of the tree or NULL if there is no element. ##### type * name\_max(const name\_t rbtree) ##### const type * name\_cmax(const name\_t rbtree) Return a pointer to the maximum element of the tree or NULL if there is no element. ##### type * name\_get(const name\_t rbtree, const type *data) ##### const type * name\_cget(const name\_t rbtree, const type *data) Return a pointer to the element of the tree 'rbtree' that is equal to 'data', or NULL if there is no match. ##### void name\_swap(name\_t rbtree1, name\_t rbtree2) Swap both trees. ##### bool name\_empty\_p(const name\_t rbtree) Return true if the tree is empty, false otherwise. ##### void name\_it(name\_it\_t it, name\_t rbtree) Set the iterator 'it' to the first element of 'rbtree'. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the same element than 'ref'. ##### void name\_it\_last(name\_it\_t it, name\_t rbtree) Set the iterator 'it' to the last element of 'rbtree'. ##### void name\_it\_end(name\_it\_t it, name\_t rbtree) Set the iterator 'it' to no element of 'rbtree'. ##### void name\_it\_from(name\_it\_t it, const name\_t rbtree, const type data) Set the iterator 'it' to the lowest element of the tree 'rbtree' greater or equal than 'data' or an iterator to no element is there is none. ##### bool name\_end\_p(const name\_it\_t it) Return true if 'it' references no longer a valid element. ##### bool name\_last\_p(const name\_it\_t it) Return true if 'it' references the last element or is no longer valid. ##### bool name\_it\_until\_p(const name\_it\_t it, const type data) Return true if 'it' references an element that is greater or equal than 'data' or if it references no longer a valid element, false otherwise. ##### bool name\_it\_while\_p(const name\_it\_t it, const type data) Return true if 'it' references an element that is lower or equal than 'data'. Otherwise (or if it references no longer a valid element) it returns false. ##### void name\_it\_remove(name\_t rbtree, name\_it\_t it) Remove the element pointed by 'it' from the tree 'rbtree' and update 'it' to point to the next element. All other iterators to the tree became invalid. ##### void name\_next(name\_it\_t it) Update the iterator 'it' to the next element. ##### void name\_previous(name\_it\_t it) Update the iterator 'it' to the previous element. ##### type *name\_ref(name\_it\_t it) ##### const type *name\_ref(name\_it\_t it) Return a pointer to the element pointer by the iterator 'it'. This pointer remains valid until the Red Black Tree is modified by another method. ##### void name\_get\_str(string\_t str, const name\_t rbtree, bool append) Generate a formatted string representation of the rbtree 'rbtree' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t tree, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a RBTREE and set 'tree' to this representation. This method is only defined if all types of the element defines PARSE\_STR & INIT methods itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t rbtree) Generate a formatted string representation of the rbtree 'rbtree' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t rbtree, FILE *file) Read from the file 'file' a formatted string representation of a rbtree and set 'rbtree' to this representation. This method is only defined if the type of the element defines a IN\_STR method itself. ##### bool name\_equal\_p(const name\_t rbtree1, const name\_t rbtree2) Return true if both rbtree 'rbtree1' and 'rbtree2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### size\_t name\_hash\_p(const name\_t rbtree) Return the hash of the tree. This method is only defined if the type of the element defines a HASH method itself. ### M-BPTREE A [B+TREE](https://en.wikipedia.org/wiki/B%2B_tree) is a variant of [BTREE](https://en.wikipedia.org/wiki/B-tree) that itself is a generalization of [Binary Tree](https://en.wikipedia.org/wiki/Binary_tree). A B+TREE is an N-ary tree with a variable but often large number of children per node. It is mostly used for handling slow media by file system and database. It provides a fully sorted container enabling fast access to individual item or range of items, and as such is concurrent to Red-Black Tree. On modern architecture, a B+TREE is typically faster than a red-black tree due to being more cache friendly (The RAM itself can be considered as a slow media nowadays!) When defining a B+TREE it is necessary to give the type of the item within, but also the maximum number of child per node (N). The best maximum number of child per node depends on the type itself (its size, its compare cost) and the cache of the processor. #### BPTREE\_DEF2(name, N, key\_type, key\_oplist, value\_type, value\_oplist) Define the B+TREE tree of rank N 'name##\_t' and its associated methods as "static inline" functions. This B+TREE will be created as an associative array of the key\_type to the value\_type. The CMP operator is used to perform the total ordering of the key elements. N is the number of items per node and shall be greater or equal than 2. It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET, CLEAR and CMP), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. Example: BPTREE_DEF2(tree_uint, 4, unsigned int, (), float, ()) void f(unsigned int num) { tree_uint_t tree; tree_uint_init(tree); for(unsigned int i = 0; i < num; i++) tree_uint_set_at(tree, i, (float) i); tree_uint_clear(tree); } #### BPTREE\_DEF2\_AS(name, name\_t, name\_it\_t, name\_itref\_t, N, key\_type, key\_oplist, value\_type, value\_oplist) Same as BPTREE\_DEF2 except the name of the types name\_t, name\_it\_t, name\_itref\_t are provided. #### BPTREE\_OPLIST2(name, key\_oplist, value\_oplist) Return the oplist of the BPTREE defined by calling BPTREE\_DEF2 with name, key\_oplist and value\_oplist. #### BPTREE\_DEF(name, N, key\_type[, key\_oplist]) Define the B+TREE tree of rank N 'name##\_t' and its associated methods as "static inline" functions. This B+TREE will be created as an ordered set of key\_type. The CMP operator is used to perform the total ordering of the key elements. N is the number of items per node and shall be greater or equal than 2. It shall be done once per type and per compilation unit. It also define the iterator name##\_it\_t and its associated methods as "static inline" functions. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET, CLEAR and CMP), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. In the following specification, in this case, value\_type will be defined as the same as key\_type. Example: BPTREE_DEF(tree_uint, unsigned int) void f(unsigned int num) { tree_uint_t tree; tree_uint_init(tree); for(unsigned int i = 0; i < num; i++) tree_uint_push(tree, i); tree_uint_clear(tree); } #### BPTREE\_DEF\_AS(name, name\_t, name\_it\_t, name\_itref\_t, N, key\_type, key\_oplist) Same as BPTREE\_DEF except the name of the types name\_t, name\_it\_t, name\_itref\_t are provided. #### BPTREE\_OPLIST(name[, key\_oplist]) Return the oplist of the BPTREE defined by calling BPTREE\_DEF with name, key\_oplist. If there is no given oplist, the default oplist for standard C type is used. #### BPTREE\_MULTI\_DEF2(name, N, key\_type, key\_oplist, value\_type, value\_oplist) Define the B+TREE tree of rank N 'name##\_t' and its associated methods as "static inline" functions. This B+TREE will be created as an associative array of the 'key\_type' to the 'value\_type' and allows multiple instance of the same key in the tree (aka it is a multimap: re-adding the same key in the tree will add a new instance of the key in the tree rather than update the value associated to the key). See BPTREE\_DEF2 for additional details and example. #### BPTREE\_MULTI\_DEF2\_AS(name, name\_t, name\_it\_t, name\_itref\_t, N, key\_type, key\_oplist, value\_type, value\_oplist) Same as BPTREE\_MULTI\_DEF2 except the name of the types name\_t, name\_it\_t, name\_itref\_t are provided. #### BPTREE\_MULTI\_DEF(name, N, key\_type[, key\_oplist]) Define the B+TREE tree of rank N 'name##\_t' and its associated methods as "static inline" functions. This B+TREE will be created as an ordered set of key\_type and allows multiple instance of the same key in the tree (aka it is a multiset: re-adding the same key in the tree will add a new instance of the key in the tree rather than update the key value). See BPTREE\_DEF for additional details and example. #### BPTREE\_MULTI\_DEF\_AS(name, name\_t, name\_it\_t, name\_itref\_t, N, key\_type, key\_oplist) Same as BPTREE\_MULTI\_DEF except the name of the types name\_t, name\_it\_t, name\_itref\_t are provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### name\_t Type of the B+Tree of 'type'. ##### name\_it\_t Type of an iterator over this B+Tree. ##### name\_itref\_t Type of one item referenced in the B+Tree. It is either: * a structure composed of a pointer to the key (field key\_ptr) and a pointer to the value (field value\_ptr) if the B+Tree is a map * or the basic type of the container if the B+Tree is a set. ##### void name\_init(name\_t tree) Initialize the B+Tree 'tree' and set it to empty. ##### void name\_clear(name\_t tree) Clear the B+Tree 'tree'. ##### void name\_init\_set(name\_t tree, const name\_t ref) Initialize the B+Tree 'tree' to be the same as 'ref'. ##### void name\_set(name\_t tree, const name\_t ref) Set the B+Tree 'tree' to be the same as 'ref'. ##### void name\_init\_move(name\_t tree, name\_t ref) Initialize the B+Tree 'tree' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_move(name\_t tree, name\_t ref) Set the B+Tree 'tree' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_reset(name\_t tree) Reset the B+Tree 'tree' (It remains initialized but empty). ##### size\_t name\_size(const name\_t tree) Return the number of elements of the B+Tree. ##### void name\_push(name\_t tree, const key\_type data) Push 'data' into the B+Tree 'tree' at the right order while keeping the tree balanced. This function is defined only if the tree is not defined as an associative array. ##### void name\_set\_at(name\_t tree, const key\_type data, const value\_type val) Associate the value 'val' to the key 'data' in the B+Tree 'tree' while keeping the tree balanced. This function is defined only if the tree is defined as an associative array. ##### void name\_pop(value\_type *dest, name\_t tree, const key\_type data) Pop 'data' from the B+Tree 'tree' and save the popped value into 'dest' if the pointer is not null while keeping the tree balanced. Do nothing if 'data' is no present in the B+Tree. ##### bool name\_erase(name\_t tree, const key\_type data) Remove 'data' from the B+Tree 'tree' while keeping the tree balanced. Return true if the data is removed, false if nothing is done (data is not present). ##### value\_type * name\_min(const name\_t tree) ##### const value\_type * name\_cmin(const name\_t tree) Return a pointer to the minimum element of the tree or NULL if there is no element in the B+Tree. ##### value\_type * name\_max(const name\_t tree) ##### const value\_type * name\_cmax(const name\_t tree) Return a pointer to the maximum element of the tree or NULL if there is no element in the B+Tree. ##### value\_type * name\_get(const name\_t tree, const key\_type data) ##### const value\_type * name\_cget(const name\_t tree, const key\_type data) Return a pointer to the value of the tree 'tree' that is associated to 'data', or NULL if there is no match. ##### value\_type * name\_safe\_get(name\_t tree, const key\_type data) Return a pointer to the value of the tree 'tree' that is associated to 'data'. If the key doesn't exist yet in the tree, a new entry is created with 'key' and a value initialized with its INIT operator, then a pointer to the newly created entry is returned. ##### void name\_swap(name\_t tree1, name\_t tree2) Swap both trees. ##### bool name\_empty\_p(const name\_t tree) Return true if the tree is empty, false otherwise. ##### void name\_it(name\_it\_t it, name\_t tree) Set the iterator 'it' to the first element of 'tree'. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the same element than 'ref'. ##### void name\_it\_end(name\_it\_t it, name\_t tree) Set the iterator 'it' to no element of 'tree'. ##### void name\_it\_from(name\_it\_t it, const name\_t tree, const type data) Set the iterator 'it' to the greatest element of 'tree' lower of equal than 'data' or the first element is there is none. ##### bool name\_end\_p(const name\_it\_t it) Return true if 'it' references no longer a valid element. ##### bool name\_it\_until\_p(const name\_it\_t it, const type data) Return true if 'it' references an element that is greater or equal than 'data'. ##### bool name\_it\_while\_p(const name\_it\_t it, const type data) Return true if 'it' references an element that is lower or equal than 'data'. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it1) Return true if both iterators reference the same object. ##### void name\_next(name\_it\_t it) Update the iterator 'it' to the next element. ##### name\_itref\_t *name\_ref(name\_it\_t it) ##### const name\_itref\_t *name\_ref(name\_it\_t it) Return a pointer to the element pointer by the iterator 'it'. This pointer remains valid until the B+Tree is modified by another method. ##### void name\_get\_str(string\_t str, const name\_t tree, bool append) Generate a formatted string representation of the tree 'tree' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the type of the element defines a GET\_STR method itself. ##### bool name\_parse\_str(name\_t tree, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a tree and set 'tree' to this representation. This method is only defined if the type of the element defines a PARSE\_STR method itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void name\_out\_str(FILE *file, const name\_t tree) Generate a formatted string representation of the tree 'tree' and outputs it into the FILE 'file'. This method is only defined if the type of the element defines a OUT\_STR method itself. ##### void name\_in\_str(name\_t tree, FILE *file) Read from the file 'file' a formatted string representation of a tree and set 'tree' to this representation. This method is only defined if the type of the element defines a IN\_STR method itself. ##### bool name\_equal\_p(const name\_t tree1, const name\_t tree2) Return true if both trees 'tree1' and 'tree2' are equal. This method is only defined if the type of the element defines a EQUAL method itself. ##### size\_t name\_hash\_p(const name\_t tree) Return the hash of the tree. This method is only defined if the type of the element defines a HASH method itself. ### M-PRIOQUEUE A [priority queue](https://en.wikipedia.org/wiki/Priority_queue) is a queue where each element has a "priority" associated with it: an element with high priority is served before an element with low priority. It is currently implemented as a [heap](https://en.wikipedia.org/wiki/Heap_(data_structure)). #### PRIOQUEUE\_DEF(name, type [, oplist]) Define the priority queue 'name##\_t' and its associated methods as "static inline" functions. The queue will be composed of object of type 'type'. 'name' shall be a C identifier that will be used to identify the container. The CMP operator is used to sort the queue so that it always returns the minimum of the queue. The EQUAL operator is used to identify an item on update or remove operations. It is uncorrelated with the CMP operator from the point of view of this operator. (i.e. EQUAL() == TRUE is not equivalent to CMP() == 0 for this container) The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET, CLEAR, CMP and EQUAL), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. #### PRIOQUEUE\_DEF\_AS(name, name\_t, name\_it\_t, type [, oplist]) Same as PRIOQUEUE\_DEF except the name of the types name\_t, name\_it\_t are provided. #### PRIOQUEUE\_OPLIST(name, [, oplist]) Define the oplist of the prioqueue defined with 'name' and potentially 'oplist'. If there is no given oplist, the default oplist for standard C type is used. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### name\_t Type of the priority queue of 'type'. ##### name\_it\_t Type of an iterator over this priority queue. ##### void name\_init(name\_t queue) Initialize the priority queue 'queue' and set it to empty. ##### void name\_clear(name\_t queue) Clear the priority queue 'tree'. ##### void name\_init\_set(name\_t queue, const name\_t ref) Initialize the Priority Queue 'queue' to be the same as 'ref'. ##### void name\_set(name\_t queue, const name\_t ref) Set the Priority Queue 'queue' to be the same as 'ref'. ##### void name\_init\_move(name\_t queue, name\_t ref) Initialize the Priority Queue 'queue' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_move(name\_t queue, name\_t ref) Set the Priority Queue 'queue' by stealing as resource as possible from 'ref' and clear 'ref'. ##### void name\_reset(name\_t queue) Reset the Priority Queue 'queue' (It remains initialized but empty). ##### size\_t name\_size(const name\_t queue) Return the number of elements of the Priority Queue. ##### bool name\_empty\_p(const name\_t queue) Return true if the queue is empty, false otherwise. ##### void name\_swap(name\_t queue1, name\_t queue2) Swap both queues. ##### void name\_push(name\_t queue, const type x) Push 'x' into the Priority Queue 'queue' (somewhere in the queue). ##### const type *name\_front(name\_t queue) Return a constant pointer to the item having the minimum value of all elements in the queue 'queue'. ##### void name\_pop(type *dest, name\_t queue) Pop the minimum value from the priority Queue 'queue' and save the popped value into 'dest' if the pointer is not null. ##### bool name\_equal\_p(const name\_t queue1, const name\_t queue2) Return true if both queues are equal, false otherwise. ##### void name\_update(name\_t queue, const type\_t old\_val, const type\_t new\_val) Change the priority of the data of the priority equals to 'old\_val' (in EQUAL sense) to 'new\_val' (increase or decrease priority). This method has a complexity of O(n) (due to to linear search of the data). This method is defined only if the EQUAL method is defined. ##### void name\_erase(name\_t queue, const type\_t val) Remove the data of the priority equals to 'val' (in EQUAL sense). This method has a complexity of O(n) (due to to linear search of the data). This method is defined only if the EQUAL method is defined. ##### void name\_it(name\_it\_t it, name\_t queue) Set the iterator 'it' to the first element of 'queue'. It won't iterate from minimum to maximum but in an implementation define way that ensures that all items are accessed. ##### void name\_it\_last(name\_it\_t it, name\_t queue) Set the iterator 'it' to the last element of 'queue'. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the same element than 'ref'. ##### void name\_it\_end(name\_it\_t it, name\_t queue) Set the iterator 'it' to no element of 'queue'. ##### bool name\_end\_p(const name\_it\_t it) Return true if 'it' references no longer a valid element. ##### bool name\_last\_p(const name\_it\_t it) Return true if 'it' references the last element, or there is no longer any valid element. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it2) Return true if both iterators reference the same entries. ##### void name\_next(name\_it\_t it) Update the iterator 'it' to the next element. ##### void name\_previous(name\_it\_t it) Update the iterator 'it' to the previous element. ##### const type *name\_cref(name\_it\_t it) Return a constant pointer to the referenced item. ### M-BUFFER This header implements different kind of fixed circular buffer. A [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer) (or ring buffer) is a data structure using a single, bounded buffer as if its head was connected to its tail. #### BUFFER\_DEF(name, type, size, policy[, oplist]) Define the buffer 'name##\_t' and its associated methods as "static inline" functions. A buffer is a fixed circular queue implementing a queue (or stack) interface. It can be used to transfer message from multiple producer threads to multiple consumer threads. This is done internally using a mutex and conditional waits (if it is built with the BUFFER\_THREAD\_SAFE option -- default) 'name' shall be a C identifier that will be used to identify the container. The size parameter defined the fixed size of the queue. It can be 0. In this case, the fixed size is defined at initialization time only and the needed objects to handle the buffer are allocated at initialization time too. Otherwise the needed objects are embedded within the structure, preventing any other allocations. The size of the buffer shall be lower or equal than the maximum of the type 'int'. Multiple additional policy can be applied to the buffer by performing a logical or of the following properties: * BUFFER\_QUEUE : define a FIFO queue (default), * BUFFER\_STACK : define a stack (exclusive with BUFFER\_QUEUE), * BUFFER\_THREAD\_SAFE : define thread safe functions (default), * BUFFER\_THREAD\_UNSAFE : define thread unsafe functions (exclusive with BUFFER\_THREAD\_SAFE), * BUFFER\_PUSH\_INIT\_POP\_MOVE : change the behavior of PUSH to push a new initialized object, and POP as moving this new object into the new emplacement (this is mostly used for performance reasons or to handle properly a shared\_ptr semantic). In practice, it works as if POP performs the initialization of the object. * BUFFER\_PUSH\_OVERWRITE : PUSH overwrites the last entry if the queue is full instead of blocking, * BUFFER\_DEFERRED\_POP : do not consider the object to be fully popped from the buffer by calling the pop method until the call to pop\_deferred ; this enables to handle object that are in-progress of being consumed by the thread. This container is designed to be used for easy synchronization inter-threads (and the variable should be a global shared one). It shall be done once per type and per compilation unit. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. It supports also INIT\_MOVE if available. Example: BUFFER_DEF(buffer_uint, unsigned int, 10, BUFFER_QUEUE|BUFFER_BLOCKING) buffer_uint_t g_buff; void f(unsigned int i) { buffer_uint_init(g_buff, 10); buffer_uint_push(g_buff, i); buffer_uint_pop(&i, g_buff); buffer_uint_clear(g_buff); } #### BUFFER\_DEF\_AS(name, name\_t, type, size, policy[, oplist]) Same as BUFFER\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### void name\_init(buffer\_t buffer, size\_t size) Initialize the buffer 'buffer' for 'size' elements. The 'size' argument shall be the same as the one used to create the buffer or the one used to create the buffer was '0'. The size of the buffer shall be lower or equal than UINT\_MAX. This function is not thread safe. ##### void name\_clear(buffer\_t buffer) Clear the buffer and destroy all its allocation. This function is not thread safe and doesn't perform any synchronization: all threads shall have stopped using the buffer. ##### void name\_reset(buffer\_t buffer) Reset the buffer, making it empty but initialized. This function is thread safe if the buffer was built thread safe. ##### bool name\_empty\_p(const buffer\_t buffer) Return true if the buffer is empty, false otherwise. This function is thread safe if the buffer was built thread safe. ##### bool name\_full\_p(const buffer\_t buffer) Return true if the buffer is full, false otherwise. This function is thread safe if the buffer was built thread safe. ##### size\_t name\_size(const buffer\_t buffer) Return the number of elements in the buffer that can be en-queued. This function is thread safe if the buffer was built thread safe. ##### size\_t name\_capacity(const buffer\_t buffer) Return the capacity of the buffer. ##### size\_t name\_overwrite(const buffer\_t buffer) If the buffer is built with the BUFFER\_PUSH\_OVERWRITE option, this function returns the number of elements that have been overwritten and thus discarded. If the buffer was not built with the BUFFER\_PUSH\_OVERWRITE option, it returns 0. ##### bool name\_push\_blocking(buffer\_t buffer, const type data, bool blocking) Push the object 'data' in the buffer 'buffer', waiting for an empty room if 'blocking' is true. Returns true if the data was pushed, false otherwise. Always return true if the buffer is blocking. This function is thread safe if the buffer was built thread safe. ##### bool name\_pop\_blocking(type *data, buffer\_t buffer, bool blocking) Pop from the buffer 'buffer' into the object '*data', waiting for a data if 'blocking' is true. If the buffer is built with the BUFFER\_PUSH\_INIT\_POP\_MOVE option, the object pointed by 'data' shall be ***uninitialized*** as the pop function will perform a quick initialization of the object (using an INIT\_MOVE operator) , otherwise it shall be an initialized object (the pop function will perform a SET operator). If the buffer is built with the BUFFER\_DEFERRED\_POP option, the object is still considered being present in the queue until a call to name\_pop\_release. Returns true if a data was popped, false otherwise. Always return true if the buffer is blocking. This function is thread safe if the buffer was built thread safe. ##### bool name\_push(buffer\_t buffer, const type data) Same as name\_push\_blocking with blocking equals to true. ##### bool name\_pop(type *data, buffer\_t buffer) Same as name\_pop\_blocking with blocking equals to true. ##### bool name\_pop\_release(buffer\_t buffer) If the buffer is built with the BUFFER\_DEFERRED\_POP option, the object being popped is considered fully release (freeing a space in the queue). Otherwise it does nothing. #### QUEUE\_MPMC\_DEF(name, type, policy[, oplist]) Define the MPMC queue 'name##\_t' and its associated methods as "static inline" functions. A MPMC queue is a fixed circular queue implementing a queue (or stack) interface. It can be used to transfer message from Multiple Producer threads to Multiple Consumer threads. This queue is not strictly lock free but [has](https://stackoverflow.com/questions/45907210/lock-free-progress-guarantees) a lot of the properties of such algorithms. The size is specified only at run-time and shall be a power of 2. 'name' shall be a C identifier that will be used to identify the container. An additional policy can be applied to the buffer by performing a logical or of the following properties: * BUFFER\_QUEUE : define a FIFO queue (default), * BUFFER\_PUSH\_INIT\_POP\_MOVE : change the behavior of PUSH to push a new initialized object, and POP as moving this new object into the new emplacement (this is mostly used for performance reasons or to handle properly a shared\_ptr semantic). In practice, it works as if POP performs the initialization of the object. This container is designed to be used for easy synchronization inter-threads in a context of very fast communication (the variable should be a global shared one). There should not have more threads using this queue than they are available hardware cores due to the only partial protection on Context-switch Immunity of this structure (This can happen only if you abuse **massively** the number of threads vs the number of cores). It shall be done once per type and per compilation unit. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. It supports also INIT\_MOVE if available. #### QUEUE\_MPMC\_DEF\_AS(name, name\_t, type, policy[, oplist]) Same as QUEUE\_MPMC\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type (prefix). ##### void name\_init(buffer\_t buffer, size\_t size) Initialize the buffer 'buffer' with 'size' elements. The 'size' argument shall be a power of two greater than 0, and less than UINT\_MAX. This function is not thread safe. ##### void name\_clear(buffer\_t buffer) Clear the buffer and destroy all its allocation. This function is not thread safe and doesn't perform any synchronization: all threads shall have stopped using the buffer. ##### bool name\_empty\_p(const buffer\_t buffer) Return true if the buffer is empty, false otherwise. This function is thread safe. ##### bool name\_full\_p(const buffer\_t buffer) Return true if the buffer is full, false otherwise. This function is thread safe. ##### size\_t name\_size(const buffer\_t buffer) Return the number of elements in the buffer that can be en-queued. This function is thread safe but may return a size greater than the size of the queue in some race condition. ##### size\_t name\_capacity(const buffer\_t buffer) Return the capacity of the buffer. ##### bool name\_push(buffer\_t buffer, const type data) Push the object 'data' in the buffer 'buffer' if possible. Returns true if the data was pushed, false otherwise (buffer full or unlikely data race). This function is thread safe. ##### bool name\_pop(type *data, buffer\_t buffer) Pop from the buffer 'buffer' into the object '*data' if possible. If the buffer is built with the BUFFER\_PUSH\_INIT\_POP\_MOVE option, the object pointed by 'data' shall be ***uninitialized*** as the pop function will perform a quick initialization of the object (using an INIT\_MOVE operator) , otherwise it shall be an initialized object (the pop function will perform a SET operator). Returns true if a data was popped, false otherwise (buffer empty or unlikely data race). This function is thread safe. #### QUEUE\_SPSC\_DEF(name, type, policy[, oplist]) Define the SPSC queue 'name##\_t' and its associated methods as "static inline" functions. A SPSC queue is a fixed circular queue implementing a queue (or stack) interface. It can be used to transfer message from a Single Producer thread to a Single Consumer thread. This is done internally using lock-free objects. It is more specialized than QUEUE\_MPMC\_DEF and as such, is faster. The size is specified only at run-time and shall be a power of 2. 'name' shall be a C identifier that will be used to identify the container. An additional policy can be applied to the buffer by performing a logical or of the following properties: * BUFFER\_QUEUE : define a FIFO queue (default), * BUFFER\_PUSH\_INIT\_POP\_MOVE : change the behavior of PUSH to push a new initialized object, and POP as moving this new object into the new emplacement (this is mostly used for performance reasons or to handle properly a shared\_ptr semantic). In practice, it works as if POP performs the initialization of the object. This container is designed to be used for easy synchronization inter-threads in a context of very fast communication (the variable should be a global shared one). It shall be done once per type and per compilation unit. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. It supports also INIT\_MOVE if available. #### QUEUE\_SPSC\_DEF\_AS(name, name\_t, type, policy[, oplist]) Same as QUEUE\_MPMC\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type (prefix). ##### void name\_init(buffer\_t buffer, size\_t size) Initialize the buffer 'buffer' with 'size' elements. The 'size' argument shall be a power of two greater than 0, and less than UINT\_MAX. This function is not thread safe. ##### void name\_clear(buffer\_t buffer) Clear the buffer and destroy all its allocation. This function is not thread safe and doesn't perform any synchronization: all threads shall have stopped using the buffer. ##### bool name\_empty\_p(const buffer\_t buffer) Return true if the buffer is empty, false otherwise. This function is thread safe. ##### bool name\_full\_p(const buffer\_t buffer) Return true if the buffer is full, false otherwise. This function is thread safe. ##### size\_t name\_size(const buffer\_t buffer) Return the number of elements in the buffer that can be en-queued. This function is thread safe but may return a size greater than the size of the queue in some race condition. ##### size\_t name\_capacity(const buffer\_t buffer) Return the capacity of the buffer. ##### bool name\_push(buffer\_t buffer, const type data) Push the object 'data' in the buffer 'buffer' if possible. Returns true if the data was pushed, false otherwise (buffer full). This function is thread safe. ##### bool name\_push\_move(buffer\_t buffer, type *data) Push & move the object '*data' in the buffer 'buffer' if possible. Returns true if the data was pushed, false otherwise (buffer full). Afterwards '*data' is cleared (destructor) if true is returned. This function is thread safe. ##### bool name\_push\_force(buffer\_t buffer, const type data) Push the object 'data' in the buffer 'buffer' discarding the oldest data if needed. This function is thread safe. ##### bool name\_push\_bulk(buffer\_t buffer, unsigned n, const type data[]) Push as much objects from the array 'data' in the buffer 'buffer' as possible, starting from the object at index 0 to the object at index 'n-1'. Returns the number of objects pushed. This function is thread safe. ##### bool name\_pop(type *data, buffer\_t buffer) Pop from the buffer 'buffer' into the object '*data' if possible. If the buffer is built with the BUFFER\_PUSH\_INIT\_POP\_MOVE option, the object pointed by 'data' shall be ***uninitialized*** as the pop function will perform a quick initialization of the object (using an INIT\_MOVE operator) , otherwise it shall be an initialized object (the pop function will perform a SET operator). Returns true if a data was popped, false otherwise (buffer empty or unlikely data race). This function is thread safe. ##### unsigned name\_pop\_bulk(unsigned n, type tab[n], buffer\_t buffer) Pop from the buffer 'buffer' as many objects as possible to fill in 'tab' and at most 'n'. If the buffer is built with the BUFFER\_PUSH\_INIT\_POP\_MOVE option, the object pointed by 'data' shall be ***uninitialized*** as the pop function will perform a quick initialization of the object (using an INIT\_MOVE operator) , otherwise it shall be an initialized object (the pop function will perform a SET operator). It returns the number of objects popped. This function is thread safe. ### M-SNAPSHOT This header is for created snapshots. A snapshot is a mechanism enabling a reader thread and a writer thread, **working at different speeds**, to exchange messages in a fast, reliable and thread safe way (the message is always passed automatically from a thread point of view) without waiting for synchronization. The consumer thread has only access to the latest published data of the producer thread. This is implemented in a fast way as the writer directly writes the message in the buffer that will be passed to the reader (avoiding copy of the buffer) and a simple exchange of index is sufficient to handle the switch. This container is designed to be used for easy synchronization inter-threads (and the variable should be a global shared one). This is linked to [shared atomic register](https://en.wikipedia.org/wiki/Shared_register) in the literature and [snapshot](https://en.wikipedia.org/wiki/Shared_snapshot_objects) names vector of such registers where each element of the vector can be updated separately. They can be classified according to the number of producers/consumers: SPSC (Single Producer, Single Consumer), MPSC (Multiple Producer, Single Consumer), SPMC (Single Producer, Multiple Consumer), MPMC (Multiple Producer, Multiple Consumer), The provided containers by the library are designed to handle huge structure efficiently and as such deal with the memory reclamation needed to handle them. If the data you are sharing are supported by the atomic header (like bool or integer), using atomic\_load and atomic\_store is a much more efficient and simple way to do even in the case of MPMC. #### SNAPSHOT\_SPSC\_DEF(name, type[, oplist]) Define the snapshot 'name##\_t' and its associated methods as "static inline" functions. Only a single reader thread and a single writer thread are supported. It is a SPSC atomic shared register. In practice, it is implemented using a triple buffer (lock-free). It shall be done once per type and per compilation unit. Not all functions are thread safe. The object oplist is expected to have at least the following operators (INIT, INIT\_SET, SET and CLEAR), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to init, set and clear the contained object. It supports also INIT\_MOVE if available. Example: SNAPSHOT_DEF(snapshot_uint, unsigned int) snapshot_uint_t message; void f(unsigned int i) { unsigned *p = snapshot_uint_get_write_buffer(message); *p = i; snapshot_uint_write(message); } unsigned int g(void) { unsigned *p = snapshot_uint_read(message); return *p; } #### SNAPSHOT\_SPSC\_DEF\_AS(name, name\_t, type[, oplist]) Same as SNAPSHOT\_SPSC\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### void name\_init(snapshot\_t snapshot) Initialize the snapshot 'snapshot'. This function is not thread safe. ##### void name\_clear(snapshot\_t snapshot) Clear the snapshot and destroy all its allocations. This function is not thread safe. ##### void name\_init\_set(snapshot\_t snapshot, const snapshot\_t org) Initialize the snapshot 'snapshot' from the state of 'org'. This function is not thread safe. ##### void name\_set(snapshot\_t snapshot, const snapshot\_t org) Set the snapshot 'snapshot' from the state of 'org'. This function is not thread safe. ##### void name\_init\_move(snapshot\_t snapshot, snapshot\_t org) Move the contain of the snapshot 'org' to the uninitialized 'snapshot', clearing 'org' in the process. This function is not thread safe. This function is defined only if the underlying type has defined the INIT\_MOVE operator. ##### void name\_move(snapshot\_t snapshot, snapshot\_t org) Move the contain of the snapshot 'org' to the initialized 'snapshot', clearing 'org' in the process. This function is not thread safe. This function is defined only if the underlying type has defined the MOVE operator. ##### type *name\_write(snapshot\_t snap) Publish the 'in-progress' data of the snapshot 'snap so that the read thread can have access to the data. It returns the pointer to the new 'in-progress' data buffer of the snapshot (which is not yet published but will be published for the next call of name\_write). This function is thread-safe and performs atomic operation on the snapshot. ##### type *name\_read(snapshot\_t snap) Get access to the last published data of the snapshot 'snap'. It returns the pointer to the data. If a publication has been performed since the last call to name\_read a new pointer to the data is returned. Otherwise the previous pointer is returned. This function is thread-safe and performs atomic operation on the snapshot. ##### bool name\_updated\_p(snapshot\_t snap) Return true if the buffer has updated data compared to the last time it was read. This function is thread-safe and performs atomic operation on the snapshot. ##### type *name\_get\_write\_buffer(snapshot\_t snap) Return a pointer to the active 'in-progress' data of the snapshot 'snap'. It is the same as the last return from name\_write. This function is thread-safe and performs atomic operation on the snapshot. ##### type *name\_get\_read\_buffer(snapshot\_t snap) Return a pointer to the active published data of the snapshot 'snap'. It is the same as the last return from name\_read. It doesn't perform any switch of the data that has to be read. This function is thread-safe and performs atomic operation on the snapshot. TODO: Document SPMC & MPMC snapshots ### M-SHARED This header is for creating shared pointer. A [shared pointer](https://en.wikipedia.org/wiki/Smart_pointer) is a smart pointer that retains shared ownership of an object. Several shared pointers may own the same object, sharing ownership of an object. #### SHARED\_PTR\_DEF(name, type[, oplist]) Define the shared pointer 'name##\_t' and its associated methods as "static inline" functions. A shared pointer is a mechanism to keep tracks of all users of an object and performs an automatic destruction of the object whenever all users release their need on this object. The tracking of ownership is atomic and the destruction of the object is thread safe. The object oplist is expected to have at least the following operators (CLEAR to clear the object and DEL to free the allocated memory), otherwise default operators are used. If there is no given oplist, the default oplist for standard C type is used or a globally registered oplist is used. The created methods will use the operators to initialize, set and clear the contained object. It supports also the INIT\_MOVE operator of the object if available. There are designed to work with buffers with policy BUFFER\_PUSH\_INIT\_POP\_MOVE to send a shared pointer across multiple threads. Example: SHARED_PTR_DEF(shared_mpz, mpz_t, (CLEAR(mpz_clear))) void f(void) { shared_mpz_t p; mpz_t z; mpz_init(z); shared_mpz_init2 (p, z); buffer_uint_push(g_buff1, p); buffer_uint_push(g_buff2, p); shared_mpz_clear(p); } #### SHARED\_PTR\_DEF\_AS(name, name\_t, type[, oplist]) Same as SHARED\_PTR\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### void name\_init(shared\_t shared) Initialize the shared pointer 'shared' to represent NULL (no object is therefore referenced). ##### void name\_init2(shared\_t shared, type *data) Initialize the shared pointer 'shared' to reference '*data'. User code shall not use '*data' (or any pointer to it) anymore as the shared pointer gets the exclusive ownership of the object. ##### void name\_init\_new(shared\_t shared) Initialize the shared pointer 'shared' to a new object of type 'type'. The default constructor of type is used to initialize the object. ##### void name\_init\_set(shared\_t shared, const shared\_t src) Initialize the shared pointer 'shared' to the same object than the one pointed by 'src' (sharing ownership). This function is thread safe from 'src' point of view. ##### bool name\_NULL\_p(const shared\_t shared) Return true if shared doesn't reference any object. ##### void name\_clear(shared\_t shared) Clear the shared pointer: the shared pointer loses its ownership of the object and it destroys it if no longer any other shared pointers own the object. This function is thread safe. ##### void name\_reset(shared\_t shared) 'shared' loses ownership of its object and destroys it if no longer any other shared pointers own it. Then it makes the shared pointer 'shared' references no object (NULL) (it doesn't reference its object any-longer and loses its ownership of it). This function is thread safe. ##### void name\_set(shared\_t shared, const shared\_t src) 'shared' loses ownership of its object and destroy it if no longer any other shared pointers own it. Then it sets the shared pointer 'shared' to the same object than the one pointed by 'src' (sharing ownership). This function is thread safe. ##### void name\_init\_move(shared\_t shared, shared\_t src) Move the shared pointer from the initialized 'src' to 'shared'. ##### void name\_move(shared\_t shared, shared\_t src) 'shared' loses ownership of its object and destroy it if no longer any other shared pointers own it. Then it moves the shared pointer from the initialized 'src' to 'shared'. ##### void name\_swap(shared\_t shared1, shared\_t shared2) Swap the references of the objects owned by the shared pointers 'shared1' and 'shared2'. ##### bool name\_equal\_p(const shared\_t shared1, const shared\_t shared2) Return true if both shared pointers own the same object. ##### const type *name\_cref(const shared\_t shared) Return a constant pointer to the shared object owned by the shared pointer. The pointer shall be kept only until another use of shared pointer method. Keeping the pointer otherwise is undefined behavior. ##### type *name\_ref(const shared\_t shared) Return a pointer to the shared object pointed by the shared pointer. The pointer shall be kept only until another use of shared pointer method. Keeping the pointer otherwise is undefined behavior. TODO: Document shared resource ### M-I-SHARED This header is for creating intrusive shared pointer. #### ISHARED\_PTR\_INTERFACE(name, type) Extend the definition of the structure of an object of type 'type' by adding the necessary interface to handle it as a shared pointer named 'name'. It shall be put within the structure definition of the object (See example). #### ISHARED\_PTR\_STATIC\_INIT(name, type) Provide the static initialization value of an object defined with a ISHARED\_PTR\_INTERFACE extra fields. It shall be used only for global variables with the \_init\_once function. Usage (provided that the interface is used as the first element of the structure): struct mystruct variable = { ISHARED_PTR_STATIC_INIT(ishared_double, struct mystruct) }; #### ISHARED\_PTR\_STATIC\_DESIGNATED\_INIT(name, type) Provide the static initialization value of an object defined with a ISHARED\_PTR\_INTERFACE extra fields. It shall be used only for global variables with the \_init\_once function. It uses designated initializers to set the right fields. Usage: struct mystruct variable = { ISHARED_PTR_STATIC_DESIGNATED_INIT(ishared_double, struct mystruct) }; #### ISHARED\_PTR\_DEF(name, type[, oplist]) Define the associated methods to handle the shared pointer named 'name' as "static inline" functions. A shared pointer is a mechanism to keep tracks of all 'users' of an object and performs an automatic destruction of the object whenever all users release their need on this object. The destruction of the object is thread safe and occurs when the last user of the object releases it. The destruction of the object implies: * calling the CLEAR operator to clear the object, * calling the DEL operator to release the memory used by the object (if the method has not been disabled). The object oplist is expected to have the following operators (CLEAR and DEL), otherwise default operators are used. If there is no given oplist, the default operators are also used. The created methods will use the operators to init, set and clear the contained object. There are designed to work with buffers with policy BUFFER\_PUSH\_INIT\_POP\_MOVE to send a shared pointer across multiple threads. It is recommended to use the intrusive shared pointer over the standard one if possible (They are faster & cleaner). The default is to use heap allocated entities, which are allocated by NEW & freed by DEL. It can be used for statically allocated entities. However, in this case, you shall disable the operator NEW & DEL when expanding the oplist so that the CLEAR method doesn't try to free the objects like this: (NEW(0), DEL(0)) NEW & DEL operators shall be either both defined, or both disabled. Example (dynamic): typedef struct mystruct_s { ISHARED_PTR_INTERFACE(ishared_mystruct, struct mystruct_s); char *message; } mystruct_t; static inline void mystruct_init(mystruct_t *p) { p->message = NULL; } static inline void mystruct_clear(mystruct_t *p) { free(p->message); } ISHARED_PTR_DEF(ishared_mystruct, mystruct_t, (INIT(mystruct_init), CLEAR(mystruct_clear M_IPTR))) void f(void) { mystruct_t *p = ishared_mystruct_init_new(); p->message = strdup ("Hello"); buffer_mystruct_push(g_buff1, p); buffer_mystruct_push(g_buff2, p); ishared_mystruct_clear(p); } #### ISHARED\_PTR\_DEF\_AS(name, name\_t, type[, oplist]) Same as ISHARED\_PTR\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### typedef type *name\_t It will define name\_t as a pointer to shared counted object. This is a synonymous to a pointer to the object. ##### name\_t name\_init(type *object) Return a shared pointer to 'object' which owns 'object'. It initializes the private fields of 'object' handling the shared pointer, returning a pointer to the object (but initialized). As a consequence, the shared pointer part of 'object' shall not have been initialized yet. The other part of 'object' may or may not be initialized before calling this method. ##### name\_t name\_init\_set(name\_t shared) Return a new shared pointer to the same object than the one pointed by 'shared', incrementing the ownership of the object. This function is thread safe. ##### name\_t name\_init\_new(void) Allocate a new object, initialize it and return an initialized shared pointer to it. The used allocation function is the NEW operator. This function is created only if the INIT method is defined in the oplist and if the NEW method has not been disabled in the oplist. ##### name\_t name\_init\_once(type *object) Initialize the new object 'object' and return an initialized shared pointer to it. The INIT operator of 'object' is ensured to be called only once, even if multiple threads try to initialize it at the same time. Once the object is fully cleared, the initialization function may occur once again. object shall be a global variable initialized with the ISHARED\_PTR\_STATIC\_INIT macro. This function is created only if the INIT method is defined in the oplist and if the NEW method has been disabled in the oplist. ##### void name\_clear(name\_t shared) Clear the shared pointer, releasing ownership of the object and destroying the shared object if no longer any other shared pointers own it. This function is thread safe. ##### void name\_clear\_ptr(name\_t *shared) Clear the shared pointer '*shared', releasing ownership of the object and destroying the shared object if no longer any other shared pointers own it. This function is thread safe. Afterwards, '*shared' is set to NULL. ##### void name\_set(name\_t *shared1, name\_t shared2) Update the shared pointer '*shared1' to point to the same object than the shared pointer 'shared2'. Destroy the shared object pointed by '*shared1' if no longer any other shared pointers own it, set the shared pointer 'shared' to the same object than the one pointed by 'src'. This function is thread safe. ### M-I-LIST This header is for creating intrusive doubly-linked list. #### ILIST\_INTERFACE(name, type) Extend an object by adding the necessary interface to handle it within an intrusive doubly-linked list. This is the intrusive part. It shall be put within the structure of the object to link, at the top level of the structure. See example of ILIST\_DEF. #### ILIST\_DEF(name, type[, oplist]) Define the intrusive doubly-linked list and define the associated methods to handle it as "static inline" functions. 'name' shall be a C identifier that will be used to identify the list. It will be used to create all the types and functions to handle the container. It shall be done once per type and per compilation unit. An object is expected to be part of only one list of a kind in the entire program at a time. An intrusive list enables to move from an object to the next object without needing to go through the entire list, or to remove an object from a list in O(1). It may, or may not, be better than standard list. It depends on the context. The object oplist is expected to have the default operators. If there is no given oplist, the methods for a standard C type are used, or if there is a global defined oplist, it is used. The created methods will use the operators to init, set and clear the contained object. The given interface won't allocate anything to handle the objects as all allocations and initialization are let to the user. However the objects within the list can be automatically be cleared (by calling the CLEAR method to destruct the object) on list destruction. The memory allocation, performed by the called, can also be reclaimed by defining a DEL operator to free the used memory in the object oplist. If there is no DEL operator, it is up to the user to free the used memory. Example: typedef struct test_s { int n; ILIST_INTERFACE (ilist_tname, struct test_s); } test_t; ILIST_DEF(ilist_tname, test_t) void f(void) { test_t x1, x2, x3; ilist_tname_t list; x1.n = 1; x2.n = 2; x3.n = 3; ilist_tname_init(list); ilist_tname_push_back (list, &x3); ilist_tname_push_front (list, &x1); ilist_tname_push_after (&x1, &x2); int n = 1; for M_EACH(item, list, ILIST_OPLIST(ilist_tname)) { assert (n == item->n); n++; } ilist_tname_clear(list); } #### ILIST\_DEF\_AS(name, name\_t, name\_it\_t, type[, oplist]) Same as SNAPSHOT\_SPSC\_DEF except the name of the types name\_t, name\_it\_t are provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. #### name\_t Type of the list of 'type'. #### name\_it\_t Type of an iterator over this list. The following methods are automatically and properly created by the previous macro. ##### void name\_init(name\_t list) Initialize the list 'list' (aka constructor) to an empty list. ##### void name\_init\_field(type *obj) Initialize the additional fields of the object '*obj'. ##### void name\_clear(name\_t list) Clear the list 'list (aka destructor). The list can't be used anymore, except with a constructor. If the DEL operator is available in the oplist of the type, the cleared object will also be deleted. ##### void name\_reset(name\_t list) Reset the list (the list becomes empty). The list remains initialized but is empty. If the DEL operator is available in the oplist of the type, the cleared object will also be deleted. ##### type *name\_back(const name\_t list) Return a pointer to the data stored in the back of the list. ##### type *name\_front(const name\_t list) Return a pointer to the data stored in the front of the list. ##### void name\_push\_back(name\_t list, type *obj) Push the object '*obj' itself at the back of the list 'list'. ##### void name\_push\_front(name\_t list, type *obj) Push the object '*obj' itself at the front of the list 'list'. ##### void name\_push\_after(type *position, type *obj) Push the object '*obj' after the object '*position'. ##### type *name\_pop\_back(name\_t list) Pop the object from the back of the list 'list' and return a pointer to the popped object. ##### type *name\_pop\_front(name\_t list) Pop the object from the front of the list 'list' and return a pointer to the popped object. ##### bool name\_empty\_p(const name\_t list) Return true if the list is empty, false otherwise. ##### void name\_swap(name\_t list1, name\_t list2) Swap the list 'list1' and 'list2'. ##### void name\_unlink(type *obj) Remove the object '*obj' from the list. ##### type *name\_next\_obj(const name\_t list, const type *obj) Return the object that is after the object '*obj' in the list or NULL if there is no more object. ##### type *name\_previous\_obj(const name\_t list, const type *obj) Return the object that is before the object '*obj' in the list or NULL if there is no more object. ##### void name\_it(name\_it\_t it, name\_t list) Set the iterator 'it' to the back(=first) element of 'list'. There is no destructor associated to this initialization. ##### void name\_it\_set(name\_it\_t it, const name\_it\_t ref) Set the iterator 'it' to the iterator 'ref'. There is no destructor associated to this initialization. ##### void name\_it\_last(name\_it\_t it, name\_t list) Set the iterator 'it' to the last element of the list. There is no destructor associated to this initialization. ##### void name\_it\_end(name\_it\_t it, name\_t list) Set the iterator 'it' to the end of the list (i.e. not a valid element). There is no destructor associated to this initialization. ##### bool name\_end\_p(const name\_it\_t it) Return true if the iterator doesn't reference a valid element anymore. ##### bool name\_last\_p(const name\_it\_t it) Return true if the iterator references the last element or if the iterator doesn't reference a valid element anymore. ##### bool name\_it\_equal\_p(const name\_it\_t it1, const name\_it\_t it2) Return true if the iterator it1 references the same element than it2. ##### void name\_next(name\_it\_t it) Move the iterator 'it' to the next element of the list. ##### void name\_previous(name\_it\_t it) Move the iterator 'it' to the previous element of the list. ##### type *name\_ref(name\_it\_t it) Return a pointer to the element pointed by the iterator. This pointer remains valid until the list is modified by another method. ##### const type *name\_cref(const name\_it\_t it) Return a constant pointer to the element pointed by the iterator. This pointer remains valid until the list is modified by another method. ##### size\_t name\_size(const name\_t list) Return the number elements of the list (aka size). Return 0 if there no element. ##### void name\_insert(name\_t list, name\_it\_t it, type x) Insert a copy of 'x' after the position pointed by 'it' (which is an iterator of the list 'list') or if 'it' doesn't point anymore to a valid element of the list, it is added as the back (=first) element of the 'list' This service is available only if a NEW operator is available for the type. ##### void name\_remove(name\_t list, name\_it\_t it) Remove the element 'it' from the list 'list'. After wise, 'it' points to the next element of the list. ##### void name\_splice\_back(name\_t list1, name\_t list2, name\_it\_t it) Move the element pointed by 'it' (which is an iterator of 'list2') from the list 'list2' to the back position of 'list1'. After wise, 'it' points to the next element of 'list2'. ##### void name\_splice(name\_t list1, name\_t list2) Move all the element of 'list2' into 'list1", moving the last element of 'list2' after the first element of 'list1'. After-wise, 'list2' is emptied. ### M-CONCURRENT This header is for transforming a standard container (LIST, ARRAY, DICT, DEQUE, ...) into an equivalent container but compatible with concurrent access by different threads. In practice, it puts a lock to access the container. As such it is quite generic. However it is less efficient than containers specially tuned for multiple threads. There is also no iterators. #### methods #### CONCURRENT\_DEF(name, type[, oplist]) Define the concurrent container 'name' based on container 'type' of oplist 'oplist', and define the associated methods to handle it as "static inline" functions. 'name' shall be a C identifier that will be used to identify the list. It will be used to create all the types and functions to handle the container. It shall be done once per type and per compilation unit. It scans the 'oplist' of the type to create equivalent function, so it needs it (either explicitly or implicitly). Example: /* Define a stack container (STACK)*/ ARRAY_DEF(array1, int) CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1)) /* Define a queue container (FIFO) */ DEQUE_DEF(deque_uint, unsigned int) CONCURRENT_DEF(cdeque_uint, deque_uint_t, M_OPEXTEND(DEQUE_OPLIST(deque_uint, M_DEFAULT_OPLIST), PUSH(deque_uint_push_front))) extern parray1_t x1; extern cdeque_uint_t x2; void f(void) { parray1_push (x1, 17); cdeque_uint_push (x2, 17); } #### CONCURRENT\_DEF\_AS(name, name\_t, type[, oplist]) Same as CONCURRENT\_DEF except the name of the type name\_t is provided. #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### name\_t Type of the concurrent container of 'type'. ##### void name\_init(name\_t concurrent) Initialize the concurrent container. This method is only defined if the base container exports the INIT operator. ##### void name\_init\_set(name\_t concurrent, const name\_t src) Initialize the concurrent container and set it with a copy of 'src'. This method is only defined if the base container exports the INIT\_SET operator. ##### void name\_init\_move(name\_t concurrent, name\_t src) Initialize the concurrent container by stealing as much resources from 'src' as possible. Afterwards 'src' is cleared. This method is only defined if the base container exports the INIT\_MOVE operator. ##### void name\_set(name\_t concurrent, const name\_t src) Set the container with a copy of 'src'. This method is only defined if the base container exports the SET operator. ##### void name\_move(name\_t concurrent, name\_t src) Set the container with the value of 'src' by stealing as much resources from 'src' as possible. Afterwards 'src' is cleared. This method is only defined if the base container exports the MOVE operator. ##### void name\_reset(name\_t concurrent) Reset the concurrent container. Afterwards the container is empty, but remains initialized. This method is only defined if the base container exports the RESET operator. ##### void name\_clear(name\_t concurrent) Clear the concurrent container and destroy any resource. This method shall only be called in context when no other threads can use the resource. This method is only defined if the base container exports the CLEAR operator. ##### void name\_swap(name\_t concurrent1, name\_t concurrent2) Swap both containers. This method is only defined if the base container exports the SWAP operator. ##### bool name\_empty\_p(const name\_t concurrent) Return true if the container is empty, false otherwise. This method is only defined if the base container exports the EMPTY\_P operator. ##### void name\_set\_at(name\_t concurrent, key\_t key, value\_t value) Associate to the key 'key' the value 'value' in the container. This method is only defined if the base container exports the SET\_KEY operator. ##### bool name\_get\_copy(value\_t *value, name\_t concurrent, key\_t key) Read the value associated to the key 'key'. If it exists, it sets '*value' to it and returns true. Otherwise it returns false. This method is only defined if the base container exports the GET\_KEY operator. ##### void name\_safe\_get\_copy(value\_t *value, name\_t concurrent, key\_t key) Read the value associated to the key 'key'. If it exists, it sets '*value' to it. Otherwise it creates a new value and sets '*value' to it. This method is only defined if the base container exports the SAFE\_GET\_KEY operator. ##### bool name\_erase(name\_t concurrent, const key\_t key) Erase the association for the key 'key'. Returns true in case of success, false otherwise. This method is only defined if the base container exports the ERASE\_KEY operator. ##### void name\_push(name\_t concurrent, const subtype\_t data) Push data in the container. This method is only defined if the base container exports the PUSH operator. ##### void name\_pop(subtype\_t *data, name\_t concurrent) Pop data from the container and set it in '*data'. There shall be at least one data to pop. Testing with the operator EMPTY\_P before calling this function is not enough as there can be some concurrent scenario where another thread pop the last value. name\_pop\_blocking should be used instead. This method is only defined if the base container exports the POP operator. ##### void name\_push\_move(name\_t concurrent, subtype\_t *data) Push data in the container by stealing as much resources from data as possible. Afterwards, data is cleared. This method is only defined if the base container exports the PUSH\_MOVE operator. ##### void name\_pop\_move(subtype\_t *data, name\_t concurrent) Pop data from the container and initialize '*data' with it. name\_pop\_move\_blocking should be used instead (See name\_pop for details). This method is only defined if the base container exports the POP\_MOVE operator. ##### void name\_get\_str(string\_t str, name\_t concurrent, bool append) Convert the formatted container into a string representation of it and put it in 'str' This method is only defined if the base container exports the GET\_STR operator. ##### void name\_out\_str(FILE *file, name\_t concurrent) Convert the formatted container into a string and put it in 'file'. This method is only defined if the base container exports the OUT\_STR operator. ##### bool name\_parse\_str(name\_t concurrent, const char str[], const char **end) Convert the formatted string representing the container and set it 'concurrent' to it. Return true in case of success, false otherwise. This method is only defined if the base container exports the PARSE\_STR operator. ##### bool name\_in\_str(name\_t concurrent, FILE *file) Read the file and convert the formatted string representing the container and set it 'concurrent' to it. Return true in case of success, false otherwise. This method is only defined if the base container exports the IN\_STR operator. ##### bool name\_equal\_p(name\_t concurrent1, name\_t concurrent2) Return true if both containers are equal, false otherwise. This method is only defined if the base container exports the EQUAL operator. ##### bool name\_get\_blocking(value\_t *value, name\_t concurrent, key\_t key, bool blocking) Read the value associated to the key 'key'. If it exists, it sets '*value' to it and returns true. Otherwise if blocking is true, it waits for the data to be filled. After the wait, it sets '*value' to it and returns true. Otherwise if blocking is false, it returns false. This method is only defined if the base container exports the GET\_KEY operator. ##### bool name\_pop\_blocking(type\_t *data, name\_t concurrent, bool blocking) Pop a value from the container and set '*data' with it. If the container is not empty, it sets '*data' and return true. Otherwise if blocking is true, it waits for the data to be pushed. After the wait, it sets '*data' to it and returns true. Otherwise if blocking is false, it returns false. This method is only defined if the base container exports the POP and EMPTY\_P operators. ##### bool name\_pop\_move\_blocking(type\_t *data, name\_t concurrent, bool blocking) Pop a value from the container and initialize & set '*data' with it. If the container is not empty, it initializes & sets '*data' and return true. Otherwise if blocking is true, it waits for the data to be pushed. After the wait, it initializes & sets '*data' to it and returns true. Otherwise if blocking is false, it returns false (*data remains uninitialized!). This method is only defined if the base container exports the POP\_MOVE and EMPTY\_P operators. ##### size\_t name\_hash(name\_t concurrent) Return a value suitable for being a hash of the container. This method is only defined if the base container exports the HASH operator. ### M-BITSET This header is for using bitset. A [bitset](https://en.wikipedia.org/wiki/Bit_array) can be seen as a specialized version of an array of bool, where each item takes only 1 bit. It enables for compact representation of such array. Example: void f(void) { bitset_t set; bitset_init(set); for(int i = 0; i < 100; i ++) bitset_push_back(set, i%2); bitset_clear(set); } #### methods The methods are mostly the same than for an array. The following methods are available: ##### bitset\_t This type defines a dynamic array of bit and is the primary type of the module. ##### bitset\_it\_t This type defines an iterator over the bitset. ##### void bitset\_init(bitset\_t array) Initialize the bitset 'array' (aka constructor) to an empty array. ##### void bitset\_init\_set(bitset\_t array, const bitset\_t ref) Initialize the bitset 'array' (aka constructor) and set it to the value of 'ref'. ##### void bitset\_set(bitset\_t array, const bitset\_t ref) Set the bitset 'array' to the value of 'ref'. ##### void bitset\_init\_move(bitset\_t array, bitset\_t ref) Initialize the bitset 'array' (aka constructor) by stealing as many resources from 'ref' as possible. Afterwards 'ref' is cleared. ##### void bitset\_move(bitset\_t array, bitset\_t ref) Set the bitset 'array' by stealing as many resources from 'ref' as possible. Afterwards 'ref' is cleared. ##### void bitset\_clear(bitset\_t array) Clear the bitset 'array (aka destructor). ##### void bitset\_reset(bitset\_t array) Reset the bitset (the bitset becomes empty but remains initialized). ##### void bitset\_push\_back(bitset\_t array, const bool value) Push a new element into the back of the bitset 'array' with the value 'value'. ##### void bitset\_push\_at(bitset\_t array, size\_t key, const bool x) Push a new element into the position 'key' of the bitset 'array' with the value 'value' contained within. 'key' shall be a valid position of the array: from 0 to the size of array (included). ##### void bitset\_pop\_back(bool *data, bitset\_t array) Pop a element from the back of the bitset 'array' and set *data to this value if data is not NULL (if data is NULL, the popped data is cleared). ##### void bitset\_pop\_at(bool *dest, bitset\_t array, size\_t key) Set *dest to the value the element 'key' if dest is not NULL, then remove the element 'key' from the bitset. 'key' shall be within the size of the bitset. ##### bool bitset\_front(const bitset\_t array) Return the first element of the bitset. The bitset shall have at least one element. ##### bool bitset\_back(const bitset\_t array) Return the last element of the bitset. The bitset shall have at least one element. ##### void bitset\_set\_at(bitset\_t array, size\_t i, bool value) Set the element 'i' of bitset 'array' to 'value'. 'i' shall be within 0 to the size of the array (excluded). ##### void bitset\_flip\_at(bitset\_t array, size\_t i) Flip the element 'i' of bitset 'array''. 'i' shall be within 0 to the size of the array (excluded). ##### bool bitset\_get(bitset\_t array, size\_t i) Return the element 'i' of the bitset. 'i' shall be within 0 to the size of the array (excluded). ##### bool bitset\_empty\_p(const bitset\_t array) Return true if the bitset is empty, false otherwise. ##### size\_t bitset\_size(const bitset\_t array) Return the size of the bitset. ##### size\_t bitset\_capacity(const bitset\_t array) Return the capacity of the bitset. ##### void bitset\_resize(bitset\_t array, size\_t size) Resize the bitset 'array' to the size 'size' (initializing or clearing elements). ##### void bitset\_reserve(bitset\_t array, size\_t capacity) Extend or reduce the capacity of the 'array' to a rounded value based on 'capacity'. If the given capacity is below the current size of the bitset, the capacity is set to the size of the bitset. ##### void bitset\_swap(bitset\_t array1, bitset\_t array2) Swap the bitsets 'array1' and 'array2'. ##### void bitset\_swap\_at(bitset\_t array, size\_t i, size\_t j) Swap the elements 'i' and 'j' of the bitset 'array'. 'i' and 'j' shall reference valid elements of the array. ##### void bitset\_it(bitset\_it\_t it, bitset\_t array) Set the iterator 'it' to the first element of 'array'. ##### void bitset\_it\_last(bitset\_it\_t it, bitset\_t array) Set the iterator 'it' to the last element of 'array'. ##### void bitset\_it\_end(bitset\_it\_t it, bitset\_t array) Set the iterator 'it' to the end of 'array'. ##### void bitset\_it\_set(bitset\_it\_t it1, bitset\_it\_t it2) Set the iterator 'it1' to 'it2'. ##### bool bitset\_end\_p(bitset\_it\_t it) Return true if the iterator doesn't reference a valid element anymore. ##### bool bitset\_last\_p(bitset\_it\_t it) Return true if the iterator references the last element of the array, or doesn't reference a valid element. ##### bool bitset\_it\_equal\_p(const bitset\_it\_t it1, const bitset\_it\_t it2) Return true if both iterators reference the same element. ##### void bitset\_next(bitset\_it\_t it) Move the iterator 'it' to the next element of the array. ##### void bitset\_previous(bitset\_it\_t it) Move the iterator 'it' to the previous element of the array. ##### const bool *bitset\_cref(const bitset\_it\_t it) Return a constant pointer to the element pointed by the iterator. This pointer remains valid until the array or the iterator is modified by another method. ##### void bitset\_get\_str(string\_t str, const bitset\_t array, bool append) Generate a formatted string representation of the bitset 'array' and set 'str' to this representation (if 'append' is false) or append 'str' with this representation (if 'append' is true). This method is only defined if the header 'm-string.h' was included before including 'm-bitset.h' ##### bool bitset\_parse\_str(bitset\_t array, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a bitset and set 'array' to this representation. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void bitset\_out\_str(FILE *file, const bitset\_t array) Generate a formatted string representation of the bitset 'array' and outputs it into the FILE 'file'. ##### void bitset\_in\_str(bitset\_t array, FILE *file) Read from the file 'file' a formatted string representation of a bitset and set 'array' to this representation. ##### bool bitset\_equal\_p(const bitset\_t array1, const bitset\_t array2) Return true if both bitsets 'array1' and 'array2' are equal. ##### size\_t bitset\_hash(const bitset\_t array) Return a hash value of 'array'. ##### void bitset\_and(bitset\_t dst, const bitset\_t src) Perform a bitwise AND operation in 'dst' between 'dst' and 'src' (effectively performing an intersection of the sets). ##### void bitset\_or(bitset\_t dst, const bitset\_t src) Perform a bitwise OR operation in 'dst' between 'dst' and 'src' (effectively performing an union of the sets). ##### void bitset\_xor(bitset\_t dst, const bitset\_t src) Perform a bitwise XOR operation in 'dst' between dst and src. ##### void bitset\_not(bitset\_t dst) Perform a bitwise NOT operation for dst. ##### size\_t bitset\_clz(const bitset\_t src) Return the leading zero position in 'src' (Count Leading Zero). ##### size\_t bitset\_popcount(const bitset\_t src) Count the number of '1' in 'src'. ### M-STRING This header is for using dynamic [string](https://en.wikipedia.org/wiki/String_(computer_science)). The size of the string is automatically updated in function of the needs. Example: void f(void) { string_t s1; string_init (s1); string_set_str (s1, "Hello, world!"); string_clear(s1); } #### methods The following methods are available: ##### string\_t This type defines a dynamic string and is the primary type of the module. The provided methods are just handy wrappers to the C library, providing few algorithms on its own. ##### STRING\_FAILURE Constant Macro defined as the index value returned in case of error. (equal as -1U). ##### string\_fgets\_t This type defines the different enumerate value for the string\_fgets function: * STRING\_READ\_LINE: read a full line until the EOL character (included), * STRING\_READ\_PURE\_LINE: read a full line until the EOL character (excluded), * STRING\_READ\_FILE: read the full file. ##### void string\_init(string\_t str) Init the string 'str' to an empty string. ##### void string\_clear(string\_t str) Clear the string 'str' and frees any allocated memory. ##### char *string\_clear\_get\_str(string\_t v) Clear the string 'str' and returns the allocated array of char, representing a C string, giving back ownership of this array to the caller. This array will have to be freed. It can return NULL if no array was allocated by the string. ##### void string\_reset(string\_t str) Reset the string 'str' to an empty string. ##### size\_t string\_size(const string\_t str) Return the size in bytes of the string. It can be also the number of characters of the string if the encoding type is one character per byte. If the characters are encoded as UTF8, the function string\_length\_u is preferred. ##### size\_t string\_capacity(const string\_t str) Return the capacity in bytes of the string. The capacity is the number of bytes the string accept before a reallocation of the underlying array of char has to be performed. ##### char string\_get\_char(const string\_t v, size\_t index) Return the byte at position 'index' of the string 'v'. 'index' shall be within the allowed range of bytes of the string. ##### void string\_set\_char(string\_t v, size\_t index, const char c) Set the byte at position 'index' of the string 'v' to 'c'. 'index' shall be within the allowed range of bytes of the string. ##### bool string\_empty\_p(const string\_t v) Return true if the string is empty, false otherwise. ##### void string\_reserve(string\_t v, size\_t alloc) Update the capacity of the string to be able to receive at least 'alloc' bytes. Calling with 'alloc' lower or equal than the size of the string enables the function to perform a shrink of the string to its exact needs. If the string is empty, it will free the memory. ##### void string\_set\_str(string\_t v, const char str[]) Set the string to the array of char 'str'. 'str' is supposed to be 0 terminated as any C string. ##### void string\_set\_strn(string\_t v, const char str[], size\_t n) Set the string to the array of char 'str' by copying at most 'n' char from the array. 'str' is supposed to be 0 terminated as any C string. ##### const char* string\_get\_cstr(const string\_t v) Return a constant pointer to the underlying array of char of the string. This array of char is terminated by 0, enabling the pointer to be passed to standard C function. The pointer remains valid until the string itself remains valid and the next call to a function that updates the string. ##### void string\_set (string\_t v1, const string\_t v2) Set the string 'v1' to the value of the string 'v2'. ##### void string\_set\_n(string\_t v, const string\_t ref, size\_t offset, size\_t length) Set the string to the value of the string 'ref' by skipping the first 'offset' char of the array of char of 'ref' and copying at most 'length' char in the remaining array of characters of 'ref'. 'offset' shall be within the range of index of the string 'ref'. 'ref' and 'v' cannot be the same string. ##### void string\_init\_set(string\_t v1, const string\_t v2) Initialize 'v1' to the value of the string 'v2'. ##### void string\_init\_set\_str(string\_t v1, const char str[]) Initialize 'v1' to the value of the array of char 'str'. The array of char shall be terminated with 0. ##### void string\_init\_move(string\_t v1, string\_t v2) Initialize 'v1' by stealing as most resource from 'v2' as possible and clear 'v2' afterward. ##### void string\_move(string\_t v1, string\_t v2) Set 'v1' by stealing as most resource from 'v2' as possible and clear 'v2' afterward. ##### void string\_swap(string\_t v1, string\_t v2) Swap the content of both strings. ##### void string\_push\_back (string\_t v, char c) Append the character 'c' to the string 'v' ##### void string\_cat\_str(string\_t v, const char str[]) Append the array of char 'str' to the string 'v'. The array of char shall be terminated with 0. ##### void string\_cat(string\_t v, const string\_t v2) Append the string 'v2' to the string 'v'. NOTE: v2 can also be a 'const char *' in C11. ##### void string\_cats(string\_t v, const string\_t v2[, ...] ) Append all the strings 'v2' ... to the string 'v'. NOTE: v2 can also be a 'const char *' in C11. ##### void string\_sets(string\_t v, const string\_t v2[, ...] ) Set the string 'v' to the concatenation of all the strings 'v2' NOTE: v2 can also be a 'const char *' in C11. ##### int string\_cmp\_str(const string\_t v1, const char str[]) ##### int string\_cmp(const string\_t v1, const string\_t str) Perform a byte comparison of both string by using the strcmp function and return the result of this comparison. ##### bool string\_equal\_str\_p(const string\_t v1, const char str[]) Return true if the string is equal to the array of char, false otherwise. ##### bool string\_equal\_p(const string\_t v1, const string\_t v2) Return true if both strings are equal, false otherwise. ##### int string\_cmpi\_str(const string\_t v, const char str[]) ##### int string\_cmpi(const string\_t v, const string\_t str) This function compares both strings by ignoring the difference due to the case. This function doesn't work with UTF-8 strings. It returns a negative integer if the string is before the array, 0 if there are equal, a positive integer if the string is after the array. ##### size\_t string\_search\_char (const string\_t v, char c [, size\_t start]) Search for the character 'c' in the string from the offset 'start'. 'start' shall be within the valid ranges of offset of the string. 'start' is an optional argument. If it is not present, the default value 0 is used instead. This doesn't work if the function is used as function pointer. Return the offset of the string where the character is first found, or STRING\_FAILURE otherwise. ##### size\_t string\_search\_rchar (const string\_t v, char c [, size\_t start]) Search backwards for the character 'c' in the string from the offset 'start'. 'start' shall be within the valid ranges of offset of the string. 'start' is an optional argument. If it is not present, the default value 0 is used instead. This doesn't work if the function is used as function pointer. Return the offset of the string where the character is last found, or STRING\_FAILURE otherwise. ##### size\_t string\_search\_str (const string\_t v, char str[] [, size\_t start]) ##### size\_t string\_search (const string\_t v, string\_t str [, size\_t start]) Search for the string 'str' in the string from the offset 'start'. 'start' shall be within the valid ranges of offset of the string. 'start' is an optional argument. If it is not present, the default value 0 is used instead. This doesn't work if the function is used as function pointer. Return the offset of the string where 'str' is first found, or STRING\_FAILURE otherwise. ##### size\_t string\_pbrk(const string\_t v, const char first\_of[] [, size\_t start]) Search for the first occurrence in the string 'v' from the offset 'start' of any of the bytes in the string 'first\_of'. 'start' shall be within the valid ranges of offset of the string. 'start' is an optional argument. If it is not present, the default value 0 is used instead. This doesn't work if the function is used as function pointer. Return the offset of the string where 'str' is first found, or STRING\_FAILURE otherwise. ##### int string\_strcoll\_str(const string\_t str1, const char str2[]) ##### int string\_strcoll(const string\_t str1, const string\_t str2[]) Compare the two strings str1 and str2. It returns an integer less than, equal to, or greater than zero if 's1' is found, respectively, to be less than, to match, or be greater than s2. The comparison is based on strings interpreted as appropriate for the program's current locale. ##### size\_t string\_spn(const string\_t v1, const char accept[]) Calculate the length (in bytes) of the initial segment of the string that consists entirely of bytes in accept. ##### size\_t string\_cspn(const string\_t v1, const char reject[]) Calculate the length (in bytes) of the initial segment of the string that consists entirely of bytes not in reject. ##### void string\_left(string\_t v, size\_t index) Keep at most the 'index' left bytes of the string, terminating the string at the given index. index can be out of range. ##### void string\_right(string\_t v, size\_t index) Keep the right part of the string, after the index 'index'. ##### void string\_mid (string\_t v, size\_t index, size\_t size) Extract the medium string from offset 'index' and up to 'size' bytes. ##### size\_t string\_replace\_str (string\_t v, const char str1[], const char str2[] [, size\_t start]) ##### size\_t string\_replace (string\_t v, const string\_t str1, const string\_t str2 [ , size\_t start]) Replace in the string 'v' from the offset 'start' the string 'str1' by the string 'str2' once. Returns the offset of the replacement or STRING\_FAILURE if no replacement was performed. str1 shall be a non empty string. ##### size\_t string\_replace\_all\_str (string\_t v, const char str1[], const char str2[]) ##### size\_t string\_replace\_all (string\_t v, const string\_t str1, const string\_t str2) Replace in the string 'v' all the occurrences of the string 'str1' by the string 'str2'. str1 shall be a non empty string. ##### void string\_replace\_at (string\_t v, size\_t pos, size\_t len, const char str2[]) Replace in the string 'v' the sub-string defined as starting from 'pos' and of size 'len' by the string str2. It assumes that pos+len is before the end of the string of 'v'. ##### void string\_init\_printf(string\_t v, const char format[], ...) Initialize 'v' to the formatted string 'format' with the given variable argument lists. 'format' is like the printf function family. ##### void string\_init\_vprintf(string\_t v, const char format[], va\_list args) Initialize 'v' to the formatted string 'format' with the given variable argument lists 'args'. 'format' is like the printf function family. ##### int string\_printf (string\_t v, const char format[], ...) Set the string 'v' to the formatted string 'format'. 'format' is like the printf function family with the given variable argument list. Return the number of characters printed (excluding the final null char), or a negative value in case of error. ##### int string\_vprintf (string\_t v, const char format[], va\_list args) Set the string 'v' to the formatted string 'format'. 'format' is like the vprintf function family with the variable argument list 'args'. Return the number of characters printed (excluding the final null char), or a negative value in case of error. ##### int string\_cat\_printf (string\_t v, const char format[], ...) Appends to the string 'v' the formatted string 'format'. 'format' is like the printf function family. ##### bool string\_fgets(string\_t v, FILE \*f, string\_fgets\_t arg) Read from the opened file 'f' a stream of characters and set 'v' with this stream. It stops after the character end of line if arg is STRING\_READ\_PURE\_LINE or STRING\_READ\_LINE, and until the end of the file if arg is STRING\_READ\_FILE. If arg is STRING\_READ\_PURE\_LINE, the character end of line is removed from the string. Return true if something has been read, false otherwise. ##### bool string\_fget\_word (string\_t v, const char separator[], FILE \*f) Read a word from the file 'f' and set 'v' with this word. A word is separated from another by the list of characters in the array 'separator'. (Example: "^ \t.\n"). It is highly recommended for separator to be a constant string. 'separator' shall be at most composed of 100 characters (as bytes). ##### void string\_fputs(FILE \*f, const string\_t v) Put the string in the file. ##### bool string\_start\_with\_str\_p(const string\_t v, const char str[]) ##### bool string\_start\_with\_string\_p(const string\_t v, const string\_t str) Return true if the string starts with the same characters than 'str', false otherwise. ##### bool string\_end\_with\_str\_p(const string\_t v, const char str[]) ##### bool string\_end\_with\_string\_p(const string\_t v, const string\_t str) Return true if the string ends with the same characters than 'str', false otherwise. ##### size\_t string\_hash(const string\_t v) Return a hash of the string. ##### void string\_strim(string\_t v [, const char charTab[]]) Remove from the string any leading or trailing space-like characters (space or tabulation or end of line). If 'charTab' is given, it get the list of characters to remove from this argument. ##### bool string\_oor\_equal\_p(const string\_t v, unsigned char n) Provide the OOR\_EQUAL\_P method of a string. ##### void string\_oor\_set(string\_t v, unsigned char n) Provide the OOR\_SET method of a string. ##### void string\_get\_str(string\_t v, const string\_t v2, bool append) Convert a string into a formatted string usable for I/O: Outputs the input string with quote around, replacing any \" by \\\" within the string into the output string. ##### bool string\_parse\_str(string\_t v, const char str[], const char **endp) Parse the formatted string 'str' that is assumed to be a string representation of a string and set 'v' to this representation. This method is only defined if the type of the element defines a PARSE\_STR method itself. It returns true if success, false otherwise. If endp is not NULL, it sets '*endp' to the pointer of the first character not decoded by the function. ##### void string\_out\_str(FILE *f, const string\_t v) Write a string into a FILE as a formatted string: Outputs the input string while quoting around, replacing any \" by \\\" within the string, and quote special characters. ##### bool string\_in\_str(string\_t v, FILE *f) Read a formatted string from a FILE. The string shall follow the formatting rules of string\_out\_str. It returns true if it has successfully parsed the string, false otherwise. In this case, the position within the FILE is undefined. ##### string\_unicode\_t Define a type suitable to store a unicode character. ##### string\_it\_t Define an iterator over the string, enabling to iterate the string over UTF8 encoded character. ##### void string\_it(string\_it\_t it, const string\_t str) Initialize the iterator 'it' to iterate over the string 'str' over UTF8 encoded character. ##### bool string\_end\_p (string\_it\_t it) Return true if the iterator has reached the end of the string, false otherwise. ##### void string\_next (string\_it\_t it) Move the iterator to the next UTF8 encoded character. string\_end\_p shall have been called at least once per UTF8 character before. ##### string\_unicode\_t string\_get\_cref (const string\_it\_t it) Return the unicode character associated to the UTF8 encoded character pointer by the iterator. string\_end\_p shall have been called at least once per UTF8 character before. It returns -1 in case of error in decoding the UTF8 string. ##### void string\_push\_u (string\_t str, string\_unicode\_t u) Push the unicode character 'u' into the string 'str' encoding it as a UTF8 encoded characters. ##### size\_t string\_length\_u(string\_t str) Return the number of UTF8 encoded characters in the string. ##### bool string\_utf8\_p(string\_t str) Return true if the string is a valid UTF8, false otherwise. It doesn't check for unique canonical form for UTF8 string, so it may report 'true' whereas the string is not strictly conforming. ##### STRING\_CTE(string) Macro to convert a constant array string into a temporary string\_t variable suitable only for being called within a function. ##### STRING\_OPLIST The oplist of a string\_t ##### BOUNDED\_STRING\_DEF(name, size) aka char[N+1] TODO: Document the API. ### M-CORE This header is the internal core of M\*LIB, providing a lot of functionality by extending a lot the preprocessing capability. Working with these macros is not easy and the developer needs to know how the macro preprocessing works. It also adds the needed macro for handling the oplist. As a consequence, it is needed by all other header files. Some macros are using recursion to work. This is not an easy feat to do as it needs some tricks to work (see reference). This still work well with only one major limitation: it can not be chained. For example, if MACRO is a macro implementing recursion, then MACRO(MACRO()) won't work. Example: M_MAP(f, 1, 2, 3, 4) M_REDUCE(f, g, 1, 2, 3, 4) M_SEQ(1, 20, f) #### Compiler Macros The following compiler macros are available: ##### M\_ASSUME(cond) M\_ASSUME is equivalent to assert, but gives hints to compiler about how to optimize the code if NDEBUG is defined. ##### M\_LIKELY(cond) / M\_UNLIKELY(cond) M\_LIKELY / M\_UNLIKELY gives hints on the compiler of the likelihood of the given condition. #### Preprocessing macro extension ##### M\_MAX\_NB\_ARGUMENT Maximum default number of argument that can be handled by this header. It is currently 52 (even if some local macros may have increased this limit). ##### M\_C(a,b) ##### M\_C3(a,b,c) ##### M\_C4(a,b,c,d) Return a symbol corresponding to the concatenation of the input arguments. ##### M\_INC(number) Increment the number given as argument and return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). The number shall be within the range [0..M\_MAX\_NB\_ARGUMENT-1]. ##### M\_DEC(number) Decrement the number given as argument and return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). The number shall be within the range [1..M\_MAX\_NB\_ARGUMENT]. ##### M\_ADD(x, y) Return x+y (resolution is performed at preprocessing time). x, y shall be within [0..M\_MAX\_NB\_ARGUMENT]. If the result is not in [0..M\_MAX\_NB\_ARGUMENT+1], it returns M_OVERFLOW. ##### M\_SUB(x, y) Return x-y (resolution is performed at preprocessing time). x, y shall be within [0..M\_MAX\_NB\_ARGUMENT]. If the result is not in [0..M\_MAX\_NB\_ARGUMENT], it returns M_UNDERFLOW. ##### M\_BOOL(cond) Convert an integer or a symbol into 0 (if 0) or 1 (if not 0 or symbol unknown). Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_INV(cond) Inverse 0 into 1 and 1 into 0. It is undefined if cond is not 0 or 1 (use M\_BOOL to convert). Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_AND(cond1, cond2) Perform a logical 'and' between cond1 and cond2. cond1 and cond2 shall be 0 or 1. (You should use M\_bool to convert this parameter otherwise). Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_OR(cond1, cond2) Perform a logical 'or' between cond1 and cond2. cond1 and cond2 shall be 0 or 1. (You should use M\_bool to convert this parameter otherwise). Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_NOTEQUAL(x, y) Return 1 if x != y, 0 otherwise (resolution is performed at preprocessing time). x and y shall be within [0..M\_MAX\_NB\_ARGUMENT]. ##### M\_EQUAL(x, y) Return 1 if x == y, 0 otherwise (resolution is performed at preprocessing time). x and y shall be within [0..M\_MAX\_NB\_ARGUMENT]. ##### M\_LESS\_THAN\_P(x, y) Return 1 if x < y, 0 otherwise (resolution is performed at preprocessing time). x and y shall be within [0..M\_MAX\_NB\_ARGUMENT].x ##### M\_LESS_OR\_EQUAL\_P(x, y) Return 1 if x <= y, 0 otherwise (resolution is performed at preprocessing time). x and y shall be within [0..M\_MAX\_NB\_ARGUMENT].x ##### M\_GREATER_OR\_EQUAL\_P(x, y) Return 1 if x >= y, 0 otherwise (resolution is performed at preprocessing time). x and y shall be within [0..M\_MAX\_NB\_ARGUMENT].x ##### M\_GREATER_THAN\_P(x, y) Return 1 if x > y, 0 otherwise (resolution is performed at preprocessing time). x and y shall be within [0..M\_MAX\_NB\_ARGUMENT].x ##### M\_COMMA\_P(arglist) Return 1 if there is a comma inside the argument list, 0 otherwise. Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_EMPTY\_P(expression) Return 1 if the argument 'expression' is 'empty', 0 otherwise. Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). NOTE: It should work for a wide range of inputs except when it is called with a macro function that takes more than one argument and without its arguments (in which case it generates a compiler error). ##### M\_PARENTHESIS\_P(expression) Return 1 if the argument 'expression' starts a parenthesis and ends it (like '(...)'), 0 otherwise. Return a pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_KEYWORD\_P(reference\_keyword, get\_keyword) Return 1 if the argument 'get\_keyword' is equal to 'reference\_keyword', 0 otherwise. reference\_keyword shall be a keyword in the following list: * and * or * add (or sum, they are considered equivalent) * mul (or product, they are considered equivalent) * void * bool * char * short * int * long * float * double * TYPE * SUBTYPE * IT_TYPE * M\_OVERFLOW * M\_UNDERFLOW * SEQUENCE * MAP * KEYVAL * KEYVAL\_PTR ##### M\_IF(cond)(action\_if\_true, action\_if\_false) Return the pre-processing token 'action\_if\_true' if 'cond' is true, action\_if\_false otherwise (meaning it is evaluated at macro processing stage, not at compiler stage). cond shall be a 0 or 1 as a preprocessing constant. (You should use M\_bool to convert this parameter otherwise). ##### M\_IF\_EMPTY(cond)(action\_if\_true, action\_if\_false) Return the pre-processing token 'action\_if\_true' if 'cond' is empty, action\_if\_false otherwise (meaning it is evaluated at macro processing stage, not at compiler stage). cond shall be a preprocessing constant equal to 0 or 1. (You should use M\_bool to convert this parameter otherwise). ##### M\_DELAY1(expr) ##### M\_DELAY2(expr) ##### M\_DELAY3(expr) ##### M\_DELAY4(expr) ##### M\_ID Delay the evaluation by 1, 2, 3 or 4 steps. This is necessary to write macros that are recursive. The argument is a macro-function that has to be deferred. M\_ID is an equivalent of M\_DELAY1. ##### M\_EVAL(expr) Perform a complete stage evaluation of the given expression, removing recursive expression within it. Only ONE M\_EVAL expression is expected in the evaluation chain. Can not be chained. ##### M\_APPLY(func, args...) Apply 'func' to '(args...) ensuring that a() isn't evaluated until all 'args' have been also evaluated. It is used to delay evaluation. ##### M\_EAT(...) Clobber the input, whatever it is. ##### M\_RET\_ARG'N'(arglist...) Return the argument 'N' of the given arglist. N shall be within [1..76]. The argument shall exist in the arglist. The arglist shall have at least one more argument that 'N'. ##### M\_GET\_AT(list, index) Return the index 'index' of the list 'list', which is a list of arguments encapsulated with parenthesis, (it is not a true C array). Return the pre-processing token corresponding to this value (meaning it is evaluated at macro processing stage, not at compiler stage). M_GET_AT((f_0,f_1,f_2),1) ==> f_1 ##### M\_SKIP\_ARGS(N,...) Skip the Nth first arguments of the argument list. N shall be within [0..M\_MAX\_NB\_ARGUMENT]. M_SKIP_ARGS(2, a, b, c, d) ==> c, d ##### M\_KEEP\_ARGS(N,...) Keep the Nth first arguments of the argument list. N shall be within [0..M\_MAX\_NB\_ARGUMENT]. M_KEEP_ARGS(2, a, b, c, d) ==> a, b ##### M\_MID\_ARGS(first, len,...) Keep the medium arguments of the argument list, starting from the 'first'-th one (zero based) and up to 'len' arguments. first and len shall be within [0..M\_MAX\_NB\_ARGUMENT]. first+len shall be within the argument of the argument list. M_MID_ARGS(2, 1, a, b, c, d) ==> c ##### M\_REVERSE(args...) Reverse the argument list. M_REVERSE(a, b, c, d) ==> d, c, b, a ##### M\_MAP(func, args...) Apply 'func' to each argument of the 'args...' list of argument. M_MAP(f, 1, 2, 3) ==> f(1) f(2) f(3) ##### M\_MAP\_C(func, args...) Apply 'func' to each argument of the 'args...' list of argument, putting a comma between each expanded 'func(argc)' M_MAP_C(f, 1, 2, 3) ==> f(1) , f(2) , f(3) ##### M\_MAP2(func, data, args...) Apply 'func' to each couple '(data, argument)' with argument an element of the 'args...' list. M_MAP2(f, d, 1, 2, 3) ==> f(d, 1) f(d, 2) f(d, 3) ##### M\_MAP2\_C(func, data, args...) Apply 'func' to each couple '(data, argument)' with argument an element of the 'args...' list, putting a comma between each expanded 'func(argc)' M_MAP2_C(f, d, 1, 2, 3) ==> f(d, 1) , f(d, 2) , f(d, 3) ##### M\_MAP3(func, data, args...) Apply 'func' to each tuple '(data, number, argument)' with argument an element of the 'args...' list and number from 1 to N (the index of the list). M_MAP3(f, d, a, b, c) ==> f(d, 1, a) f(d, 2, b) f(d, 3, c) ##### M\_MAP3\_C(func, data, args...) Apply 'func' to each tuple '(data, number, argument)' with argument an element of the 'args...' list, and number from 1 to N (the index of the list) putting a comma between each expanded 'func(argc)' M_MAP3_C(f, d, a, b, c) ==> f(d, 1, a) , f(d, 2, b) , f(d, 3, c) ##### M\_MAP\_PAIR(func, args...) Map a macro to all given pair of arguments (Using recursion). Can not be chained. M_MAP_PAIR(f, a, b, c, d) ==> f(a, b) f(c, d) ##### M\_REDUCE(funcMap, funcReduce, args...) Map the macro funcMap to all given arguments 'args' and reduce all theses computation with the macro 'funcReduce'. M_REDUCE(f, g, a, b, c) ==> g( f(a) , g( f(b) , f(c) ) ) ##### M\_REDUCE2(funcMap, funcReduce, data, args...) Map the macro funcMap to all pair (data, arg) of the given argument list 'args' and reduce all theses computation with the macro 'funcReduce'. M_REDUCE2(f, g, d, a, b, c) ==> g( f(d, a) , g( f(d, b) , f(d, c) ) ) ##### M\_REDUCE3(funcMap, funcReduce, data, args...) Map the macro funcMap to all tuple (data, number arg) of the given argument list 'args' with number from 1 to N( the size of args) and reduce all theses computation with the macro 'funcReduce'. M_REDUCE3(f, g, d, a, b, c) ==> g( f(d, 1, a) , g( f(d, 2, b) , f(d, 3, c) ) ) ##### M\_SEQ(init, end) Generate a sequence of number from 'init' to 'end' M_SEQ(1, 6) ==> 1,2,3,4,5,6 ##### M\_REPLICATE(N, value) Replicate the value 'value' N times. M_REPLICATE(5, D) ==> D D D D D ##### M\_REPLICATE\_C(N, value) Replicate the value 'value' N times, separating then by commas. M_REPLICATE_C(5, D) ==> D , D , D , D , D ##### M\_FILTER(func, data, ...) Filter the arglists by keeping only the element that match the function 'func(data, element)' M_FILTER(M_NOTEQUAL, 8, 1, 3, 4, 8, 9, 8, 10) ==> 1 3 4 9 10 ##### M\_FILTER\_C(func, data, ...) Filter the arglists by keeping only the element that match the function 'func(data, element)' separated by commas. M_FILTER(M_NOTEQUAL, 8, 1, 3, 4, 8, 9, 8, 10) ==> 1 , 3 , 4 , 9 , 10 ##### M\_NARGS(args...) Return the number of argument of the given list. args shall not be an empty argument. ##### M\_IF\_NARGS\_EQ1(argslist)(action\_if\_one\_arg, action\_otherwise) Return the pre-processing token 'action\_if\_one\_arg' if 'argslist' has only one argument, action\_otherwise otherwise (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_IF\_NARGS\_EQ2(argslist)(action\_if\_two\_arg, action\_otherwise) Return the pre-processing token 'action\_if\_two\_arg' if 'argslist' has two arguments, action\_otherwise otherwise (meaning it is evaluated at macro processing stage, not at compiler stage). ##### M\_IF\_DEBUG(action) Return the pre-processing token 'action' if the build is compiled in debug mode (i.e. NDEBUG is undefined). ##### M\_IF\_DEFAULT1(default\_value, argumentlist) Helper macro to redefine a function with a default value. If there is only one variable as the argument list, print the variable of the argument list then ', value', instead only print the argument list (and so two arguments). int f(int a, int b); #define f(...) M_APPLY(f, M_IF_DEFAULT1(0, __VA_ARGS__)) This need to be called within a M\_APPLY macro. Experimental macro. It may dissapear or change in a broken way. ##### M\_DEFAULT\_ARGS(nbExpectedArg, (defaultArgumentlist), argumentList ) Helper macro to redefine a function with one or more default values. defaultArgumentlist is a list of the default value to complete the list argumentList to reach the number nbExpectedArg arguments. Example: int f(int a, int b, long p, void *q); #define f(...) f(M_DEFAULT_ARGS(4, (0, 1, NULL), __VA_ARGS__)) The last 3 arguments have their default value as 0 (for b), 1 (for p) and NULL (for q). Experimental macro. It may dissapear or change in a broken way. ##### M\_DEFERRED\_COMMA Return a comma ',' at a later phase of the macro processing steps (delay evaluation). ##### M\_AS\_STR(expression) Return the string representation of the evaluated expression. #### C11 Macro Theses macros are only valid if the program is built in C11 mode: ##### M\_PRINTF\_FORMAT(x) Return the printf format associated to the type of 'x'. 'x' shall be a basic C variable, printable with printf. ##### M\_PRINT\_ARG(x) Print using printf the variable 'x'. The format of 'x' is deduced provided that it is a standard numerical C type. If m-string is included, it supports also the type 'string\_t' natively. If the argument is extended (i.e. in the format '(var, optype)' with optype being either an oplist or a type with a globally registered oplist), then it will call the OUT\_STR method of the oplist on the variable 'var'. ##### M\_FPRINT\_ARG(file, x) Print into a file 'file' using fprintf the variable 'x'. See M\_PRINT\_ARG for details on the supported variable. ##### M\_GET\_STRING\_ARG(string,x,append) Print into the string\_t 'string' the variable 'x'. The format of 'x' is deduced provided that it is a standard numerical C type. It needs the header 'm-string.h' for working (this macro is only a wrapper around it). It supports also the type 'string\_t'. ##### M\_PRINT(args...) Print using printf all the variable in 'args'. See M\_PRINT\_ARG for details on the supported variable. ##### M\_FPRINT(file, args...) Print into a file 'file' using fprintf all the variables in 'args'. See M\_PRINT\_ARG for details on the supported variable. ##### M\_AS\_TYPE(type, x) Within a C11 \_Generic statement, all expressions must be valid C expression even if the case if always false, and is not executed. This can seriously limit the \_Generic statement. This macro overcomes this limitation by returning : * either the input 'x' if it is of type 'type', * or the value 0 view as a type 'type'. So the returned value is **always** of type 'type' and is safe in a \_Generic statement. #### C Macro Theses macros expand their code at compilation level: ##### M\_MIN(x, y) Return the minimum of 'x' and 'y'. x and y shall not have any side effect. ##### M\_MAX(x, y) Return the maximum of 'x' and 'y'. x and y shall not have any side effect. ##### M\_POWEROF2\_P(n) Return true if 'n' is a power of 2. n shall not have any side effect. ##### M\_SWAP(type, a, b) Swap the values of 'a' and 'b'. 'a' and 'b' are of type 'type'. a and b shall not have any side effect. ##### M\_ASSIGN\_CAST(type, a) Check if 'a' can be assigned to a temporary of type 'type' and returns this temporary. If it cannot, the compilation failed. ##### M\_CONST\_CAST(type, a) Check if 'a' can be properly casted to (const type *) and returns this casted pointer if possible. If it cannot, the compilation failed. ##### M\_TYPE\_FROM\_FIELD(type, ptr, fieldType, field) Assuming 'ptr' is a pointer to a fieldType object that is stored within a structure of type 'type' at the position 'field', it returns a pointer to the structure. NOTE: It is equivalent to the container\_of macro of the Linux kernel. ##### M\_CSTR(format, ...) Return a constant string constructed based on the printf-liked formated string and its arguments. The string is constructed at run time and uses a temporary space on the stack. If the constructed string is longer than M\_USE\_CSTR\_ALLOC-1, the string is truncated. Example: strcmp( M_CSTR("Len=%d", 17) , "Len=17" ) == 0 #### HASH Functions ##### M\_USE\_HASH\_SEED A User modifiable macro defining the initial random seed (of type size\_t). It shall be define before including any header of M\*LIB, so that hash functions use this variable as their initial seed for their hash computation of an object. It can be used to generate less predictable hash values at runtime, which may protect against [DOS dictionary attacks](https://events.ccc.de/congress/2011/Fahrplan/attachments/2007_28C3_Effective_DoS_on_web_application_platforms.pdf). It shall be unique for a running instance of M\*LIB. Note that using a random seed is not enough to protect efficienly againt such attacks. A cryptographic secure hash may be also needed. If it is not defined, the default is to use the value 0, making all hash computations predictable. ##### M\_HASH\_DECL(hash) Declare and initialize a new hash computation, named 'hash' that is an integer. ##### M\_HASH\_UP(hash, value) Update the 'hash' variable with the given 'value' by incorporating the 'value' within the 'hash'. 'value' can be up to a 'size\_t' variable. ##### uint32\_t m\_core\_rotl32a (uint32\_t x, uint32\_t n) ##### uint64\_t m\_core\_rotl64a (uint64\_t x, uint32\_t n) Perform a rotation of 'n' bits of the input 'x'. n shall be within 1 and the number of bits of the type minus 1. ##### uint64\_t m\_core\_roundpow2(uint64\_t v) Round to the next highest power of 2. ##### unsigned int m\_core\_clz32(uint32\_t limb) ##### unsigned int m\_core\_clz64(uint64\_t limb) Count the number of leading zero and return it. limb can be 0. ##### size\_t m\_core\_hash (const void *str, size\_t length) Compute the hash of the binary representation of the data pointer by 'str' of length 'length'. 'str' shall have the same alignment restriction than a 'size\_t'. #### OPERATORS Functions ##### M\_GET\_INIT oplist ##### M\_GET\_INIT\_SET oplist ##### M\_GET\_INIT\_MOVE oplist ##### M\_GET\_SET oplist ##### M\_GET\_MOVE oplist ##### M\_GET\_SWAP oplist ##### M\_GET\_CLEAR oplist ##### M\_GET\_NEW oplist ##### M\_GET\_DEL oplist ##### M\_GET\_REALLOC oplist ##### M\_GET\_FREE oplist ##### M\_GET\_MEMPOOL oplist ##### M\_GET\_MEMPOOL\_LINKAGE oplist ##### M\_GET\_HASH oplist ##### M\_GET\_EQUAL oplist ##### M\_GET\_CMP oplist ##### M\_GET\_TYPE oplist ##### M\_GET\_SUBTYPE oplist ##### M\_GET\_OPLIST oplist ##### M\_GET\_SORT oplist ##### M\_GET\_IT\_TYPE oplist ##### M\_GET\_IT\_FIRST oplist ##### M\_GET\_IT\_LAST oplist ##### M\_GET\_IT\_END oplist ##### M\_GET\_IT\_SET oplist ##### M\_GET\_IT\_END\_P oplist ##### M\_GET\_IT\_LAST\_P oplist ##### M\_GET\_IT\_EQUAL\_P oplist ##### M\_GET\_IT\_NEXT oplist ##### M\_GET\_IT\_PREVIOUS oplist ##### M\_GET\_IT\_REF oplist ##### M\_GET\_IT\_CREF oplist ##### M\_GET\_IT\_REMOVE oplist ##### M\_GET\_IT\_INSERT oplist ##### M\_GET\_ADD oplist ##### M\_GET\_SUB oplist ##### M\_GET\_MUL oplist ##### M\_GET\_DIV oplist ##### M\_GET\_RESET oplist ##### M\_GET\_PUSH oplist ##### M\_GET\_POP oplist ##### M\_GET\_REVERSE oplist ##### M\_GET\_GET\_STR oplist ##### M\_GET\_OUT\_STR oplist ##### M\_GET\_IN\_STR oplist ##### M\_GET\_SEPARATOR oplist ##### M\_GET\_EXT\_ALGO oplist ##### M\_GET\_INC\_ALLOC oplist ##### M\_GET\_OOR\_SET oplist ##### M\_GET\_OOR\_EQUAL oplist Return the associated method to the given operator within the given oplist. ##### M\_BOOL\_OPLIST Default oplist for C standard Boolean. ##### M\_DEFAULT\_OPLIST Default oplist for C standard types (int & float) ##### M\_ENUM\_OPLIST(type, init\_value) Default oplist for a C standard enumerate of type 'type', and of initial value 'init\_value' ##### M\_CSTR\_OPLIST Default oplist for the C type const char *, seen as a constant string. ##### M\_POD\_OPLIST Default oplist for a structure C type without any init & clear prerequisites (plain old data). ##### M\_A1\_OPLIST Default oplist for an array of size 1 of a structure C type without any init & clear prerequisites. ##### M\_EMPTY\_OPLIST Default oplist for a type that shall not be instantiated. Each method does nothing. ##### M\_CLASSIC\_OPLIST(name) Create the oplist with the operators using the pattern 'name', i.e. name##\_init, name##\_init\_set, name##\_set, name##\_clear, name##\_t ##### M\_OPFLAT oplist Remove the parenthesis around an oplist. ##### M\_OPCAT(oplist1,oplist2) Concat two oplists in one. 'oplist1''s operators will have higher priority to 'oplist2' ##### M\_OPEXTEND(oplist, ...) Extend an oplist with the given list of operators. Theses new operators will have higher priority than the ones in the given oplist. ##### M\_TEST\_METHOD\_P(method, oplist) ##### M\_TEST\_METHOD\_ALTER\_P(method, oplist) Test if a method is present in an oplist. Return 0 or 1. M\_TEST\_METHOD\_P does not work if the returned method is something within parenthesis (like OPLIST*) If this case is possible, you shall use the M_TEST_METHOD_ALTER_P macro (safer but slower). ##### M\_IF\_METHOD(method, oplist) Perform a preprocessing M\_IF, if the method is present in the oplist. Example: M\_IF\_METHOD(HASH, oplist)(define function that uses HASH method, ) ##### M\_IF\_METHOD\_BOTH(method, oplist1, oplist2) Perform a preprocessing M\_IF if the method exists in both oplist. Example: M\_IF\_METHOD\_BOTH(HASH, oplist1, oplist2) (define function , ) ##### M\_IF\_METHOD\_ALL(method, ...) Perform a preprocessing M\_IF if the method exists for all oplist. Example: M\_IF\_METHOD\_ALL(HASH, oplist1, oplist2, oplist3) (define function, ) ##### M\_IPTR By putting this after a method for an operator in the oplist, it specifies that the first argument of the method shall be a pointer to the destination type, rather than the type. See M\_API\_2 for an equivalent implementation. ##### M\_DO\_INIT\_MOVE(oplist, dest, src) ##### M\_DO\_MOVE(oplist, dest, src) Perform an INIT\_MOVE/MOVE if present, or emulate it otherwise. Note: default methods for INIT\_MOVE/MOVE are not robust enough yet. ##### M\_GLOBAL\_OPLIST(a) If 'a' is a type that has registered a global oplist, it returns the registered oplist, otherwise it return 'a'. 'a' shall be a type or an oplist. NOTE: It tests the existence of the symbol M\_OPL\_##a(). See M\_OPL\_ to register an oplist to a type. Global registered oplists are limited to typedef types. ##### M\_GLOBAL\_OPLIST\_OR\_DEF(a) If 'a' is a type that has registered a global oplist, it returns the registered oplist, otherwise it return the default oplist M\_DEFAULT\_OPLIST. The return value shall be evaluated once again to get the oplist (this is needed due to technical reasons) like this: M_GLOBAL_OPLIST_OR_DEF(mpz_t)() 'a' shall be a type or an oplist. NOTE: It tests the existence of the symbol M\_OPL\_##a(). See M\_OPL\_ to register an oplist to a type. Global registered oplists are limited to typedef types. #### Syntax enhancing These macros are quite useful to lighten the C style and make full use of the library. ##### M\_EACH(item, container, oplist|type) This macro iterates over the given 'container' of oplist 'oplist' (or of type 'type' with a globally registered oplist) and sets 'item' to reference one different element of the container for each iteration of the loop. 'item' is a created pointer variable to the contained type of the container, only available within the 'for' loop. There can only have one M\_EACH per line. It shall be used after the 'for' C keyword to perform a loop over the container. The order of the iteration depends on the given container. Example: for M_EACH(item, list, LIST_OPLIST) { action; } ##### M\_LET(var1[,var2[,...]], oplist|type) This macro defines the variable 'var1'(resp. var2, ...) of oplist 'oplist' (or of type 'type' with a globally registered oplist). It initializes 'var1' (resp. var2, ...) by calling the initialization method, and clears 'var1' (resp. var2, ...) by calling the clear method when the bracket associated to the M\_LET go out of scope. If 'var1' (resp. var2, ...) has the form (v1, arguments...), then the variable 'v1' will be initialized with the contains of 'arguments...'. If 'arguments' is within parenthesis or if there is more one argument or if the property LET\_AS\_INIT\_WITH is defined, it will use the operator INIT\_WITH if it exists to emplace the variable with the given arguments (The arguments are expected to be compatible with the INIT\_WITH operator). Otherwise it will use the operator INIT\_SET (argument is expected to be the same type as the initialized type). In both cases, the empty initializer INIT operator is not called. LIMITATION: An argument shall not contain any comma or it shall be put between parenthesis. In particular, if the argument is a compound litteral the full compound litteral shall be put between parenthesis and casted to the right type outside the parenthesis (due to the conflict with the INIT\_WITH detection it is needed to put something outside the parenthesis like a cast). An argument shall not have any post effect when it is evaluated. There shall be at most one M\_LET macro per line of source code. Example: M_LET(a, STRING_OPLIST) { do something with a } or M_LET(a, b, c, string_t) { do something with a, b & c } M_LET( (a, ("Hello")), string_t) { do something with a } NOTE: The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function otherwise the clear code of the object won't be called . However, you can use the break instruction to quit the block (the clear function will be executed), and you can chain the M\_LET macro to create several different variables. ##### M\_LET\_IF(init\_code, test\_code, clear\_code [, else\_code] ) This macro defines the variable(s) in 'init\_code', executes the next block of instructions where the variable(s) is(are) used if the initialization succeeds by testing 'test\_code', then it executes the 'clear\_code'. 'test\_code' shall return a boolean indicating if the initialization succeeds (true) or not. If the initialization fails, it won't call the 'clear\_code', but the 'else\_code' if it is present. The syntax of 'init\_code' is the same as a one of a for loop. There shall be at most one M\_LET\_IF macro per line of source code. Example: M_LET_IF(int *p = malloc(100), p!=0, free(p)) { M_LET_IF( /* nothing */ , mtx_lock(&mut)!=thrd_error, mtx_unlock(&mut)) { printf ("OK! Let's use p in a mutex section\n"); } } NOTE: The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function otherwise the clear\_code won't be called . However, you can use the break instruction to quit properly the block (the clear\_code will be executed). You can chain the M\_LET\_IF macro to create several different variables. ##### M\_DEFER(clear\_code) This macro registers the execution of 'clear\_code' when reaching the further closing brace of the next block of instruction. There shall be at most one M\_DEFER macro per line of source code. Example: m_mutex_lock(mut); M_DEFER(m_mutex_unlock(mut)) { // Use of the critical section. } // Now m_mutex_unlock is called NOTE: The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function otherwise the clear\_code won't be called. You can use the break instruction to quit the block (the clear\_code will be executed). #### Memory / Error macros All these macro can be overridden before including the header m-core.h so that they can be adapted to a particular memory pool. ##### type *M\_MEMORY\_ALLOC (type) Return a pointer to a new allocated non-initialized object of type 'type'. In case of allocation error, it returns NULL. The default used function is the 'malloc' function of the LIBC. The user may defined its own implementation of the macro before including any M\*LIB header. ##### void M\_MEMORY\_DEL (type *ptr) Delete the cleared object pointed by the pointer 'ptr' that was previously allocated by the macro M\_MEMORY\_ALLOC. 'ptr' can not be NULL. The default used function is the 'free' function of the LIBC. The user may defined its own implementation of the macro before including any M\*LIB header. ##### type *M\_MEMORY\_REALLOC (type, ptr, number) Return a pointer to an array of 'number' objects of type 'type' 'ptr' is either NULL (in which the array is allocated), or a pointer returned from a previous call of M\_MEMORY\_REALLOC (in which case the array is reallocated). The objects are not initialized, nor the state of previous objects changed (in case of reallocation). The address of the previous objects may have moved and the MOVE operator is not used in this case. In case of allocation error, it returns NULL. The default used function is the 'realloc' function of the LIBC. The user may defined its own implementation of the macro before including any M\*LIB header. ##### void M\_MEMORY\_FREE (type *ptr) Delete the cleared object pointed by the pointer 'ptr'. The pointer was previously allocated by the macro M\_MEMORY\_REALLOC. 'ptr' can not be NULL. The default used function is the 'free' function of the LIBC. The user may defined its own implementation of the macro before including any M\*LIB header. ##### void M\_MEMORY\_FULL (size\_t size) This macro is called by M\*LIB when a memory error has been detected. The parameter 'size' is what was tried to be allocated (as a hint). The default is to abort the execution. The macro can : * abort the execution, * throw an exception (In this case, the state of the object is unchanged), * set a global error variable and return. NOTE: The last two cases are not properly fully supported yet. Throwing an exception is not fully supported yet (Needs M\*LIB support to clear the skipped objects). The user may defined its own implementation of the macro before including any M\*LIB header. ##### void M\_ASSERT\_INIT\_FAILURE(expression, object\_name) This macro is called when an assertion used in an initialization context is called to check the good creation of an object (like a thread, a mutex) that string name is 'object\_name'. If the given 'expression' is false, the execution shall be aborted. The assertion is kept in programs built in release mode. The default is to abort the execution. The user may defined its own implementation of the macro before including any M\*LIB header. #### Generic Serialization objects A [serialization](https://en.wikipedia.org/wiki/Serialization) is the process of translating data structures into a format that can be stored or transmitted. When the resulting format is reread and translated, it creates a semantically identical clone of the original object. A generic serialization object is an object that takes a C object (Boolean, integer, float, structure, union, pointer, array, list, hash-map, ...) and outputs it into a serialization way through the following defined interface that defined the format of the serialization and where it is physically output. The M\*LIB containers define the methods \_out\_serial and \_in\_serial if the underlying types define theses methods over the operators OUT\_SERIAL and IN\_SERIAL. The methods for the basic C types (int, float, ...) are also defined (but only in C11 due to technical limitation). The methods \_out\_serial and \_in\_serial will request the generic serialization object to serialize their current object: * calling the interface of the generic serialization object if needed, * performing recursive call to serialize the contained-objects. The final output of this serialization can be a FILE or a string. Two kinds of serialization objects exist: one for input and one for output. The serialization is fully recursive and can be seen as a collection of token. The only constraint is that what is output by the output serialization object shall be able to be parsed by the input serialization object. The serialization input object is named as m\_serial\_read\_t, defined in m-core.h as a structure (of array of size 1) with the following fields: * m\_interface: a pointer to the constant m\_serial\_read\_interface\_s structure that defines all methods that operate on this object to parse it. The instance has to be customized for the needs of the wanted serialization. * data: a table of M\_SERIAL\_MAX\_DATA\_SIZE of C types (Boolean, integer, size or pointer). This data is used to store the needed data for the methods. This is pretty much like a pure virtual interface object in C++. The interface has to defines the following fields with the following definition: * read\_boolean: Read from the stream 'serial' a boolean. Set '*b' with the boolean value if it succeeds. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise * read\_integer: Read from the stream 'serial' an integer that can be represented with 'size\_of\_type' bytes. Set '*i' with the integer value if it succeeds. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise. * read\_float: Read from the stream 'serial' a float that can be represented with 'size\_of\_type' bytes. Set '*r' with the float value if it succeeds. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise. * read\_string: Read from the stream 'serial' a string. Set 's' with the string if it succeeds. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise * read\_array\_start: Start reading from the stream 'serial' an array (which is defined as a sequential collection of object). Set '*num' with the number of elements, or 0 if it is not known. Initialize the object 'local' so that it can be used by the serialization object to serialize the array. ('local' is an unique local serialization object of the array). Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the parsing of the array can continue (the array is not empty), M\_SERIAL\_OK\_DONE if it succeeds and the array ends (the array is empty), M\_SERIAL\_FAIL otherwise. * read\_array\_next: Continue reading from the stream 'serial' an array using 'local' to load / save data if needed. Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the array can continue (the array end is still not reached), M\_SERIAL\_OK\_DONE if it succeeds and the array ends, M\_SERIAL\_FAIL otherwise. * read\_map\_start: Start reading from the stream 'serial' a map (an associative array). Set '*num' with the number of elements, or 0 if it is not known. Initialize 'local' so that it can be used to serialize the map. ('local' is an unique serialization object of the map). Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the map continue, M\_SERIAL\_OK\_DONE if it succeeds and the map ends (the map is empty), M\_SERIAL\_FAIL otherwise * read\_map\_value: Continue reading from the stream 'serial' the value separator token (if needed) using 'local' to load / save data if needed. Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the map continue, M\_SERIAL\_FAIL otherwise * read\_map\_next: Continue reading from the stream 'serial' a map. using 'local' to load / save data if needed. Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the map continue, M\_SERIAL\_OK\_DONE if it succeeds and the map ends, M\_SERIAL\_FAIL otherwise * read\_tuple\_start: Start reading a tuple from the stream 'serial'. Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the tuple continues, M\_SERIAL\_FAIL otherwise * read\_tuple\_id: Continue reading a tuple (a structure) from the stream 'serial'. using 'local' to load / save data if needed. Set '*id' with the corresponding index of the table 'field\_name[max]' associated to the parsed field in the stream. Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the tuple continues, Return M\_SERIAL\_OK\_DONE if it succeeds and the tuple ends, M\_SERIAL\_FAIL otherwise * read\_variant\_start: Start reading a variant (an union) from the stream 'serial'. Set '*id' with the corresponding index of the table 'field\_name[max]' associated to the parsed field in the stream. Return M\_SERIAL\_OK\_CONTINUE if it succeeds and the variant continues, Return M\_SERIAL\_OK\_DONE if it succeeds and the variant ends(variant is empty), M\_SERIAL\_FAIL otherwise * read\_variant\_end: End reading a variant from the stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds and the variant ends, M\_SERIAL\_FAIL otherwise The serialization output object is named as m\_serial\_write\_t, defined in m-core.h as a structure (of array of size 1) with the following fields: * m\_interface: a pointer to the constant m\_serial\_write\_interface\_s structure that defines all methods that operate on this object to output it. The instance has to be customized for the needs of the wanted serialization. * data: a table of M\_SERIAL\_MAX\_DATA\_SIZE of C types (boolean, integer, size or pointer). This data is used to store the needed data for the methods. This is pretty much like a pure virtual interface object in C++. The interface has to defines the following fields with the following definition: * write\_boolean: Write the boolean 'b' into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_integer: Write the integer 'data' of 'size\_of\_type' bytes into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_float: Write the float 'data' of 'size\_of\_type' bytes into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_string: Write the null-terminated string 'data' of 'length' characters into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_array\_start: Start writing an array of 'number\_of\_elements' objects into the serial stream 'serial'. If 'number\_of\_elements' is 0, then either the array has no data, or the number\ of\ elements of the array is unknown. Initialize 'local' so that it can be used to serialize the array (local is an unique serialization object of the array). Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_array\_next: Write an array separator between elements of an array into the serial stream 'serial' if needed. Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_array\_end: End the writing of an array into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_map\_start: Start writing a map of 'number\_of\_elements' pairs of objects into the serial stream 'serial'. If 'number\_of\_elements' is 0, then either the map has no data, or the number of elements is unknown. Initialize 'local' so that it can be used to serialize the map (local is an unique serialization object of the map). Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_map\_value: Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed. Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_map\_next: Write a map separator between elements of a map into the serial stream 'serial' if needed. Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_map\_end: End the writing of a map into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_tuple\_start: Start writing a tuple into the serial stream 'serial'. Initialize 'local' so that it can serial the tuple (local is an unique serialization object of the tuple). Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_tuple\_id: Start writing the field named field\_name[index] of a tuple into the serial stream 'serial'. Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_tuple\_end: End the write of a tuple into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_variant\_start: Start writing a variant into the serial stream 'serial'. If index <= 0, the variant is empty. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise Otherwise, the field 'field\_name[index]' will be filled. Return M\_SERIAL\_OK\_CONTINUE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_variant\_end: End Writing a variant into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ M\_SERIAL\_MAX\_DATA\_SIZE can be overloaded before including any M\*LIB header to increase the size of the generic object. The maximum default size is 4 fields. The full C definition are: // Serial return code typedef enum m_serial_return_code_e { M_SERIAL_OK_DONE = 0, M_SERIAL_OK_CONTINUE = 1, M_SERIAL_FAIL = 2 } m_serial_return_code_t; // Different types of types that can be stored in a serial object to represent it. typedef union m_serial_ll_u { bool b; int i; size_t s; void *p; } m_serial_ll_t; /* Object to handle the construction of a serial write/read of an object that needs multiple calls (array, map, ...) It is common to all calls to the same object */ typedef struct m_serial_local_s { m_serial_ll_t data[M_USE_SERIAL_MAX_DATA_SIZE]; } m_serial_local_t[1]; // Object to handle the generic serial read of an object. typedef struct m_serial_read_s { const struct m_serial_read_interface_s *m_interface; m_serial_ll_t data[M_USE_SERIAL_MAX_DATA_SIZE]; } m_serial_read_t[1]; // Interface exported by the serial read object. typedef struct m_serial_read_interface_s { m_serial_return_code_t (*read_boolean)(m_serial_read_t serial,bool *b); m_serial_return_code_t (*read_integer)(m_serial_read_t serial, long long *i, const size_t size_of_type); m_serial_return_code_t (*read_float)(m_serial_read_t serial, long double *f, const size_t size_of_type); m_serial_return_code_t (*read_string)(m_serial_read_t serial, struct string_s *s); m_serial_return_code_t (*read_array_start)(m_serial_local_t local, m_serial_read_t serial, size_t *s); m_serial_return_code_t (*read_array_next)(m_serial_local_t local, m_serial_read_t serial); m_serial_return_code_t (*read_map_start)(m_serial_local_t local, m_serial_read_t serial, size_t *); m_serial_return_code_t (*read_map_value)(m_serial_local_t local, m_serial_read_t serial); m_serial_return_code_t (*read_map_next)(m_serial_local_t local, m_serial_read_t serial); m_serial_return_code_t (*read_tuple_start)(m_serial_local_t local, m_serial_read_t serial); m_serial_return_code_t (*read_tuple_id)(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id); m_serial_return_code_t (*read_variant_start)(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id); m_serial_return_code_t (*read_variant_end)(m_serial_local_t local, m_serial_read_t serial); } m_serial_read_interface_t; // Object to handle the generic serial write of an object. typedef struct m_serial_write_s { const struct m_serial_write_interface_s *m_interface; m_serial_ll_t data[M_USE_SERIAL_MAX_DATA_SIZE]; } m_serial_write_t[1]; // Interface exported by the serial write object. typedef struct m_serial_write_interface_s { m_serial_return_code_t (*write_boolean)(m_serial_write_t serial, const bool b); m_serial_return_code_t (*write_integer)(m_serial_write_t serial, const long long i, const size_t size_of_type); m_serial_return_code_t (*write_float)(m_serial_write_t serial, const long double f, const size_t size_of_type); m_serial_return_code_t (*write_string)(m_serial_write_t serial, const char s[], size_t length); m_serial_return_code_t (*write_array_start)(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements); m_serial_return_code_t (*write_array_next)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_array_end)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_map_start)(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements); m_serial_return_code_t (*write_map_value)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_map_next)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_map_end)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_tuple_start)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_tuple_id)(m_serial_local_t local, m_serial_write_t serial, const char * const field_name[], const int max, const int index); m_serial_return_code_t (*write_tuple_end)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_variant_start)(m_serial_local_t local, m_serial_write_t serial, const char * const field_name[], const int max, const int index); m_serial_return_code_t (*write_variant_end)(m_serial_local_t local, m_serial_write_t serial); } m_serial_write_interface_t; See [m-serial-json.h](#m-serial-json) for example of use. ### M-MUTEX This header is for providing very thin layer around OS implementation of threads, conditional variables and mutex. It has back-ends for WIN32, POSIX thread or C11 thread. It was needed due to the low adoption rate of the C11 equivalent layer. It uses the C11 threads.h if possible. If the C11 implementation does not respect the C standard (i.e. the compiler targets C11 mode, the \_\_STDC\_NO\_THREADS\_\_ macro is not defined but the header threads.h is not available or not working), then the user shall define manually the M\_USE\_THREAD\_BACKEND macro to select the back end to use: * 1: for C11 * 2: for WINDOWS * 3: for PTHREAD Example: m_thread_t idx_p; m_thread_t idx_c; m_thread_create (idx_p, conso_function, NULL); m_thread_create (idx_c, prod_function, NULL); m_thread_join (idx_p; m_thread_join (idx_c); #### Attributes The following attributes are available: ##### M\_THREAD\_ATTR An attribute used for qualifying a variable to be thread specific. It is an alias for \_\_thread, \_Thread\_local or \_\_declspec( thread ) depending on the used backend. #### methods The following methods are available: ##### m\_mutex\_t A type representing a mutex. ##### void m\_mutex\_init(mutex) Initialize the variable mutex of type m\_mutex\_t. If the initialization fails, the program aborts. ##### void m\_mutex\_clear(mutex) Clear the variable mutex of type m\_mutex\_t. If the variable is not initialized, the behavior is undefined. ##### void m\_mutex\_lock(mutex) Lock the variable mutex of type m\_mutex\_t for exclusive use. If the variable is not free, it will wait indefinitely until it is. If the variable is not initialized, the behavior is undefined. ##### void m\_mutex\_unlock(mutex) Unlock the variable mutex of type m\_mutex\_t for exclusive use. If the variable is not locked, the behavior is undefined. If the variable is not initialized, the behavior is undefined. #### m\_cond\_t A type representing a conditional variable, used within a mutex section. ##### void m\_cond\_init(m\_cond\_t cond) Initialize the conditional variable cond of type m\_cond\_t. If the initialization failed, the program aborts. ##### void m\_cond\_clear(m\_cond\_t cond) Clear the variable cond of type m\_cond\_t. If the variable is not initialized, the behavior is undefined. ##### void m\_cond\_signal(m\_cond\_t cond) Within a mutex exclusive section, signal that the event associated to the variable cond of type m\_cond\_t has occurred to at least a single thread. If the variable is not initialized, the behavior is undefined. ##### void m\_cond\_broadcast(m\_cond\_t cond) Within a mutex exclusive section, signal that the event associated to the variable cond of type m\_cond\_t has occurred to all waiting threads. If the variable is not initialized, the behavior is undefined. ##### void m\_cond\_wait(m\_cond\_t cond, m\_mutex\_t mutex) Within a mutex exclusive section defined by mutex, wait indefinitely for the event associated to the variable cond of type m\_cond\_t to occur. IF multiple threads wait for the same event, which thread to awoken is not specified. If any variable is not initialized, the behavior is undefined. #### m\_thread\_t A type representing an id of a thread. ##### void m\_thread\_create(m\_thread\_t thread, void (\*function)(void\*), void \*argument) Create a new thread and set the it of the thread to 'thread'. The new thread run the code function(argument) with argument a 'void \*' and function taking a 'void \*' and returning nothing. If the initialization fails, the program aborts. ##### void m\_thread\_join(m\_thread\_t thread) Wait indefinitely for the thread 'thread' to exit. ### M-WORKER This header is for providing a pool of workers. Each worker run in a separate thread and can handle work orders sent by the main threads. A work order is a computation task. Work orders are organized around synchronization points. Workers can be disabled globally to ease debugging. This implements parallelism just like OpenMP or CILK++. Example: worker_t worker; worker_init(worker, 0, 0, NULL); worker_sync_t sync; worker_start(sync, worker); void *data = ...; worker_spawn (sync, taskFunc, data); taskFunc(otherData); worker_sync(sync); Currently, there is no support for: * throw exceptions by the worker tasks, * unbalanced design: the worker tasks shall not lock a mutex without closing it (same for other synchronization structures). Thread Local Storage variables have to be reinitialized properly with the reset function. This may result in subtle difference between the serial code and the parallel code. #### methods The following methods are available: #### worker\_t A pool of worker. #### worker\_sync\_t A synchronization point between workers. #### void worker\_init(worker\_t worker[, unsigned int numWorker, unsigned int extraQueue, void (*resetFunc)(void), void (*clearFunc)(void) ]) Initialize the pool of workers 'worker' with 'numWorker' workers. if 'numWorker' is 0, then it will detect how many core is available on the system and creates as much workers as there are cores. Before starting any work, the function 'resetFunc' is called by the worker to reset its state (or call nothing if the function pointer is NULL). 'extraQueue' is the number of tasks that can be accepted by the work order queue in case if there is no worker available. Before terminating, each worker will call 'clearFunc' if the function is not NULL. Default values are respectively 0, 0, NULL and NULL. #### void worker\_clear(worker\_t worker) Request termination to the pool of workers, and wait for them to terminate. It is undefined if there is any work order in progress. #### void worker\_start(worker\_block\_t syncBlock, worker\_t worker) Start a new synchronization block for a pool of work orders linked to the pool of worker 'worker'. #### void worker\_spawn(worker\_block\_t syncBlock, void (*func)(void *data), void *data) Register the work order 'func(data)' to the the synchronization point 'syncBlock'. If no worker is available (and no extraQueue), the work order 'func(data)' will be handled by the caller. Otherwise the work order 'func(data)' will be handled by an asynchronous worker and the function immediately returns. #### bool worker\_sync\_p(worker\_block\_t syncBlock) Test if all work orders registered to this synchronization point are terminated (true) or not (false). #### void worker\_sync(worker\_block\_t syncBlock) Wait for all work orders registered to this synchronization point 'syncBlock' to be terminated. #### size\_t worker\_count(worker\_t worker) Return the number of workers of the pool. #### WORKER\_SPAWN(syncBlock, input, core, output) Request the work order 'core' to the synchronization point syncBlock. If no worker is available (and no extra queue), the work order 'core' will be handled by the caller. Otherwise the work order 'core' will be handled by an asynchronous worker. 'core' is any C code that doesn't break the control flow (you cannot use return / goto / break to go outside the flow). 'input' is the list of local input variables of the 'core' block within "( )". 'output' is the list of local output variables of the 'core' block within "( )". These lists shall be exhaustive to capture all needed variables. This macro needs either GCC (for nested function) or CLANG (for blocks) or a C++11 compiler (for lambda and functional) to work. NOTE1: Even if nested functions are used for GCC, it doesn't generate a trampoline and the stack doesn't need to be executable as all variables are captured by the library. NOTE2: For CLANG, you need to add -fblocks to CFLAGS and -lBlocksRuntime to LIB (See CLANG manual). NOTE3: It will generate warnings about shadowed variables. There is no way to avoid this. NOTE4: arrays and not trivially movable object are not supported as input / output variables due to current technical limitations. ### M-ATOMIC This header goal is to provide the C header 'stdatomic.h' to any C compiler (C11 or C99 compliant) or C++ compiler. If available, it uses the C11 header stdatomic.h, otherwise if the compiler is a C++ compiler, it uses the header 'atomic' and imports all definition into the global name space (this is needed because the C++ standard doesn't support officially the stdatomic header, resulting in broken compilation when building C code with a C++ compiler). Otherwise it provides a non-thin emulation of atomic using mutex. This emulation has a known theoretical limitation: the mutex are never cleared. There is nothing to do to fix this. In practice, it is not a problem. ### M-ALGO This header is for generating generic algorithm to containers. #### ALGO\_DEF(name, container\_oplist) Define the available algorithms for the container which oplist is container\_oplist. The defined algorithms depend on the availability of the methods of the containers (For example, if there no CMP operator, there is no MIN method defined). Example: ARRAY_DEF(array_int, int) ALGO_DEF(array_int, ARRAY_OPLIST(array_int)) void f(void) { array_int_t l; array_int_init(l); for(int i = 0; i < 100; i++) array_int_push_back (l, i); array_int_push_back (l, 17); assert( array_int_contains(l, 62) == true); assert( array_int_contains(l, -1) == false); assert( array_int_count(l, 17) == 2); array_int_clear(l); } #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. In the following descriptions, it\_t is an iterator of the container container\_t is the type of the container and type\_t is the type of object contained in the container. ##### void name\_find(it\_t it, const container\_t c, const type\_t data) Search for the first occurrence of 'data' within the container. Update the iterator with the found position or return the end iterator. The search is linear. ##### void name\_find\_again(it\_t it, const type\_t data) Search from the position 'it' for the next occurrence of 'data' within the container. Update the iterator with the found position or return end iterator. The search is linear. ##### void name\_find\_if(it\_t it, const container\_t c, bool (*pred)(type\_t const)) Search for the first occurrence within the container than matches the predicate 'pred' Update the iterator with the found position or return end iterator. The search is linear. ##### void name\_find\_again\_if(it\_t it, bool (*pred)(type\_t const)) Search from the position 'it' for the next occurrence matching the predicate 'pred' within the container. Update the iterator with the found position or return end iterator. The search is linear. ##### void name\_find\_last(it\_t it, const container\_t c, const type\_t data) Search for the last occurrence of 'data' within the container. Update the iterator with the found position or return end iterator. The search is linear and can be backward or forwards depending on the possibility of the container. ##### bool name\_contains(const container\_t c, const type\_t data) Return true if 'data' is within the container, false otherwise. The search is linear. ##### size\_t name\_count(const container\_t c, const type\_t data) Return the number of occurrence of 'data' within the container. The search is linear. ##### size\_t name\_count\_if(const container\_t c, bool (*pred)(type\_t const data)) Return the number of occurrence matching the predicate 'pred' within the container. The search is linear. ##### void name\_mismatch(it\_t it1, it\_t it2, const container\_t c1, const container\_t c2) Returns the first mismatching pair of elements of the two containers 'c1' and 'c2'. ##### void name\_mismatch\_again(it\_t it1, it\_t it2) Returns the next mismatching pair of elements of the two containers from the position 'it1' of container 'c1' and from the position 'it2' of container 'c2'. ##### void name\_mismatch\_if(it\_t it1, it\_t it2, const container\_t c1, const container\_t c2, bool (*cmp)(type\_t const, type\_t const)) Returns the first mismatching pair of elements of the two containers using the provided comparison 'cmp'. ##### void name\_mismatch\_again\_if(it\_t it1, it\_t it2, bool (*cmp)(type\_t const, type\_t const)) Returns the next mismatching pair of elements of the two containers using the provided comparison 'cmp' from the position 'it1' and from the position 'it2'. ##### void name\_fill(container\_t c, type\_t value) Set all elements of the container 'c' to 'value'. ##### void name\_fill\_n(container\_t c, size\_t n, type\_t value) Set the container to 'n' elements equal to 'value'. This method is defined only if the container exports a PUSH method. ##### void name\_fill\_a(container\_t c, type\_t value, type\_t inc) Set all elements of the container 'c' to 'value + i * inc' with i = 0.. size(c) This method is defined only if the base type exports an ADD method. This method is defined only if the container exports a PUSH method. ##### void name\_fill\_an(container\_t c, size\_t n, type\_t value) Set the container to 'n' elements to 'value + i * inc' with i = 0..n This method is defined only if the base type exports an ADD method. This method is defined only if the container exports a PUSH method. ##### void name\_for\_each(container\_t c, void (*func)(type\_t)) Apply the function 'func' to each element of the container 'c'. The function may transform the element provided it doesn't reallocate the object and if the container supports iterating over modifiable elements. ##### void name\_transform(container\_t d, container\_t c, void (*func)(type\_t *out, const type\_t in)) Apply the function 'func' to each element of the container 'c' and push the result into the container 'd' so that 'd = func(c)' 'func' shall output in the initialized object 'out' the transformed value of the constant object 'in'. Afterwards 'out' is pushed moved into 'd'. This method is defined only if the base type exports an INIT method. This method is defined only if the container exports a PUSH\_MOVE method. 'c' and 'd' cannot be the same containers. ##### void name\_reduce(type\_t *dest, const container\_t c, void (*func)(type\_t *, type\_t const)) Perform a reduction using the function 'func' to the elements of the container 'c'. The final result is stored in '*dest'. If there is no element, '*dest' is let unmodified. ##### void name\_map\_reduce(type\_t *dest, const container\_t c, void (*redFunc)(type\_t *, type\_t const), void *(mapFunc)(type\_t *, type\_t const)) Perform a reduction using the function 'redFunc' to the transformed elements of the container 'c' using 'mapFunc'. The final result is stored in '*dest'. If there is no element, '*dest' is let unmodified. ##### bool name\_any\_of\_p(const container\_t c, void *(func)(const type\_t)) Test if any element of the container 'c' matches the predicate 'func'. ##### bool name\_all\_of\_p(const container\_t c, void *(func)(const type\_t)) Test if all elements of the container 'c' match the predicate 'func'. ##### bool name\_none\_of\_p(const container\_t c, void *(func)(const type\_t)) Test if no element of the container 'c' match the predicate 'func'. ##### type\_t *name\_min(const container\_t c) Return a reference to the minimum element of the container 'c'. Return NULL if there is no element. This method is available if the CMP operator has been defined. ##### type\_t *name\_max(const container\_t c) Return a reference to the maximum element of the container 'c'. Return NULL if there is no element. This method is available if the CMP operator has been defined. ##### void name\_minmax(type\_t **min, type\_t **max, const container\_t c) Stores in '*min' a reference to the minimum element of the container 'c'. Stores in '*max' a reference to the minimum element of the container 'c'. Stores NULL if there is no element. This method is available if the CMP operator has been defined. ##### void name\_uniq(container\_t c) Assuming the container 'c' has been sorted, remove any duplicate elements of the container. This method is available if the CMP and IT\_REMOVE operators have been defined. ##### void name\_remove\_val(container\_t c, type\_t val) Remove all elements equal to 'val' of the container. This method is available if the CMP and IT\_REMOVE operators have been defined. ##### void name\_remove\_if(container\_t c, bool (*func)(type\_t) ) Remove all elements matching the given condition (function func() returns true) of the container. This method is available if the CMP and IT\_REMOVE operators have been defined. ##### void name\_add(container\_t dest, const container\_t value) For each element of the container 'dest', add the corresponding element of the container 'dest' up to the minimum size of the containers. This method is available if the ADD operator has been defined. ##### void name\_sub(container\_t dest, const container\_t value) For each element of the container 'dest', sub the corresponding element of the container 'dest' up to the minimum size of the containers. This method is available if the SUB operator has been defined. ##### void name\_mul(container\_t dest, const container\_t value) For each element of the container 'dest', mul the corresponding element of the container 'dest' up to the minimum size of the containers. This method is available if the MUL operator has been defined. ##### void name\_div(container\_t dest, const container\_t value) For each element of the container 'dest', div the corresponding element of the container 'dest' up to the minimum size of the containers. This method is available if the DIV operator has been defined. ##### bool void name\_sort\_p(const container\_t c) Test if the container 'c' is sorted (=true) or not (=false). This method is available if the CMP operator has been defined. ##### bool name\_sort\_dsc\_p(const container\_t c) Test if the container 'c' is reverse sorted (=true) or not (=false) This method is available if the CMP operator has been defined. ##### void void name\_sort(container\_t c) Sort the container 'c'. This method is available if the CMP operator has been defined. The used algorithm depends on the available operators: if a specialized SORT operator is defined for the container, it is used. if SPLICE\_BACK and SPLICE\_AT operates are defined, a merge sort is defined, if IT\_PREVIOUS is defined, an insertion sort is used, otherwise a selection sort is used. ##### bool name\_sort\_dsc(const container\_t c) Reverse sort the container 'c'. This method is available if the CMP operator has been defined. The used algorithm depends on the available operators: if a specialized SORT operator is defined, it is used. if SPLICE\_BACK and SPLICE\_AT operates are defined, a merge sort is defined, if IT\_PREVIOUS is defined, an insertion sort is used, otherwise a selection sort is used. ##### void name\_sort\_union(container\_t c1, const container\_t c2) Assuming both containers 'c1' and 'c2' are sorted, perform an union of the containers in 'c1'. This method is available if the IT\_INSERT operator is defined. ##### void name\_sort\_dsc\_union(container\_t c1, const container\_t c2) Assuming both containers 'c1' and 'c2' are reverse sorted, perform an union of the containers in 'c2'. This method is available if the IT\_INSERT operator is defined. ##### void name\_sort\_intersect(container\_t c1, const container\_t c2) Assuming both containers 'c1' and 'c2' are sorted, perform an intersection of the containers in 'c1'. This method is available if the IT\_REMOVE operator is defined. ##### void name\_sort\_dsc\_intersect(container\_t c, const container\_t c) Assuming both containers 'c1' and 'c2' are reverse sorted, perform an intersection of the containers in 'c1'. This method is available if the IT\_REMOVE operator is defined. ##### void name\_split(container\_t c, const string\_t str, const char sp) Split the string 'str' around the character separator 'sp' into a set of string in the container 'c'. This method is defined if the base type of the container is a string\_t type, ##### void name\_join(string\_t dst, container\_t c, const string\_t str) Join the string 'str' and all the strings of the container 'c' into 'dst'. This method is defined if the base type of the container is a string\_t type, #### ALGO\_FOR\_EACH(container, oplist, func[, arguments..]) Apply the function 'func' to each element of the container 'container' of oplist 'oplist' : for each item in container do func([arguments,] item) The function 'func' is a method that takes as argument an object of the container and returns nothing. It may update the object provided it doesn't reallocate it. #### ALGO\_TRANSFORM(contDst, contDstOplist, contSrc, contSrcOplist, func[, arguments..]) Apply the function 'func' to each element of the container 'contSrc' of oplist 'contSrcOplist' and store its outptut in the container 'contDst' of oplist 'contDstOplist' so that 'contDst = func(contSrc)'. Exact algorithm is: clean(contDst) for each item in do init(tmp) func(tmp, item, [, arguments]) push_move(contDst, tmp) The function 'func' is a method that takes as first argument the object to put in the new container, and as second argument the object in the source container. #### ALGO\_EXTRACT(containerDest, oplistDest, containerSrc, oplistSrc[, func[, arguments..]]) Extract the items of the container 'containerSrc' of oplist 'oplistSrc' into the 'containerDest' of oplist 'oplistDest': RESET (containerDest) for each item in containerSrc do [ if func([arguments,] item) ] Push item in containerDest The optional function 'func' is a predicate that takes as argument an object of the container and returns a boolean that is true if the object has to be added to the other container. Both containers shall either provide PUSH method, or SET\_KEY method. #### ALGO\_REDUCE(dest, container, oplist, reduceFunc[, mapFunc[, arguments..]) Reduce the items of the container 'container' of oplist 'oplist' into a single element by applying the reduce function: dest = reduceFunc(mapFunc(item[0]), reduceFunc(..., reduceFunc(mapFunc(item[N-2]), mapFunc(item[N-1])))) 'mapFunc' is a method which prototype is: void mapFunc(dest, item) with both 'dest' & 'item' that are of the same type than the one of the container. It transforms the 'item' into another form that is suitable for the 'reduceFunc'. If 'mapFunc' is not specified, identity will be used instead. 'reduceFunc' is a method which prototype is: void reduceFunc(dest, item) It integrates the new 'item' into the partial 'sum' 'dest. The reduce function can be the special keywords 'add', 'sum', 'and', 'or' 'product', 'mul' in which case the special function performing a sum/sum/and/or/mul/mul operation will be used. 'dest' can be either the variable (in which case 'dest' is assumed to be of type equivalent to the elements of the container) or a tuple ('var', 'oplist') with 'var' being the variable name and 'oplist' its oplist (with TYPE, INIT, SET methods). The tuple may be needed if the map function transform a type into another. #### ALGO\_INSERT\_AT(containerDst, containerDstOPLIST, position, containerSrc, containerSrcOPLIST) Insert into the container 'contDst' at position 'position' all the values of container 'contSrc'. ### M-FUNCOBJ This header is for generating Function Object. A [function object](https://en.wikipedia.org/wiki/Function_object) is a construct an object to be invoked or called as if it were an ordinary function, usually with the same syntax (a function parameter that can also be a function) but with additional "within" parameters. Example: // Define generic interface of a function int --> int FUNC_OBJ_ITF_DEF(interface, int, int) // Define one instance of such interface FUNC_OBJ_INS_DEF(sumint, interface, x, { return self->sum + x; }, (sum, int) ) int f(interface_t i) { return interface_call(i, 4); } int h(void) { sumint_t s; sumint_init_with(s, 16); int n = f(sumint_as_interface(s)); printf("sum=%d\n", n); sumint_clear(s); } #### FUNC\_OBJ\_ITF\_DEF(name, retcode\_type[, type\_of\_param1, type\_of\_param 2, ...]) #### FUNC\_OBJ\_ITF\_DEF\_AS(name, name\_t, retcode\_type[, type\_of\_param1, type\_of\_param 2, ...]) Define a function object interface of name 'name' emulating a function pointer returning retcode\_type (which can be void), and with as inputs the list of types of paramN, thus generating a function prototype like this: retcode_type function(type_of_param1, type_of_param 2, ...) An interface cannot be used without an instance (see below) that implements this interface. In particular, there is no init nor clear function for an interface (only an instance provides such initialization). FUNC\_OBJ\_ITF\_DEF\_AS is the same as FUNC\_OBJ\_ITF\_DEF except the name of the type name\_t is provided. It will define the following type and functions: ##### name\_t Type representing an interface to such function object. There is only one method for such type (see below). ##### retcode\_type name\_call(name\_t interface, type\_of\_param1, type\_of\_param 2, ...) The call function of the interface object. It will call the particular implemented callback of the instance of this interface. It shall only be used by an inteface object derived from an instance. #### FUNC\_OBJ\_INS\_DEF(name, interface\_name, (param\_name\_1, ...), { callback\_core }, (self\_member1, self\_type1[, self\_oplist1]), ...) #### FUNC\_OBJ\_INS\_DEF\_AS(name, name\_t, interface\_name, (param\_name\_1, ...), { callback\_core }, (self\_member1, self\_type1[, self\_oplist1]), ...) Define a function object instance of name 'name' implementing the interface 'interface\_name' (it is the same as used as name in FUNC\_OBJ\_ITF\_DEF). The function instance is defined as per : - the function prototype of the inherited interface, - the parameters of the function are named as per the list param\_name\_list, - the core of the function shall be defined in the brackets within the callback\_core. The members of the function object can be accessed through the pointer named 'self' to access the member attributes of the object (without any cast), and the parameter names of the function shall be accessed as per their names in the param\_name\_list. - optional member attributes of the function object can be defined after the core (just like for tuple & variant): Each parameter is defined as a triplet: (name, type [, oplist]) It generates a function that looks like: interface_name_retcode_type function(interface_name_t *self, interface_name_type_of_param1 param_name_1, interface_name_type_of_param 2 param_name_2, ...) { callback_core } FUNC\_OBJ\_INS\_DEF\_AS is the same as FUNC\_OBJ\_INS\_DEF except the name of the type name\_t is provided. ##### name\_t Name of a particular instance to the interface of the Function Object interface\_name. ##### void name\_init(name\_t self) Initialize the instance of the function with default value. This method is defined only if all member attributes export an INIT method. If there is no member, the method is defined. ##### void name\_init\_with(name\_t self, self\_type1 a1, self\_type2 a2, ...) Initialize the instance of the function with the given values of the member attributes. ##### void name\_clear(name\_t self) Clear the instance of the function. ##### interface\_name\_t name\_as\_interface(name\_t self) Return the interface object derived from this instance. This object can then be transmitted to any function that accept the generic interface (mainly \_call). ### M-MEMPOOL This header is for generating specialized and optimized memory pools: it will generate specialized functions to allocate and free only one kind of an object. The mempool functions are not specially thread safe for a given mempool, but the mempool variable can have the attribute M\_THREAD\_ATTR so that each thread has its own instance of the mempool. The memory pool has to be initialized and cleared like any other variable. Clearing the memory pool will free all the memory that has been allocated within this memory pool. #### MEMPOOL\_DEF(name, type) Generate specialized functions & types prefixed by 'name' to alloc & free an object of type 'type'. Example: MEMPOOL_DEF(mempool_uint, unsigned int) mempool_uint_t m; void f(void) { mempool_uint_init(m); unsigned int *p = mempool_uint_alloc(m); *p = 17; mempool_uint_free(m, p); mempool_uint_clear(m); } #### Created methods The following methods are automatically and properly created by the previous macros. In the following methods, name stands for the name given to the macro that is used to identify the type. ##### name\_t The type of a mempool. ##### void name\_init(name\_t m) Initialize the mempool 'm'. ##### void name\_clear(name\_t m) Clear the mempool 'm'. All allocated objects associated to the this mempool that weren't explicitly freed will be deleted too (without calling their clear method). ##### type *name\_alloc(name\_t m) Create a new object of type 'type' and return a new pointer to the uninitialized object. ##### void name\_free(name\_t m, type *p) Free the object 'p' created by the call to name\_alloc. The clear method of the type is not called. ### M-SERIAL-JSON This header is for defining an instance of the serial interface supporting import (and export) of a container from (to) to a file in [JSON](https://en.wikipedia.org/wiki/JSON) format. It uses the generic serialization ability of M\*LIB for this purpose, providing a specialization of the serialization for JSON over FILE*. Another way of seeing it is that you define your data structure using M\*LIB containers (building it using basic types, strings, tuples, variants, array, dictionaries, ...) and then you can import / export your data structure for free in JSON format. If the JSON file cannot be translated into the data structure, a failure error is reported (M\_SERIAL\_FAIL). For example, if some new fields are present in the JSON file but not in the data structure. On contrary, if some fields are missing (or in a different order) in the JSON file, the parsing will still succeed (object fields are unmodified except for new sub-objects, for which default value are used). It is fully working with C11 compilers only. #### C functions on FILE ##### m\_serial\_json\_write\_t A synonym of m\_serial\_write\_t with a global oplist registered for use with JSON over FILE*. ##### void m\_serial\_json\_write\_init(m\_serial\_write\_t serial, FILE *f) Initialize the 'serial' object to be able to output in JSON format to the file 'f'. The file 'f' shall remain open in 'wt' mode while the 'serial' is not cleared. ##### void m\_serial\_json\_write\_clear(m\_serial\_write\_t serial) Clear the serialization object 'serial'. ##### m\_serial\_json\_read\_t A synonym of m\_serial\_read\_t with a global oplist registered for use with JSON over FILE *. ##### void m\_serial\_json\_read\_init(m\_serial\_read\_t serial, FILE *f) Initialize the 'serial' object to be able to parse in JSON format from the file 'f'. The file 'f' shall remain open in 'rt' mode while the 'serial' is not cleared. ##### void m\_serial\_json\_read\_clear(m\_serial\_read\_t serial) Clear the serialization object 'serial'. #### C functions on string ##### m\_serial\_str\_json\_write\_t A synonym of m\_serial\_write\_t with a global oplist registered for use with JSON over string\_t. ##### void m\_serial\_str\_json\_write\_init(m\_serial\_write\_t serial, string\_t str) Initialize the 'serial' object to be able to output in JSON format to the string\_t 'str'. The string 'str' shall remain initialized while the 'serial' object is not cleared. ##### void m\_serial\_str\_json\_write\_clear(m\_serial\_write\_t serial) Clear the serialization object 'serial'. ##### m\_serial\_str\_json\_read\_t A synonym of m\_serial\_read\_t with a global oplist registered for use with JSON over const string (const char*). ##### void m\_serial\_str\_json\_read\_init(m\_serial\_read\_t serial, const char str[]) Initialize the 'serial' object to be able to parse in JSON format from the const string 'str'. The const string 'str' shall remain initialized while the 'serial' object is not cleared. ##### const char * m\_serial\_str\_json\_read\_clear(m\_serial\_read\_t serial) Clear the serialization object 'serial' and return a pointer to the first unparsed character in the const string. Example: // Define a structure of two fields. TUPLE_DEF2(my, (value, int), (name, string_t) ) #define M_OPL_my_t() TUPLE_OPLIST(my, M_DEFAULT_OPLIST, STRING_OPLIST ) // Output in JSON file the structure my_t void output(my_t el1) { m_serial_write_t out; m_serial_return_code_t ret; FILE *f = fopen ("data.json", "wt"); if (!f) abort(); m_serial_json_write_init(out, f); ret = my2_out_serial(out, el1); assert (ret == M_SERIAL_OK_DONE); m_serial_json_write_clear(out); fclose(f); } // Get from JSON file the structure my_t void input(my_t el1) { m_serial_read_t in; m_serial_return_code_t ret; f = fopen ("data.json", "rt"); if (!f) abort(); m_serial_json_read_init(in, f); ret = my2_in_serial(el2, in); assert (ret == M_SERIAL_OK_DONE); m_serial_json_read_clear(in); fclose(f); } ### M-SERIAL-BIN This header is for defining an instance of the serial interface supporting import (and export) of a container from (to) to a file in an ad-hoc binary format. This format only supports the current system and cannot be used to communicate across multiple systems (endianess, size of types are typically not abstracted by this format). It uses the generic serialization ability of M\*LIB for this purpose, providing a specialization of the serialization for JSON over FILE*. It is fully working with C11 compilers only. #### C functions ##### void m\_serial\_bin\_write\_init(m\_serial\_write\_t serial, FILE *f) Initialize the 'serial' object to be able to output in BIN format to the file 'f'. The file 'f' has to remained open in 'wb' mode while the 'serial' is not cleared otherwise the behavior of the object is undefined. ##### void m\_serial\_bin\_write\_clear(m\_serial\_write\_t serial) Clear the serialization object 'serial'. ##### void m\_serial\_bin\_read\_init(m\_serial\_read\_t serial, FILE *f) Initialize the 'serial' object to be able to parse in BIN format from the file 'f'. The file 'f' has to remained open in 'rb' mode while the 'serial' is not cleared otherwise the behavior of the object is undefined. ##### void m\_serial\_bin\_read\_clear(m\_serial\_read\_t serial) Clear the serialization object 'serial'. # Global User Customization The behavior of M\*LIB can be customized globally by defining the following macros before including any headers of M\*LIB. If a macro is not defined before including any M\*LIB header, the default value will be used. Theses macros shall not be defined after including any M\*LIB header. ### M\_USE\_UNDEF\_ATOMIC Undefine the macro _Atomic in m-atomic.h if stdatomic.h is included. It is needed on MSYS2 due to a bug in their headers which is not fixed yet. Default value: 1 (undef) on MSYS2, 0 otherwise. ### M\_USE\_STDIO This macro indicates if the system header stdio.h shall be included and the FILE functions be defined (=1) or not (=0). Default value: 1 ### M\_USE\_STDARG This macro indicates if the system header stdarg.h shall be included and the va\_args functions be defined (=1) or not (=0). Default value: 1 (true) ### M\_USE\_CSTR\_ALLOC Define the allocation size of the temporary strings created by M\_CSTR (including the final nul char). Default value: 256. ### M\_USE\_IDENTIFIER\_ALLOC Define the allocation size of a C identifier in the source code (excluding the final nul char). It is used to represent a C identifier by a C string. Default value: 128 ### M\_USE\_THREAD\_BACKEND Define the thread backend to use by m-mutex.h: * 1: for C11 header threads.h * 2: for WINDOWS header windows.h * 3: for POSIX THREAD header pthread.h Default value: autodetect in function of the running system. ### M\_USE\_WORKER This macro indicates if the multi-thread code of m-worker.h shall be used (=1) or not (=0) - In this case, a single-thread code is used -. Default value: 1 ### M\_USE\_WORKER\_CLANG\_BLOCK This macro indicates if the workers shall use the CLANG block extension (=1) or not (=0). Default value: 1 (on clang), 0 (otherwise) ### M\_USE\_WORKER\_CPP\_FUNCTION This macro indicates if the workers shall use the C++ lambda function (=1) or not (=0). Default value: 1 (compiled in C++), 0 (otherwise) ### M\_USE\_BACKOFF\_MAX\_COUNT Define the maximum iteration of the backoff exponential scheme for the synchronization waiting loop of multithreading code. Default value: 6 ### M\_USE\_SERIAL\_MAX\_DATA\_SIZE Define the size of the private data (reserved to the serial implementation) in a serial object (as a number of pointers or equivalent objects). Default value: 4 ### M\_USE\_MEMPOOL\_MAX\_PER\_SEGMENT(type) Define the number of elements to allocate in a segment per object of type 'type'. Default value: number of elements that fits in a 16KB page. ### M\_USE\_DEQUE\_DEFAULT\_SIZE Define the default size of a segment for a deque structure. Default value: 8 elements. ### M\_USE\_HASH\_SEED Define the seed to inject to the hash computation of an object. Default value: 0 (predictable hash) ### M\_MEMORY\_ALLOC See [m-core.h](#m-core) ### M\_MEMORY\_REALLOC See [m-core.h](#m-core) ### M\_MEMORY\_FULL See [m-core.h](#m-core) ### M\_ASSERT See [m-core.h](#m-core) ### M\_ASSERT\_SLOW See [m-core.h](#m-core) ### M\_ASSERT\_INIT See [m-core.h](#m-core) ### M\_ASSERT\_INDEX See [m-core.h](#m-core) # License All files of M\*LIB are distributed under the following license. Copyright (c) 2017-2021, 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.