346 Commits
Author SHA1 Message Date
Patrick Pelissier f0d3e93d9d Tune for Windows CI: C11 is broken, so switch to C99 and requests ISO C compatible printf function. 2019-12-16 12:44:56 +01:00
Patrick Pelissier e77ff2451b Try setup CCI for windows 2019-12-16 12:31:10 +01:00
Patrick Pelissier 142c224f84 Add another example on complex structure usage 2019-12-15 19:53:46 +01:00
Patrick Pelissier 29e8d9199d Update doc about new API 2019-12-15 19:43:38 +01:00
Patrick Pelissier 7128f5add2 * Add support for API_6 & API_7. * Fix issue for DO_INIT_MOVE that doesn't support API calls 2019-12-15 19:43:26 +01:00
Patrick Pelissier 1b7bcda4e1 Add quiet mode. Add usage 2019-12-15 10:30:40 +01:00
Patrick Pelissier 03b6146ef0 Add option to print only the best or only the average 2019-12-15 10:12:38 +01:00
Patrick Pelissier e99735d2b6 Add repeat option for bench 2019-12-15 10:01:32 +01:00
Patrick Pelissier 7263629c55 Add rigtop hashmap 2019-12-14 20:59:01 +01:00
Patrick Pelissier 605ce9b938 * Update version * Update comments * Improve robustness 2019-12-14 11:12:59 +01:00
Patrick Pelissier 049d8b6ca6 Port to new libdynamic API 2019-12-13 23:00:57 +01:00
Patrick Pelissier 90d3dda830 Clean up generated plot files too 2019-12-09 07:53:43 +01:00
Patrick Pelissier 2022a66db3 * Update makefile (missing coverage) * Refactor 2019-12-08 22:17:56 +01:00
Patrick Pelissier 78c786070e Add coverage support 2019-12-08 22:17:27 +01:00
Patrick Pelissier c5c72007af Update list of files to distribute 2019-12-08 21:53:20 +01:00
Patrick Pelissier c60d59cb7a Port all benchs to new infrastructure 2019-12-07 19:21:21 +01:00
Patrick Pelissier 00111e3e3c Port more bench to new infrzastructure 2019-12-07 14:45:06 +01:00
Patrick Pelissier 5cc2ac17d9 Add support for generating gnuplot graph 2019-12-07 12:22:33 +01:00
Patrick Pelissier d37ea84ab5 Cleanup redundant 'time' word 2019-12-07 11:59:23 +01:00
Patrick Pelissier 5e335bc8bd * Port to generic parser 2019-12-07 11:58:47 +01:00
Patrick Pelissier 65765763ae * Add generic parser for bench. * Factorization 2019-12-07 11:58:19 +01:00
Patrick Pelissier 90f248fbe8 Rename interface field of serial object into m_interface (WINDOWS compatibility as it defines a macro called interface) 2019-12-06 21:13:37 +01:00
Patrick Pelissier a39cf23697 Remove a warning (WINDOWS) 2019-12-06 21:10:00 +01:00
Patrick Pelissier 261776762c Update debugging documentation 2019-12-02 23:38:29 +01:00
Patrick Pelissier f207f9441c Remove linker warning for examples 2019-11-30 00:33:53 +01:00
Patrick Pelissier ffed880e0a Fix static assert for C++ (disable them totally as static_assert doesn't work -- It can't be used in expressions) 2019-11-30 00:08:59 +01:00
Patrick Pelissier a79b67cea2 Generate better error message in case of missing methods in OPLIST for GCC/CLANG 2019-11-29 23:45:48 +01:00
Patrick Pelissier bc3c8b5bcd Fix M_DO_INIT_MOVE / M_DO_MOVE to be more conservative. M_MOVE_DEFAULT doesn't work for all types ([1] structure). A MOVE operator should be defined however. 2019-11-29 23:23:27 +01:00
Patrick Pelissier 3489bb42bc Add static assertion and use it for move/copy/cmp default 2019-11-29 23:13:28 +01:00
Patrick Pelissier 7fe36f7bd7 Add test for M_MOVE_DEFAULT 2019-11-29 23:12:34 +01:00
Patrick Pelissier 82fd4c94db Update CI (Simplify CI tests) 2019-11-25 23:09:54 +01:00
Patrick Pelissier 4fc48b490e Update CI 2019-11-25 23:05:25 +01:00
Patrick Pelissier 750e5e6d95 Add target checkall at root Makefile 2019-11-25 22:50:50 +01:00
Patrick Pelissier cb564128e6 Update doc 2019-11-24 09:16:43 +01:00
PpHd 047fc986f2 Setup github workflows for Continuous Integration 2019-11-24 09:05:44 +01:00
Patrick Pelissier aca800ef8a Fix warning with lastest GCC about strict overflow 2019-11-24 08:50:55 +01:00
Patrick Pelissier dde090d4b0 Make GMP optional for the examples 2019-11-24 08:34:39 +01:00
Patrick Pelissier 87183573ce Ignore generated configuration 2019-11-24 08:27:40 +01:00
Patrick Pelissier baddcc3855 Avoid warning about variable being unused (C11) 2019-11-23 23:28:36 +01:00
Patrick Pelissier ba531774a0 Fix doc 2019-11-21 06:28:28 +01:00
Patrick Pelissier 708d17a4d2 Update README on details on OPLIST 2019-11-21 06:27:32 +01:00
Patrick Pelissier 7d802c03e5 Update README on details on OPLIST 2019-11-21 06:26:10 +01:00
Patrick Pelissier 787e1e9965 Merge branch 'master' of https://github.com/P-p-H-d/mlib 2019-11-05 19:56:09 +01:00
Patrick Pelissier 625691d279 Clarify doc on m-worker 2019-11-05 08:13:16 +01:00
Patrick Pelissier 4f0148dc77 Update doc with new container 2019-11-02 12:56:00 +01:00
Patrick Pelissier c78f2b992f Add support for set based on OA hashmap (DICT_OASET_DEF) 2019-11-02 08:40:24 +01:00
Patrick Pelissier 9857db0d15 Prepare integration of set based on OA hashmap for _ref and _cref 2019-11-02 08:29:44 +01:00
Patrick Pelissier 8b71064643 Simplify code for SET (since EQUAL returns TRUE as an EMPTY method) 2019-11-02 08:24:42 +01:00
Patrick Pelissier bc39034f6d Prepare integration of set based on OA hashmap 2019-11-02 08:17:44 +01:00
Patrick Pelissier e6a9b8fc8e Add M_EMPTY_OPLIST 2019-11-02 08:16:45 +01:00
Patrick Pelissier 3db43dc2db Update comments 2019-11-02 07:33:48 +01:00
Patrick Pelissier d384d5ad6c Always use INIT_MOVE when shrinking or enlarging the OA hashmap (Reversing the order of the last scanning) 2019-11-01 20:01:02 +01:00
Patrick Pelissier 7b2004caad Remove item 2019-10-29 12:32:51 +01:00
Patrick Pelissier 6754f88861 Update 2019-10-29 12:32:36 +01:00
Patrick Pelissier 9393809dfd Update todo 2019-10-26 19:22:33 +02:00
Patrick Pelissier 79ecacb6c0 Clarify what dict to use 2019-10-26 11:11:37 +02:00
Patrick Pelissier 853acbad4b Update doc 2019-10-21 23:13:37 +02:00
Patrick Pelissier e158eac10a Update doc 2019-10-21 22:56:28 +02:00
Patrick Pelissier 48ca2a2384 Add bench for OA for big objects (it confirms that OA dict is not suited for such object) 2019-10-21 22:56:15 +02:00
Patrick Pelissier d9d3d3cbe3 Update doc 2019-10-16 07:03:43 +02:00
Patrick Pelissier 6b4f10bf45 Prepare common macros for both hash tables 2019-10-15 07:33:31 +02:00
Patrick Pelissier 3b647b982f Add linear bench 2019-10-14 21:29:27 +02:00
Patrick Pelissier 00b875ea53 Add linear hash map bench 2019-10-14 20:57:29 +02:00
Patrick Pelissier 545fc4d93c Update doc 2019-10-11 07:50:26 +02:00
Patrick Pelissier c41342739a Reformulate 2019-10-02 08:12:09 +02:00
Patrick Pelissier ed99c5aaa3 Fix spelling 2019-09-28 17:17:14 +02:00
PpHd 08842811e8 Merge pull request #48 from taboege/string-clear-get-str-stack
Fix string_clear_get_str when stack allocated
2019-09-26 07:19:14 +02:00
Tobias Boege 3ea063d1e0 Fix string_clear_get_str when stack allocated
If a string_t is stack-allocated, string_clear_get_str would still try
to copy it from the NULL heap pointer, resulting in a failed assertion.
Copy from the stack instead. Also add a test for this.
2019-09-26 05:39:49 +02:00
Patrick Pelissier a905f12915 Document _cmp_order for tuple 2019-09-22 08:06:56 +02:00
Patrick Pelissier 5d8548b92b Update 2019-09-04 07:41:46 +02:00
Patrick Pelissier 99b63e9c77 Update 2019-08-26 08:04:06 +02:00
Patrick Pelissier 4002ef5aff Fix spelling 2019-08-23 06:42:47 +02:00
Patrick Pelissier d31ccfe8a4 Simplify example code 2019-08-11 10:43:35 +02:00
Patrick Pelissier c4ab76a330 More examples 2019-08-09 08:14:12 +02:00
Patrick Pelissier 84d4277be3 Add new example with basic types usage 2019-08-08 22:23:06 +02:00
Patrick Pelissier 0a99814ce2 Fix comments 2019-08-06 20:47:24 +02:00
Patrick Pelissier 11d695cb57 Update doc 2019-07-31 07:45:31 +02:00
Patrick Pelissier 13e2d6b473 Update issues 2019-07-25 07:10:45 +02:00
Patrick Pelissier e8137e4218 Fix spelling 2019-07-24 21:27:06 +02:00
Patrick Pelissier fe5df08d6e Update comments 2019-07-20 09:32:09 +02:00
Patrick Pelissier f77017f35f Update current limitation 2019-07-16 23:21:52 +02:00
Patrick Pelissier ce93c00a7b Workaround limitation of M_MOVE_DEFAULT for [1] structure (==> It supposes now that the destination parameter is **not** a type with [1] tricks or at least not a pointer to it, instead of having this constraint for both arguments). 2019-07-16 23:21:41 +02:00
Patrick Pelissier b8f44cc219 Fix bug (number of reader was too big, resulting in potential segfault) 2019-07-16 23:00:10 +02:00
Patrick Pelissier 2c921fdefe Improve test suite of snapshot to detect incorrect issues of INIT/CLEAR call by a container. 2019-07-16 23:00:10 +02:00
Patrick Pelissier 20eb95ba45 Update comments 2019-07-16 23:00:10 +02:00
Patrick Pelissier 7a6239912e Simplify code 2019-07-12 12:30:04 +02:00
Patrick Pelissier 9067311788 Update comment 2019-07-12 11:50:46 +02:00
Patrick Pelissier 23b2e07132 Add basic testing for _push_move method 2019-07-11 13:38:58 +02:00
Patrick Pelissier c8f2c0e06e Add _push_move method for SPSC 2019-07-11 13:38:40 +02:00
Patrick Pelissier 72c4eb80a2 Add doc on non documented operators 2019-07-11 12:56:00 +02:00
Patrick Pelissier 1f18e788e6 Update comments 2019-07-11 12:36:01 +02:00
Patrick Pelissier f855e24b56 Add description of operator INC_ALLOC 2019-07-11 12:29:00 +02:00
Patrick Pelissier 84d1b7ad50 Update doc 2019-07-08 21:35:59 +02:00
Patrick Pelissier 9f82649aa8 Add test for multiset 2019-07-08 21:25:19 +02:00
Patrick Pelissier 791f586c7e Add back contract support for multimap / multiset 2019-07-08 21:18:59 +02:00
Patrick Pelissier 8d3b0cc94b Prepare support in contract for multimap/multiset 2019-07-08 21:14:15 +02:00
Patrick Pelissier 70d1dfeb8b Add test case for multimap 2019-07-08 16:23:59 +02:00
Patrick Pelissier 9a4c6ba0d5 WIP for multimap / multiset. 2019-07-08 16:23:45 +02:00
Patrick Pelissier ef2dbf89ee Improve test 2019-07-08 11:36:09 +02:00
Patrick Pelissier d30b776fb0 Add more test 2019-07-08 11:33:42 +02:00
Patrick Pelissier 6408b28513 Update comments, function name and prepare for multimap / multiset 2019-07-08 11:17:08 +02:00
Patrick Pelissier 32b3e21564 Start supporting multimap/multiet 2019-07-07 21:18:34 +02:00
Patrick Pelissier 1ef28bedcb Rename internal functions for clarity 2019-07-07 21:16:04 +02:00
Patrick Pelissier 1356bb746e Add more tests 2019-07-06 10:44:49 +02:00
Patrick Pelissier 23240a44d5 Change how the keys are split. Before key[i] ==> node[i] has all keys < key[i], node[i+1] has all keys >= key[i]. After key[i] ==> node[i] has all keys <= key[i], node[i+1} has all keys > key[i]. 2019-07-06 00:03:26 +02:00
Patrick Pelissier 589c023622 Update test 2019-07-06 00:01:13 +02:00
Patrick Pelissier 047b6c5a58 Update 2019-07-04 21:10:38 +02:00
Patrick Pelissier 951425e963 Update comments 2019-07-04 10:42:24 +02:00
Patrick Pelissier e69cbfcab3 Add parameter isMulti 2019-07-04 09:53:58 +02:00
Patrick Pelissier 37f3cd6a72 Rename method _it_to_p into _it_until_p. Add method _it_while_p 2019-07-03 18:57:24 +02:00
Patrick Pelissier ec7b5dd120 Remove deprecated methods 2019-07-03 18:06:52 +02:00
Patrick Pelissier d50a78171f Bump to version 0.3.0 2019-07-03 18:06:39 +02:00
Patrick Pelissier b422aec2cd Add new example 2019-06-25 07:04:33 +02:00
Patrick Pelissier 7ba0e03b4f Add comments 2019-06-20 21:36:12 +02:00
Patrick Pelissier c535766c22 Update/Clean up basic type 2019-06-17 07:26:11 +02:00
Patrick Pelissier 289a3fdc71 Fix confusing position of { 2019-06-10 23:20:36 +02:00
Patrick Pelissier 7f0b6208a4 Propose a shorter version of the example. 2019-06-10 22:59:47 +02:00
Patrick Pelissier 60a10eb799 Uniformize the macro names 2019-06-09 12:02:38 +02:00
Patrick Pelissier 5321085856 Uniformize & add intermediary evaluation step for dict oplist
(like all other containers)
2019-06-09 11:39:15 +02:00
Patrick Pelissier 03be814a4d Uniformize macro names 2019-06-09 11:38:39 +02:00
Patrick Pelissier 6c3f7683a5 Uniformize API 2019-06-09 11:26:45 +02:00
Patrick Pelissier 06d6a90a21 Uniformize internal macros 2019-06-09 11:02:45 +02:00
Patrick Pelissier 747da27489 Uniformize 2019-06-09 10:49:18 +02:00
Patrick Pelissier 2869345920 Add test dor dual-push list for algo 2019-06-09 10:05:34 +02:00
Patrick Pelissier 6b8938cf76 Update comments.
Rename / Uniformize internal macros.
2019-06-09 10:01:50 +02:00
Patrick Pelissier 8e7eae5751 Update comments. 2019-06-09 09:32:35 +02:00
Patrick Pelissier 1882148a7c Add more assertions 2019-06-08 10:45:24 +02:00
Patrick Pelissier e46746ce01 Fix format string 2019-06-08 10:33:26 +02:00
Patrick Pelissier 32917e2cfa Add variant & prioqueue to tests then with concurrent 2019-06-08 10:07:03 +02:00
Patrick Pelissier 65a243fc13 Add missing const 2019-06-08 10:06:47 +02:00
Patrick Pelissier 1e011cb851 Add missing const in prototypes. 2019-06-08 09:59:16 +02:00
Patrick Pelissier 48f585a4ee Update comments 2019-06-08 09:47:49 +02:00
Patrick Pelissier bc10b616ae Update doc 2019-06-08 09:31:18 +02:00
Patrick Pelissier 851f08131d Update 2019-06-07 07:46:54 +02:00
Patrick Pelissier e294f4557e Update 2019-06-06 22:07:09 +02:00
Patrick Pelissier 9e5775fc16 Add example of serialization 2019-06-06 07:44:33 +02:00
Patrick Pelissier 8eb95d0cdc Update doc 2019-06-05 22:05:02 +02:00
Patrick Pelissier 42f807d64b Rename test file 2019-06-02 10:39:55 +02:00
Patrick Pelissier d1bb97ac52 Add new serializer 2019-06-02 10:39:24 +02:00
Patrick Pelissier f502b16b1d Fix bug 2019-06-02 10:31:25 +02:00
Patrick Pelissier 51e5764258 Add comment 2019-06-02 10:05:27 +02:00
Patrick Pelissier ec2281c29a Use of proper cast 2019-06-02 09:25:19 +02:00
Patrick Pelissier acfe1cb7e8 Update documentation for serialization 2019-05-28 22:50:16 +02:00
Patrick Pelissier 594aebd903 Fix comment 2019-05-28 22:50:06 +02:00
Patrick Pelissier 8bca63ac08 Fix MD syntax 2019-05-28 21:54:12 +02:00
Patrick Pelissier c02ec4f94c Update comments 2019-05-19 10:27:23 +02:00
Patrick Pelissier 797ac1f0da Fix for 32 bits system 2019-05-18 18:55:51 +02:00
Patrick Pelissier 058d3202ff Amend 2019-05-18 14:09:10 +02:00
Patrick Pelissier 62fcdac9cb Update list of headers 2019-05-18 14:08:17 +02:00
Patrick Pelissier b9131353b6 Add test 2019-05-18 11:17:15 +02:00
Patrick Pelissier a34deb2f3b Workaround warning. 2019-05-18 11:17:15 +02:00
Patrick Pelissier 9ef4b5c93f Fix cast 2019-05-18 11:17:14 +02:00
Patrick Pelissier 417b0295be Add C++ compatibility 2019-05-18 11:17:14 +02:00
Patrick Pelissier d11409bcc6 Add proper cast 2019-05-18 11:17:14 +02:00
Patrick Pelissier 928b81f454 Fix return value for in_serial/out_serial 2019-05-18 11:17:14 +02:00
Patrick Pelissier 05a68157bc Add local argument to serial interface to enable serial interface to create local parameters if needed (for example for serial binary) 2019-05-18 11:17:14 +02:00
Patrick Pelissier b6d5b5462b Update interface needed for serial 2019-05-18 11:17:14 +02:00
Patrick Pelissier 1c77082a29 Update comments 2019-05-18 11:17:14 +02:00
Patrick Pelissier 363c55668d Add in_serial / out_serial methods for several containers 2019-05-18 11:17:14 +02:00
Patrick Pelissier 7ed85fb97a Add in_serial / out_serial for concurrent containers 2019-05-18 11:17:14 +02:00
Patrick Pelissier 585ea1293d Add in_serial / out_serial for lists 2019-05-18 11:17:14 +02:00
Patrick Pelissier 448072555f Update comments 2019-05-18 11:17:14 +02:00
Patrick Pelissier 4701d5919b Fix serial import/export for variant 2019-05-18 11:17:14 +02:00
Patrick Pelissier 0997f59665 Update for variant 2019-05-18 11:17:14 +02:00
Patrick Pelissier dcf7a94728 Add in_serial / out_serial for variant 2019-05-18 11:17:14 +02:00
Patrick Pelissier 12ba563416 Add assertions 2019-05-18 11:17:14 +02:00
Patrick Pelissier f2fffb9219 Factor comment 2019-05-18 11:17:14 +02:00
Patrick Pelissier 3f03615ed2 Simplify code by factoring fscanf calls 2019-05-18 11:17:14 +02:00
Patrick Pelissier bc6bda057c Add in_serial / out_serial for array 2019-05-18 11:17:14 +02:00
Patrick Pelissier aeebe42bfb Fix issues for EOF & parsing arrays 2019-05-18 11:17:14 +02:00
Patrick Pelissier 5c4c93b62f Add missing export/import JSON interface 2019-05-18 11:17:14 +02:00
Patrick Pelissier c3ddd48d38 Simplify code 2019-05-18 11:17:14 +02:00
Patrick Pelissier 98f3c40ac5 add export/import of JSON string 2019-05-18 11:17:14 +02:00
Patrick Pelissier ada1b9861f Add size_of_type field for reading too 2019-05-18 11:17:13 +02:00
Patrick Pelissier 50cc8689bb Add serial methods for string_t 2019-05-18 11:17:13 +02:00
Patrick Pelissier 83da861a9c Simplify code by using more fscanf possibility 2019-05-18 11:17:13 +02:00
Patrick Pelissier 1cb1d16e77 Simplify code by using fscanf behvior on white space 2019-05-18 11:17:13 +02:00
Patrick Pelissier b525723084 Implement Serial Json Read tuple 2019-05-18 11:17:13 +02:00
Patrick Pelissier 4accdc994d Add JSON/read interface 2019-05-18 11:17:13 +02:00
Patrick Pelissier ad9bee0eec Add JSON serializer 2019-05-18 11:17:13 +02:00
Patrick Pelissier 6c7ff1c723 Update/fix bugs 2019-05-18 11:17:13 +02:00
Patrick Pelissier 3b2aad1b2e Export new methods for tuple 2019-05-18 11:17:13 +02:00
Patrick Pelissier 5222fabf02 Add tuple support for serial 2019-05-18 11:17:13 +02:00
Patrick Pelissier 44abead174 Update IN_SERIAL & OUT_SERIAL for default types. 2019-05-18 11:17:13 +02:00
Patrick Pelissier 492d5f0f9a Add IN_SERIAL & OUT_SERIAL for default types. 2019-05-18 11:17:13 +02:00
Patrick Pelissier 9bc5ac3df8 Start implementating generic serialization 2019-05-18 11:17:13 +02:00
Patrick Pelissier 5c726d63f4 Fix 2019-05-15 07:38:16 +02:00
Patrick Pelissier 2cd0a8db74 Uniformize the way of displaying the location of errors in the error message 2019-05-11 01:16:54 +02:00
Patrick Pelissier 0fa0445fa9 Fix C++ warning -Wclass-memaccess with G++ >= 8 2019-05-10 23:13:54 +02:00
Patrick Pelissier 1e171f9cc0 Increase NARGS support to 75 2019-05-10 21:39:18 +02:00
Patrick Pelissier 8d16ea4bf4 Increase maximum number of operators per oplist to 75 2019-05-10 21:26:18 +02:00
Patrick Pelissier a0c2ead91d Fix bug for M_AS_TYPE and small types 2019-05-10 10:20:38 +02:00
Patrick Pelissier b2875f6ba0 Add BSTRLIB for MASTER STRING BENCHMARK 2019-05-10 08:18:38 +02:00
Patrick Pelissier edb4ccf63e Add algo functions _remove_val & _remove_if 2019-05-07 16:35:39 +02:00
Patrick Pelissier 481009d18b Add function _end_with for strings. 2019-05-07 16:12:43 +02:00
Patrick Pelissier 6153383dab Update indent 2019-05-07 16:05:47 +02:00
Patrick Pelissier 74fed41818 Update version 2019-05-07 16:01:50 +02:00
Patrick Pelissier 2d5de1b9ea Update comments 2019-04-29 12:32:29 +02:00
Patrick Pelissier e23691c743 Fix spelling 2019-04-24 07:47:15 +02:00
Patrick Pelissier aaac236db6 Update README 2019-04-22 18:31:47 +02:00
Patrick Pelissier ccccedc2d5 Rework failure messages. 2019-04-22 18:30:46 +02:00
Patrick Pelissier 446afb04c7 Remove unused macro M_INIT_FAILURE 2019-04-22 12:07:53 +02:00
Patrick Pelissier 87cd964329 Uniformize error messages 2019-04-22 11:45:29 +02:00
Patrick Pelissier 80a344e46d Update version 2019-04-22 11:34:35 +02:00
Patrick Pelissier f2eb5014ac Fix regression on 32 bits for strings. 2019-04-21 23:50:42 +02:00
Patrick Pelissier d6c72061ce add new bench on strings 2019-04-21 23:13:29 +02:00
Patrick Pelissier 7c26fd25c9 Add SDS & RAPIDSTRING replace function implemented at used level 2019-04-21 19:54:20 +02:00
Patrick Pelissier 10e12d2b5c Update 2019-04-21 11:21:45 +02:00
Patrick Pelissier b9070f89d9 Update 2019-04-21 10:43:17 +02:00
Patrick Pelissier 46d41a8882 Fix bad merge 2019-04-20 12:33:09 +02:00
Patrick Pelissier b5dc3481d4 Update doc 2019-04-20 12:33:08 +02:00
Patrick Pelissier c18cd81dde Add more assumes needed for static analysis. 2019-04-20 12:33:08 +02:00
Patrick Pelissier eaf5a26157 Minor speed up 2019-04-20 12:33:08 +02:00
Patrick Pelissier 547c67b676 Modify string_t so that it can still be trivially movable once again. 2019-04-20 12:33:08 +02:00
Patrick Pelissier c89d6ef0ce Fix test. 2019-04-20 12:33:08 +02:00
Patrick Pelissier a54041c340 Minor fix 2019-04-20 12:33:08 +02:00
Patrick Pelissier 352e0972cd Comment code 2019-04-20 12:33:08 +02:00
Patrick Pelissier 91ebd7cc9f Cleanup code 2019-04-20 12:33:08 +02:00
Patrick Pelissier d8854c276b Cleanup code 2019-04-20 12:33:08 +02:00
Patrick Pelissier cc61840ae8 Refactor code 2019-04-20 12:33:08 +02:00
Patrick Pelissier 67a7a4762a Refactor code 2019-04-20 12:33:08 +02:00
Patrick Pelissier c28597014f Add more hypothesis 2019-04-20 12:33:08 +02:00
Patrick Pelissier d00f29bf26 Fix bug 2019-04-20 12:33:08 +02:00
Patrick Pelissier 1432fbabca Update buffer to have proper alignement so that we can perform hash computation properly. 2019-04-20 12:33:08 +02:00
Patrick Pelissier f3b6ee0190 Continue porting to new format. 2019-04-20 12:33:08 +02:00
Patrick Pelissier 229d21ef72 Add assume 2019-04-20 12:33:08 +02:00
Patrick Pelissier 00f1b1fc4f Update comment 2019-04-20 12:33:08 +02:00
Patrick Pelissier 277d8b37af Continue porting to new format. 2019-04-20 12:33:08 +02:00
Patrick Pelissier 7b83761ef4 Update 2019-04-20 12:33:08 +02:00
Patrick Pelissier a96e9c7e11 Update string contract 2019-04-20 12:33:08 +02:00
Patrick Pelissier 866721d067 Add first prototype of string with stack allocation. 2019-04-20 12:33:08 +02:00
Patrick Pelissier b356c29c89 Add PGO 2019-04-20 12:29:59 +02:00
Patrick Pelissier a2f7cb7670 Add C Macro Collections ref 2019-04-20 10:24:20 +02:00
Patrick Pelissier 08887c1df0 Add PGO 2019-04-20 09:08:10 +02:00
Patrick Pelissier 8b0a9f2da9 Cleanup Makefile 2019-04-20 08:51:52 +02:00
Patrick Pelissier e6c958c0df Fix data race. 2019-04-19 07:57:10 +02:00
Patrick Pelissier 01ad221972 Rewrite RP so that writers cannot starve and avoid warnings with thread sanitizer. 2019-04-11 20:07:27 +02:00
Patrick Pelissier c269a36e8b Add thread test for RP_CONCURRENT 2019-04-07 19:03:03 +02:00
Patrick Pelissier 95fcd054a3 Fix error in lock orders. 2019-04-04 21:22:37 +02:00
Patrick Pelissier 7760f16260 Fix memory leaks 2019-04-04 08:31:26 +02:00
Patrick Pelissier 6d016d732b Update tags 2019-03-31 22:37:00 +02:00
Patrick Pelissier b9aebf875e Update comments. 2019-03-31 22:36:52 +02:00
Patrick Pelissier 1eb0e8730d Use algorithm with dictionary too. 2019-03-24 08:38:52 +01:00
Patrick Pelissier 7eb26a29f1 Reduce the number of mandatory operators for algorithm:
* EQUAL
* INIT
are not optional (methods depending on them won't be generated
if the operators are not defined).
2019-03-24 08:37:57 +01:00
Patrick Pelissier 0ef051e6d2 Add GLIB to string bench 2019-03-24 08:29:32 +01:00
Patrick Pelissier 0d641f0d17 Update documentation 2019-03-22 07:53:13 +01:00
Patrick Pelissier ce6a7f6cb3 Add missing _it_end for normal dict & export it for both dict 2019-03-15 07:44:58 +01:00
Patrick Pelissier 07b4c1a9f4 Update image 2019-03-12 21:43:33 +01:00
Patrick Pelissier 4d3a82583d Update doc 2019-03-12 21:43:18 +01:00
Patrick Pelissier 2835d53ef1 Clean up code. 2019-03-12 21:42:53 +01:00
Patrick Pelissier 620ecadec1 Update doc 2019-03-12 07:49:04 +01:00
Patrick Pelissier dc803f23bb Add missing const 2019-03-09 16:38:44 +01:00
Patrick Pelissier a6d11fe897 Update doc (ALGO part) 2019-03-09 16:38:25 +01:00
Patrick Pelissier 78b05690e7 Update README 2019-03-07 19:28:46 +01:00
Patrick Pelissier 958a79ad73 Add another example 2019-03-07 19:25:39 +01:00
Patrick Pelissier ead25c905a Factorize common macro for FLA 2019-03-07 07:42:50 +01:00
Patrick Pelissier 97f80450ca Update issue 2019-02-21 20:19:04 +01:00
Patrick Pelissier 8e62537604 Update ISSUE 2019-02-20 11:46:58 +01:00
Patrick Pelissier 7813182e2b Update test. 2019-02-19 20:22:55 +01:00
Patrick Pelissier b199fef674 Update comments and move commented asm into common function. 2019-02-18 11:02:13 +01:00
Patrick Pelissier e27c06c227 Fix regression build with SDS & rapidstring 2019-02-18 10:55:51 +01:00
Patrick Pelissier 41b04f34e2 Fix: Uses of __thread after static keyword 2019-02-18 10:53:27 +01:00
Patrick Pelissier b091f84f90 Update code after benching empty creation of string. 2019-02-18 10:48:03 +01:00
Patrick Pelissier 05ae9f382b Update the barrier used to prevent the compiler from over-optimizing.
Shall be could enought except if using FLTO.
2019-02-18 10:47:28 +01:00
Patrick Pelissier a36efcd888 Create new helper compiler_barrier to prevent compiler from over-optimizing bench. 2019-02-18 10:46:21 +01:00
Patrick Pelissier af2fe9ccf9 Fix issue (sizeof instead of offsetof) 2019-02-17 09:54:56 +01:00
Patrick Pelissier c4a5727c2a Fix deref of NULL pointer. 2019-02-17 09:50:04 +01:00
Patrick Pelissier 31ca762c62 Add a new test case for empty string. 2019-02-17 09:49:49 +01:00
Patrick Pelissier 30bda70319 Fix issue. 2019-02-17 09:36:18 +01:00
Patrick Pelissier 7fa9d375b8 Add bench of small non empty constructor 2019-02-17 08:35:11 +01:00
Patrick Pelissier 91ae3fd4f2 Add memory barrier to limiting the effect of the optimizer on inline implementation 2019-02-16 23:20:36 +01:00
Patrick Pelissier b00d6a9655 Add CSV output to string bench 2019-02-16 22:30:39 +01:00
Patrick Pelissier 013aa58524 Update documentation 2019-02-16 09:01:57 +01:00
Patrick Pelissier 8ce7e0483f Update comment 2019-02-13 08:24:21 +01:00
Patrick Pelissier 582c413cd2 Add VLA allocation compatible with concurrent mempool & gc 2019-02-13 08:19:07 +01:00
Patrick Pelissier 22e16e983b Separate mempool in two concepts:
* A Garbage Collector that centralizes the connexion with the threads
  and registers all mempools
* the mempool that is responsible for alloc/dec of nodes.

It allows having multiple mempools registered in a GC.
2019-02-10 10:26:20 +01:00
Patrick Pelissier 7c0c16260a Update doc 2019-02-05 22:56:02 +01:00
Patrick Pelissier e496fbd152 Add test for string_t + OA DICT 2019-01-27 22:59:03 +01:00
Patrick Pelissier e025de91ab Adding OOR methods for string_t 2019-01-27 22:32:47 +01:00
Patrick Pelissier 69e2fc432f Update doc about limitation of operator TEST_EQUAL 2019-01-27 21:16:19 +01:00
Patrick Pelissier 78ba83ce42 * Fix BOUNDED_STRING_CTE macro. In OOR context, it may create invalid constant that triggers
assumption fault.
* Fix _equal_p in OOR context for bounded string. This method can be called in OOR context,
so the contracts may not be verified.
2019-01-27 17:33:47 +01:00
Patrick Pelissier 6d9b61c119 Reindent 2019-01-27 17:33:17 +01:00
Patrick Pelissier 61cfa5cedd Use of proper contract for BOUNDED_STRING. 2019-01-26 14:00:32 +01:00
Patrick Pelissier 0507d94306 Add test for IO for BOUNDED STRING 2019-01-26 13:39:47 +01:00
Patrick Pelissier 7e8919080b Add IO for BOUNDED STRING 2019-01-26 13:38:54 +01:00
Patrick Pelissier 8406efcaab Add test case of BOUNDED_STRING + OA DICT 2019-01-26 12:03:13 +01:00
Patrick Pelissier 2d87eb1e6a Adding default OOR_EQUAL & OOR_SET for BOUNDED_STRING
to be used in OA dict
2019-01-26 12:02:50 +01:00
Patrick Pelissier 4685aac454 Add test for hash of empty string 2019-01-26 09:10:36 +01:00
Patrick Pelissier 9863a8325c Fix assertion. NULL pointer is accepted for HASH providing the size is 0 2019-01-26 09:10:18 +01:00
Patrick Pelissier b9514cbe72 Fix usage of API with OOR EQUAL of OA Dict 2019-01-25 20:37:33 +01:00
Patrick Pelissier 7b085d18fc Add test case of OA DICT + concurrent 2019-01-25 20:19:52 +01:00
Patrick Pelissier 91e73520f3 Fix issue of non being able to use API_2 with OOR_SET for OA dict 2019-01-25 20:19:31 +01:00
Patrick Pelissier aebf5c83ba Update description of STORED_HASH dict as per remark of #44 2019-01-25 19:47:47 +01:00
Patrick Pelissier 42ff47f511 Fix issue described in #44 (missing types for OA dict) 2019-01-25 19:47:46 +01:00
Patrick Pelissier 746bdf144b Fix warning with CLANG7 2019-01-24 07:44:36 +01:00
Patrick Pelissier 4e8f5f7b51 Add test for queue SPSC 2019-01-21 22:20:28 +01:00
Patrick Pelissier 50d89f71cb Update 2019-01-21 22:06:31 +01:00
Patrick Pelissier d70ef53283 Add _push_force for QUEUE SPSC 2019-01-20 23:59:33 +01:00
Patrick Pelissier 1b6182c1cd Update 2019-01-20 23:57:33 +01:00
Patrick Pelissier 73b50c852a Add bench for SPSC in BULK mode 2019-01-16 05:08:03 +01:00
Patrick Pelissier 063011d8e9 Adding yield in case of failure of push/pop 2019-01-16 04:46:06 +01:00
Patrick Pelissier 0f3bf6ab82 Add _bulk version for SPSC queue. To test/bench 2019-01-15 23:27:02 +01:00
Patrick Pelissier 475836e851 Use of new macro M_CACHELINE_ALIGN 2019-01-15 19:11:10 +01:00
Patrick Pelissier 325ca30c61 Add M_CACHELINE_ALIGN macro 2019-01-15 19:10:39 +01:00
Patrick Pelissier 72e872c768 Remove outdated comment 2019-01-15 07:34:19 +01:00
Patrick Pelissier c2a837b30b Update 2019-01-15 07:32:13 +01:00
Patrick Pelissier 08822ac7d5 Update 2019-01-15 07:25:11 +01:00
Patrick Pelissier 7f9007ca1a Add UPDATE_TYPE 2019-01-11 07:58:20 +01:00
Patrick Pelissier f067cb334c Add missing return after M_MEMORY_FULL 2019-01-05 19:57:53 +01:00
Patrick Pelissier 60ee253fa7 Update 2019-01-05 13:15:50 +01:00
Patrick Pelissier 4642e0b506 Workaround issue of stdatomic on MSYS2/GCC 2019-01-05 00:19:09 +01:00
Patrick Pelissier bd29680b62 Add explanations as comment 2019-01-03 07:45:08 +01:00
Patrick Pelissier c337462af6 Use extension to silent warnings about C11 features used in C99 2019-01-02 22:37:40 +01:00
Patrick Pelissier c990277855 Add extension attribute support 2019-01-02 22:34:39 +01:00
Patrick Pelissier 8870a1c2f9 Remove atomic qualifier for one variable (useless) 2019-01-02 22:16:08 +01:00
Patrick Pelissier 60f1454550 Fix incorrect qualifier position for emulation layer (issue for atomic pointer definition) 2019-01-02 22:15:47 +01:00
Patrick Pelissier 38eb1c26c6 Variable number of threads for concurrent memory pool 2019-01-02 21:48:53 +01:00
Patrick Pelissier af050d0100 Port to C++ madness 2019-01-02 21:21:24 +01:00
Patrick Pelissier 14f3ae5cea Fix issue 2019-01-02 21:21:06 +01:00
Patrick Pelissier f4ed750d39 Add first test case for concurrent memory pool 2019-01-02 21:13:01 +01:00
Patrick Pelissier 4b3adc8d35 Add first WIP Concurrent Memory Allocator 2019-01-02 20:45:06 +01:00
Patrick Pelissier 8b3a4a802c Happy new year! 2019-01-02 18:12:07 +01:00
Patrick Pelissier 8b9f605ffa Add exponential backoff structure & functions 2019-01-02 18:09:16 +01:00
Patrick Pelissier 3c56f24db2 Update analysis 2018-12-29 00:16:50 +01:00
Patrick Pelissier 82dbf60a4c Use of the standard naming for User Defined macro that can customize the code generation. Rename macros WORKER_DISABLE. 2018-12-23 23:49:19 +01:00
Patrick Pelissier b596e81e88 Regenerate depend 2018-12-14 21:48:21 +01:00
Patrick Pelissier b7bbe15547 Fix missing header in install 2018-12-14 21:46:38 +01:00
Patrick Pelissier cb5f8c20c8 Update analysis 2018-12-11 23:23:09 +01:00
Patrick Pelissier 61a1e15103 Update analysis 2018-12-11 23:19:33 +01:00
Patrick Pelissier 2416c2e466 Fix bug of overflow of 'alloc-num' if alloc < num (case where the service is called with a big number to insert 2018-12-05 17:52:02 +01:00
Patrick Pelissier b898ce3c56 update analysis 2018-11-29 23:24:11 +01:00
Patrick Pelissier 061e83b9e1 Add basic test for CONCURRENT_RP_DEF 2018-11-29 22:18:44 +01:00
Patrick Pelissier 0a8f727908 Remove CONCURRENT_RP2_DEF (Insuficient performance) 2018-11-27 07:47:06 +01:00
Patrick Pelissier ba584ab096 Use M_THREAD_ATTR 2018-11-26 23:26:42 +01:00
Patrick Pelissier 1fb1fa2e0a Add M_THREAD_ATTR 2018-11-26 23:26:08 +01:00
Patrick Pelissier 8cf780a60f Update comment 2018-11-25 22:43:37 +01:00
Patrick Pelissier ae171115bc Export method _size in concurrent if operator GET_SIZE defined. 2018-11-25 18:03:25 +01:00
Patrick Pelissier a8620c3aff Export method _size for operator GET_SIZE 2018-11-25 18:00:47 +01:00
Patrick Pelissier c154cdf46c Add operator GET_SIZE 2018-11-25 17:52:24 +01:00
Patrick Pelissier 77c59e2b85 Fix const issue. 2018-11-25 17:49:36 +01:00
Patrick Pelissier f07656757f Add another candidate for concurrent with parallel reader. 2018-11-25 11:44:28 +01:00
Patrick Pelissier a1c22fb679 Add first alpha version of Concurrent Container with support concurrent reading. 2018-11-24 19:31:31 +01:00
Patrick Pelissier 3640041c47 Port internal functions of concurrent to use the local lock interface. 2018-11-24 12:20:37 +01:00
Patrick Pelissier d3934dccb9 Add missing const in prototypes. 2018-11-24 11:46:28 +01:00
Patrick Pelissier 10cf2f2270 Prepare concurent for using different lock strategy 2018-11-24 11:37:09 +01:00
Patrick Pelissier c180b2b96d Update analysisd 2018-11-22 07:49:15 +01:00
106 changed files with 10450 additions and 2261 deletions

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
name: C/C++ CI
on: [push]
jobs:
build:
strategy:
matrix:
platform: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v1
- name: make
run: make
- name: make check
run: make check
build_windows_mingw:
strategy:
matrix:
platform: [windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v1
- name: Install MINGW
run: choco install mingw --no-progress
- name: make
shell: bash
run: mingw32-make CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1"
- name: make check
shell: bash
run: mingw32-make CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
+1
View File
@@ -37,3 +37,4 @@ depend
# Others
other/
a*.dat
config
+1 -1
View File
@@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2017-2018, Patrick Pelissier
Copyright (c) 2017-2019, Patrick Pelissier
All rights reserved.
Redistribution and use in source and binary forms, with or without
+10 -6
View File
@@ -7,18 +7,18 @@ INSTALL_DATA=${INSTALL} -m 644
# Installation directory
PREFIX=/usr/local
DESTDIR=
# Package name & version.
PACKAGE=m*lib-$(VERSION)
VERSION=0.2.0
VERSION=0.3.0
# Define the contain of the distribution tarball
# TODO: Get theses lists from GIT itself.
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-mutex.h m-prioqueue.h m-rbtree.h m-shared.h m-snapshot.h m-string.h m-tuple.h m-variant.h m-worker.h
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tuple.h m-variant.h m-worker.h
DOC1=LICENSE README.md
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/DEV.md doc/ISSUES.org doc/depend.png doc/oplist.png
EXAMPLE=example/ex-array01.c example/ex-array04.c example/ex-dict02.c example/ex-grep01.c example/ex-multi01.c example/ex-rbtree01.c example/ex-array02.c example/ex-buffer01.c example/ex-dict03.c example/ex-list01.c example/ex-multi02.c example/Makefile example/ex-array03.c example/ex-dict01.c example/ex-dict04.c example/ex-mph.c example/ex-multi03.c
TEST=tests/coverage.h tests/test-malgo.c tests/test-mbptree.c tests/test-mdeque.c tests/test-milist.c tests/test-mmutex.c tests/test-mshared.c tests/test-mtuple.c tests/test-obj.h tests/tgen-mdict.c tests/tgen-openmp.c tests/wip-mregister.c tests/dict.txt tests/test-marray.c tests/test-mbuffer.c tests/test-mdict.c tests/test-mlist.c tests/test-mprioqueue.c tests/test-msnapshot.c tests/test-mvariant.c tests/tgen-bitset.c tests/tgen-mlist.c tests/tgen-queue.c tests/Makefile tests/test-mbitset.c tests/test-mcore.c tests/test-mgenint.c tests/test-mmempool.c tests/test-mrbtree.c tests/test-mstring.c tests/test-mworker.c tests/tgen-marray.c tests/tgen-mstring.c tests/tgen-shared.c
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png
EXAMPLE=example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-json01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-rbtree01.c example/ex-section.c example/Makefile
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/wip-mregister.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt
.PHONY: all test check doc clean distclean depend install uninstall dist
@@ -31,6 +31,10 @@ check:
cd tests && $(MAKE) check
cd example && $(MAKE) all
checkall:
cd tests && $(MAKE) checkall
cd example && $(MAKE) all
html: doc
doc: README.md doc/depend.png
markdown < README.md > README.html
+1109 -306
View File
File diff suppressed because it is too large. Load diff
+76 -39
View File
@@ -1,14 +1,20 @@
CC=cc -std=c11
CXX=c++ -std=c++11
CFLAGS=-O2 -march=native -Wall
XCFLAGS=
CPPFLAGS=-I..
N=4
BENCH_DEF=
RM=rm -rf
.PHONY: all container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
.PHONY: all pgo container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
all: container queue mempool string plain
pgo:
$(MAKE) all XCFLAGS="-fprofile-generate=pgo"
$(MAKE) all XCFLAGS="-fprofile-use=pgo"
#########################################################################
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap
@@ -32,7 +38,7 @@ plain: bench-plain
bench-mlib:
$(CC) $(CFLAGS) $(CPPFLAGS) -pthread bench-mlib.c -o bench-mlib.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -pthread bench-mlib.c common.c -o bench-mlib.exe
@./bench-mlib.exe 10
@./bench-mlib.exe 11
@./bench-mlib.exe 20
@@ -41,12 +47,14 @@ bench-mlib:
@./bench-mlib.exe 40
@./bench-mlib.exe 41
@./bench-mlib.exe 42
@./bench-mlib.exe 46
@./bench-mlib.exe 47
@./bench-mlib.exe 43
@./bench-mlib.exe 50
@./bench-mlib.exe 51
bench-mlib-mempool:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
@./bench-mlib-mempool.exe 10
@./bench-mlib-mempool.exe 11
@./bench-mlib-mempool.exe 20
@@ -55,27 +63,31 @@ bench-mlib-mempool:
@./bench-mlib-mempool.exe 40
@./bench-mlib-mempool.exe 41
@./bench-mlib-mempool.exe 42
@./bench-mlib-mempool.exe 46
@./bench-mlib-mempool.exe 47
@./bench-mlib-mempool.exe 43
@./bench-mlib-mempool.exe 50
@./bench-mlib-mempool.exe 51
bench-mlib-thread:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
@./bench-mlib-thread.exe 60
@./bench-mlib-thread.exe 61
@./bench-mlib-thread.exe 62
@./bench-mlib-thread.exe 63
@./bench-mlib-thread.exe 64
@./bench-mlib-thread.exe 65
@./bench-mlib-thread.exe 66
bench-stl:
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-stl.cpp -o bench-stl.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-stl.cpp common.c -o bench-stl.exe
@./bench-stl.exe 10
@./bench-stl.exe 20
@./bench-stl.exe 30
@./bench-stl.exe 40
@./bench-stl.exe 41
@./bench-stl.exe 42
@./bench-stl.exe 46
@./bench-stl.exe 43
@./bench-stl.exe 50
@./bench-stl.exe 51
@@ -84,7 +96,7 @@ bench-stl:
bench-qt:
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
bench-qt0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp common.c -o bench-qt.exe
@./bench-qt.exe 10
@./bench-qt.exe 20
@./bench-qt.exe 30
@@ -97,7 +109,7 @@ bench-qt0:
bench-glib:
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
bench-glib0:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-glib.c common.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
@./bench-glib.exe 10
@./bench-glib.exe 20
@./bench-glib.exe 30
@@ -110,7 +122,7 @@ bench-klib:
@if test -n "$${KLIB}" ; then $(MAKE) bench-klib0 ; else echo "Nothing to be done for KLIB." ; fi
bench-klib0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c -o bench-klib.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c common.c -o bench-klib.exe
@./bench-klib.exe 10
@./bench-klib.exe 20
@./bench-klib.exe 30
@@ -123,20 +135,17 @@ bench-libdynamic:
@if test -n "$${LIBDYNAMIC}" ; then $(MAKE) bench-libdynamic0 ; else echo "Nothing to be done for LIBDYNAMIC." ; fi
bench-libdynamic0:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-libdynamic.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
@./bench-libdynamic.exe 10
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-libdynamic.c common.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
@./bench-libdynamic.exe 20
@./bench-libdynamic.exe 30
@./bench-libdynamic.exe 40
@./bench-libdynamic.exe 41
@./bench-libdynamic.exe 50
# SPARSEPP shall point to the directory where the headers are
bench-sparsepp:
@if test -n "$${SPARSEPP}" ; then $(MAKE) bench-sparsepp0 ; else echo "Nothing to be done for SPARSEPP." ; fi
bench-sparsepp0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp -o bench-sparsepp.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp common.c -o bench-sparsepp.exe
@./bench-sparsepp.exe 41
@./bench-sparsepp.exe 42
@./bench-sparsepp.exe 43
@@ -146,7 +155,7 @@ bench-densehashmap:
@if test -n "$${DENSEHASHMAP}" ; then $(MAKE) bench-densehashmap0 ; else echo "Nothing to be done Google DENSEHASHMAP." ; fi
bench-densehashmap0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp -o bench-densehashmap.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp common.c -o bench-densehashmap.exe
@./bench-densehashmap.exe 41
@./bench-densehashmap.exe 42
@./bench-densehashmap.exe 43
@@ -156,7 +165,7 @@ bench-collectionc:
@if test -n "$${COLLECTIONC}" ; then $(MAKE) bench-collectionc0 ; else echo "Nothing to be done for COLLECTIONC." ; fi
bench-collectionc0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c -lcollectc -o bench-collectionc.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c common.c -lcollectc -o bench-collectionc.exe
@./bench-collectionc.exe 10
@./bench-collectionc.exe 20
@./bench-collectionc.exe 30
@@ -169,7 +178,7 @@ bench-tommyds:
@if test -n "$${TOMMYDS}" ; then $(MAKE) bench-tommyds0 ; else echo "Nothing to be done for TOMMYDS." ; fi
bench-tommyds0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c -o bench-tommyds.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c common.c -o bench-tommyds.exe
@./bench-tommyds.exe 10
@./bench-tommyds.exe 20
@./bench-tommyds.exe 30
@@ -181,9 +190,10 @@ bench-flathashmap:
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
bench-flathashmap0:
$(CXX) -std=c++14 $(CFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp common.c -o bench-flathashmap.exe
@./bench-flathashmap.exe 41
@./bench-flathashmap.exe 42
@./bench-flathashmap.exe 46
@./bench-flathashmap.exe 43
# HOPSCOTCHMAP shall point to the directory where the source are
@@ -191,7 +201,7 @@ bench-hopscotchmap:
@if test -n "$${HOPSCOTCHMAP}" ; then $(MAKE) bench-hopscotchmap0 ; else echo "Nothing to be done for HOPSCOTCHMAP." ; fi
bench-hopscotchmap0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}" bench-hopscotchmap.cpp -o bench-hopscotchmap.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}/include/tsl" bench-hopscotchmap.cpp common.c -o bench-hopscotchmap.exe
@./bench-hopscotchmap.exe 41
@./bench-hopscotchmap.exe 42
@./bench-hopscotchmap.exe 43
@@ -201,7 +211,7 @@ bench-uthash:
@if test -n "$${UTHASH}" ; then $(MAKE) bench-uthash0 ; else echo "Nothing to be done for UTHASH." ; fi
bench-uthash0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c -o bench-uthash.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c common.c -o bench-uthash.exe
@./bench-uthash.exe 10
@./bench-uthash.exe 20
@./bench-uthash.exe 41
@@ -214,7 +224,7 @@ bench-qlibc:
@if test -n "$${QLIBC}" ; then $(MAKE) bench-qlibc0 ; else echo "Nothing to be done for QLIBC." ; fi
bench-qlibc0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c -o bench-qlibc.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c common.c -o bench-qlibc.exe
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 10
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 20
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 30
@@ -222,11 +232,12 @@ bench-qlibc0:
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 41
# LIBSRT shall point to the directory where the source are
# The library shall be built.
bench-libsrt:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-libsrt0 ; else echo "Nothing to be done for LIBSRT." ; fi
bench-libsrt0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c common.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
@./bench-libsrt.exe 10
@./bench-libsrt.exe 20
@./bench-libsrt.exe 41
@@ -240,7 +251,7 @@ bench-libsrt0:
bench-liblfds:
@if test -n "$${LIBLFDS}" ; then $(MAKE) bench-liblfds0 ; else echo "Nothing to be done for LIBLFDS." ; fi
bench-liblfds0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c common.c -DMULTI_THREAD_MEASURE -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
@LD_LIBRARY_PATH="$${LIBLFDS}/bin:$${LD_LIBRARY_PATH}" ./bench-liblfds.exe 60
@@ -249,36 +260,47 @@ bench-liblfds0:
bench-concurrentqueue:
@if test -n "$${CONCURRENTQUEUE}" ; then $(MAKE) bench-concurrentqueue0 ; else echo "Nothing to be done for CONCURRENTQUEUE." ; fi
bench-concurrentqueue0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp -o bench-concurrentqueue.exe -lpthread
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-concurrentqueue.exe -lpthread
@./bench-concurrentqueue.exe 60
# XXHASH shall point to the source directory of xxhash
# The library shall be built.
bench-xxhash:
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
bench-xxhash0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c common.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
@./bench-xxhash.exe 70
# BOOST shall point to the header location.
bench-boost:
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
bench-boost0:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp -o bench-boost.exe -lpthread
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp common.c -DMULTI_THREAD_MEASURE -o bench-boost.exe -lpthread
@./bench-boost.exe 60
# NEDTRIES shall point to the header location.
bench-nedtries:
@if test -n "$${NEDTRIES}" ; then $(MAKE) bench-nedtries0 ; else echo "Nothing to be done for NEDTRIES." ; fi
bench-nedtries0:
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c -o bench-nedtries.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c common.c -o bench-nedtries.exe
@./bench-nedtries.exe 40
@./bench-nedtries.exe 45
# RIGTORP_HASHMAP shall point to the directory where the headers are
bench-rigtorp-hashmap:
@if test -n "$${RIGTORP_HASHMAP}" ; then $(MAKE) bench-rigtorp-hashmap0 ; else echo "Nothing to be done for RIGTORP_HASHMAP." ; fi
bench-rigtorp-hashmap0:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_HASHMAP}/include" bench-rigtorp-hashmap.cpp common.c -o bench-rigtorp-hashmap.exe
@./bench-rigtorp-hashmap.exe 42
@./bench-rigtorp-hashmap.exe 41
@./bench-rigtorp-hashmap.exe 43
############################################################################
bench-plain:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
@./bench-plain-c.exe
############################################################################
@@ -286,13 +308,13 @@ bench-plain:
bench-mempool:
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
bench-mempool0:
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
@./bench-mempool.exe
############################################################################
bench-maxdict:
$(CC) $(CFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
@./bench-max-dict.exe
############################################################################
@@ -300,27 +322,42 @@ bench-maxdict:
# BSTRLIB shall point to the BSTRLIB sources
# LIBSRT shall point to the LIBSRT sources
bench-string:
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-with BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-with ; fi
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-step1 BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-step1 ; fi
bench-string-with:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step1:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF)"; fi
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
bench-string-with2:
@if test -n "$${SDS}" ; then $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step2:
@if test -n "$${SDS}" ; then $(MAKE) bench-string-step3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-step3 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-with3:
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step3:
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-exe:
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BSTRLIB}" -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp -o bench-string.exe $(BENCH_DEF)
bench-string-step4:
@if test -n "$${GLIB}" ; then $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_GLIB $$(pkg-config --cflags --libs glib-2.0)" ; else $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step5:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp common.c $(BENCH_DEF) -o bench-string.exe
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string-2.cpp common.c $(BENCH_DEF) -o bench-string-2.exe
@./bench-string.exe
@./bench-string-2.exe 2000000 0
@./bench-string-2.exe 2000000 1
@./bench-string-2.exe 2000000 2
@./bench-string-2.exe 2000000 3
@./bench-string-2.exe 2000000 4
bench-string-pgo:
$(MAKE) bench-string XCFLAGS="-fprofile-generate=pgo"
$(MAKE) bench-string XCFLAGS="-fprofile-use=pgo"
############################################################################
clean:
rm -f *.o bench-*.exe bench-*.s *~ massif.out.*
$(RM) *.o bench-*.exe
$(RM) bench-*.s *~ massif.out.*
$(RM) plot-*.dat
$(RM) pgo
cd nucleotide && $(MAKE) clean
############################################################################
+6 -8
View File
@@ -6,11 +6,8 @@
#include <boost/lockfree/queue.hpp>
#include "m-mutex.h"
#define MULTI_THREAD_MEASURE
#include "common.h"
/********************************************************************************************/
boost::lockfree::queue<unsigned int, boost::lockfree::capacity<4*64>> g_buff;
boost::lockfree::queue<unsigned long long, boost::lockfree::capacity<4*64>> g_final;
@@ -51,7 +48,7 @@ static void prod(void *arg)
}
}
static void test_buffer(size_t n)
static void test_queue(size_t n)
{
const int cpu_count = get_cpu_count();
const int prod_count = cpu_count/2;
@@ -88,11 +85,12 @@ static void test_buffer(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 60)
test_function("Buffer time", 1000000, test_buffer);
test("BOOST-LOCKFREE", numberof(table), table, argc, argv);
exit(0);
}
+10 -13
View File
@@ -250,20 +250,17 @@ static void test_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 50, "Sort",10000000, 0, test_sort, 0},
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
if (n == 40)
test_function("Dict time", 1000000, test_dict);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test("CollectionC", numberof(table), table, argc, argv);
exit(0);
}
+6 -8
View File
@@ -6,11 +6,8 @@
#include "concurrentqueue.h"
#include "m-mutex.h"
#define MULTI_THREAD_MEASURE
#include "common.h"
/********************************************************************************************/
moodycamel::ConcurrentQueue<unsigned int> g_buff;
moodycamel::ConcurrentQueue<unsigned long long> g_final;
@@ -51,7 +48,7 @@ static void prod(void *arg)
}
}
static void test_buffer(size_t n)
static void test_queue(size_t n)
{
const int cpu_count = get_cpu_count();
const int prod_count = cpu_count/2;
@@ -88,11 +85,12 @@ static void test_buffer(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 60)
test_function("Buffer time", 1000000, test_buffer);
test("CONCURRENT-QUEUE", numberof(table), table, argc, argv);
exit(0);
}
+7 -7
View File
@@ -131,14 +131,14 @@ test_dict_str(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict", 1000000, 0, test_dict2, 0},
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 42)
test_function("Dict(u)time", 1000000, test_dict2);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 43)
test_function("DictS(u) time", 1000000, test_dict_str);
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
exit(0);
}
+25 -7
View File
@@ -42,6 +42,23 @@ test_dict2(size_t n)
g_result = s;
}
static void
test_dict2_linear(size_t n)
{
flat_hash_map<unsigned long, unsigned long> dict;
for (size_t i = 0; i < n; i++) {
dict[i] = rand_get();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
flat_hash_map<unsigned long, unsigned long>::iterator it = dict.find(i);
if (it != dict.end())
s += it->second;
}
g_result = s;
}
/********************************************************************************************/
struct char_array_s {
char a[256];
@@ -108,14 +125,15 @@ test_dict_str(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict", 1000000, 0, test_dict2, 0},
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 42)
test_function("Dict(u)time", 1000000, test_dict2);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 43)
test_function("DictS(u) time", 1000000, test_dict_str);
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
exit(0);
}
+10 -13
View File
@@ -186,20 +186,17 @@ static void test_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 50, "Sort",10000000, 0, test_sort, 0},
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
if (n == 40)
test_function("Dict time", 1000000, test_dict);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test("GLIB", numberof(table), table, argc, argv);
exit(0);
}
+9 -8
View File
@@ -15,7 +15,7 @@
#include <stdbool.h>
#include <string.h>
#include "src/hopscotch_map.h"
#include "hopscotch_map.h"
using namespace tsl;
using namespace std;
@@ -109,14 +109,15 @@ test_dict_str(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict", 1000000, 0, test_dict2, 0},
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 42)
test_function("Dict(u)time", 1000000, test_dict2);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 43)
test_function("DictS(u) time", 1000000, test_dict_str);
test("HOPSCOTCHMAP", numberof(table), table, argc, argv);
exit(0);
}
+10 -13
View File
@@ -185,21 +185,18 @@ static void test_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 50, "Sort",10000000, 0, test_sort, 0},
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]): 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("B-tree time", 1000000, test_rbtree);
if (n == 40)
test_function("DictOA time", 1000000, test_dict);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test("KLIB", numberof(table), table, argc, argv);
exit(0);
}
+28 -18
View File
@@ -45,6 +45,9 @@ static size_t hash_func (void *a)
static int equal_func(void *a, void *b)
{
const unsigned long *pa = a;
if (b == NULL) {
return *pa == -1U;
}
const unsigned long *pb = b;
return *pa == *pb;
}
@@ -52,8 +55,12 @@ static int equal_func(void *a, void *b)
static void set_func(void *a, void *b)
{
unsigned long *pa = a;
const unsigned long *pb = b;
*pa = *pb;
if (b == NULL) {
*pa = -1U;
} else {
const unsigned long *pb = b;
*pa = *pb;
}
}
typedef struct map_element map_element;
@@ -67,10 +74,10 @@ static void
test_dict(size_t n)
{
map dict;
map_construct(&dict, sizeof(map_element), (map_element[]) {{.key = -1}}, set_func);
map_construct(&dict, sizeof(map_element), set_func);
for (size_t i = 0; i < n; i++) {
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, equal_func, set_func, NULL);
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, set_func, equal_func, NULL);
}
rand_init();
unsigned int s = 0;
@@ -90,6 +97,8 @@ typedef char char_array_t[256];
static int char_equal (void *a, void *b)
{
const char_array_t *pa = (const char_array_t *)a;
if (b == NULL)
return strcmp(*pa, "") == 0;
const char_array_t *pb = (const char_array_t *)b;
return strcmp(*pa,*pb)==0;
}
@@ -103,8 +112,12 @@ static size_t char_hash(void *a)
static void char_set(void *a, void *b)
{
char_array_t *pa = (char_array_t *)a;
const char_array_t *pb = (const char_array_t *)b;
strcpy(*pa,*pb);
if (b == NULL) {
strcpy(*pa, "");
} else {
const char_array_t *pb = (const char_array_t *)b;
strcpy(*pa,*pb);
}
}
typedef struct big_map_element big_map_element;
@@ -118,16 +131,13 @@ static void
test_dict_big(size_t n)
{
map dict;
big_map_element empty;
strcpy(empty.key, "");
strcpy(empty.value, "");
map_construct(&dict, sizeof(big_map_element), &empty, char_set);
map_construct(&dict, sizeof(big_map_element), char_set);
for (size_t i = 0; i < n; i++) {
big_map_element el;
sprintf(el.key, "%u", rand_get());
sprintf(el.value, "%u", rand_get());
map_insert(&dict, &el, char_hash, char_equal, char_set, NULL);
map_insert(&dict, &el, char_hash, char_set, char_equal, NULL);
}
rand_init();
unsigned int s = 0;
@@ -144,14 +154,14 @@ test_dict_big(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 20, "Array", 100000000, 0, test_array, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 40)
test_function("Dict time", 1000000, test_dict);
if (n == 41)
test_function("Dict B time", 1000000, test_dict_big);
test("LIBDYNAMIC", numberof(table), table, argc, argv);
exit(0);
}
+14 -28
View File
@@ -6,11 +6,8 @@
#include "liblfds711.h"
#include "m-mutex.h"
#define MULTI_THREAD_MEASURE
#include "common.h"
/********************************************************************************************/
struct lfds711_queue_bmm_state g_buff;
@@ -23,12 +20,9 @@ static void final(void *arg)
unsigned long long j, s = 0;
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
for(int i = 0; i < n;i++) {
int rv;
do {
void *p;
rv = lfds711_queue_bmm_dequeue(&g_final, &p, NULL);
j = (uintptr_t)p;
} while (rv == 0);
void *p;
while (0 == lfds711_queue_bmm_dequeue(&g_final, &p, NULL)) { m_thread_yield(); }
j = (uintptr_t)p;
s += j;
}
g_result = s;
@@ -42,18 +36,12 @@ static void conso(void *arg)
unsigned long long s = 0;
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
for(int i = 0; i < n;i++) {
int rv;
do {
void *p;
rv = lfds711_queue_bmm_dequeue(&g_buff, &p, NULL);
j = (uintptr_t) p;
} while (rv == 0);
void *p;
while (0 == lfds711_queue_bmm_dequeue(&g_buff, &p, NULL)) { m_thread_yield(); }
j = (uintptr_t) p;
s += j;
}
int rv;
do {
rv = lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL);
} while (rv == 0);
while (0 == lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL)) { m_thread_yield(); }
}
static void prod(void *arg)
@@ -63,15 +51,12 @@ static void prod(void *arg)
size_t r = n;
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
for(unsigned int i = 0; i < n;i++) {
int rv;
do {
rv = lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL);
} while (rv == 0);
while (0 == lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL)) { m_thread_yield(); }
r = r * 31421U + 6927U;
}
}
static void test_buffer(size_t n)
static void test_queue(size_t n)
{
const int cpu_count = get_cpu_count();
const int prod_count = cpu_count/2;
@@ -120,11 +105,12 @@ static void test_buffer(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 60)
test_function("Buffer time", 1000000, test_buffer);
test("LIBLFDS", numberof(table), table, argc, argv);
exit(0);
}
+14 -2
View File
@@ -60,13 +60,25 @@ static void test_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 20, "Array", 100000000, 0, test_array, 0},
{ 50, "sort", 10000000, 0, test_sort, 0}
};
int main(int argc, const char *argv[])
{
test("LIBSRT", numberof(table), table, argc, argv);
exit(0);
}
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 20)
test_function("Array time", 100000000, test_array);
test_function("Array", 100000000, test_array);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test_function("Sort", 10000000, test_sort);
exit(0);
}
+170 -56
View File
@@ -220,14 +220,36 @@ test_dict_oa(size_t n)
}
}
/********************************************************************************************/
static void
test_dict_oa_linear(size_t n)
{
M_LET(dict, DICT_OPLIST(dict_oa_ulong)) {
for (size_t i = 0; i < n; i++) {
dict_oa_ulong_set_at(dict, i, rand_get() );
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long *p = dict_oa_ulong_get(dict, i);
if (p)
s += *p;
}
g_result = s;
}
}
typedef char char_array_t[256];
/********************************************************************************************/
#ifndef CHAR_ARRAY_SIZE
#define CHAR_ARRAY_SIZE 256
#endif
typedef char char_array_t[CHAR_ARRAY_SIZE];
static void char_init (char_array_t a) { a[0] = 0; }
static void char_set (char_array_t a, const char_array_t b) { strcpy(a, b); }
static bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a,b)==0; }
static size_t char_hash(const char_array_t a) { return m_core_hash (a, strlen(a)); }
#define M_OPL_char_array_t() (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p))
static bool char_oor_equal_p(const char_array_t a, unsigned char n) { return a[0] == 1+n; }
static void char_oor_set(char_array_t a, unsigned char n) { a[0] = 1+n; }
#define M_OPL_char_array_t() (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p), OOR_EQUAL(char_oor_equal_p), OOR_SET(char_oor_set))
#ifdef USE_MEMPOOL
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(M_OPL_char_array_t(),MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, M_OPL_char_array_t())
@@ -235,6 +257,7 @@ DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(M_OPL_char_array_t(),MEM
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPL_char_array_t(), char_array_t, M_OPL_char_array_t())
#endif
static void
test_dict_big(size_t n)
{
@@ -264,6 +287,31 @@ test_dict_big(size_t n)
#endif
}
DICT_OA_DEF2(dict_oa_char, char_array_t, char_array_t)
static void
test_dict_oa_big(size_t n)
{
M_LET(dict, DICT_OPLIST(dict_oa_char)) {
for (size_t i = 0; i < n; i++) {
char_array_t s1, s2;
sprintf(s1, "%u", rand_get());
sprintf(s2, "%u", rand_get());
dict_oa_char_set_at(dict, s1, s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1, "%u", rand_get());
char_array_t *p = dict_oa_char_get(dict, s1);
if (p)
s ++;
}
g_result = s;
}
}
/********************************************************************************************/
#ifdef USE_MEMPOOL
@@ -441,7 +489,7 @@ static void conso_mpmc(void *arg)
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
while (!queue_uint_pop(&j, g_buff_mpmc));
while (!queue_uint_pop(&j, g_buff_mpmc)) m_thread_yield();
s += j;
}
while (!queue_ull_push(g_final_mpmc, s));
@@ -453,7 +501,7 @@ static void prod_mpmc(void *arg)
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!queue_uint_push(g_buff_mpmc, r ));
while (!queue_uint_push(g_buff_mpmc, r )) m_thread_yield();
r = r * 31421U + 6927U;
}
}
@@ -526,7 +574,7 @@ static void conso_spsc(void *arg)
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
while (!queue_single_uint_pop(&j, g_buff_spsc));
while (!queue_single_uint_pop(&j, g_buff_spsc)) m_thread_yield();
s += j;
}
while (!queue_single_ull_push(g_final_spsc, s));
@@ -538,7 +586,7 @@ static void prod_spsc(void *arg)
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!queue_single_uint_push(g_buff_spsc, r ));
while (!queue_single_uint_push(g_buff_spsc, r )) m_thread_yield();
r = r * 31421U + 6927U;
}
}
@@ -581,6 +629,96 @@ static void test_queue_single(size_t n)
/********************************************************************************************/
#define BULK_SIZE 20
static void final_spsc_bulk(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long j, s = 0;
for(int i = 0; i < n;i++) {
while (!queue_single_ull_pop(&j, g_final_spsc)) m_thread_yield();
s += j;
}
g_result = s;
}
static void conso_spsc_bulk(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long s = 0;
unsigned tab[BULK_SIZE];
for(int i = 0; i < n;i+= BULK_SIZE) {
unsigned k = queue_single_uint_pop_bulk(BULK_SIZE, tab, g_buff_spsc);
while (k != BULK_SIZE) {
m_thread_yield();
k += queue_single_uint_pop_bulk(BULK_SIZE-k, tab+k, g_buff_spsc);
}
for(k = 0; k < BULK_SIZE;k++)
s += tab[k];
}
while (!queue_single_ull_push(g_final_spsc, s));
}
static void prod_spsc_bulk(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
if ((n % BULK_SIZE) != 0) abort();
size_t r = n;
unsigned tab[BULK_SIZE];
for(unsigned int i = 0; i < n;i+= BULK_SIZE) {
for(unsigned k = 0; k < BULK_SIZE; k++) {
tab[k] = r;
r = r * 31421U + 6927U;
}
unsigned k = queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE, tab);
while (k != BULK_SIZE) {
m_thread_yield();
k += queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE-k, tab+k);
}
}
}
static void test_queue_single_bulk(size_t n)
{
const int cpu_count = 2;
const int prod_count = cpu_count/2;
const int conso_count = cpu_count - prod_count;
// Init
queue_single_uint_init(g_buff_spsc, 64*cpu_count);
queue_single_ull_init (g_final_spsc, 64*cpu_count);
// Create thread
m_thread_t idx_p[prod_count];
m_thread_t idx_c[conso_count];
m_thread_t idx_final;
for(int i = 0; i < prod_count; i++) {
m_thread_create (idx_p[i], prod_spsc_bulk, &n);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso_spsc_bulk, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final_spsc_bulk, &n2);
// Wait for jobs to be done.
for(int i = 0; i < prod_count; i++) {
m_thread_join(idx_p[i]);
}
for(int i = 0; i < conso_count; i++) {
m_thread_join(idx_c[i]);
}
m_thread_join(idx_final);
// Clear & quit
queue_single_ull_clear(g_final_spsc);
queue_single_uint_clear(g_buff_spsc);
}
/********************************************************************************************/
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)))
@@ -701,57 +839,33 @@ static void test_core_hash(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 11, "DPList", 10000000, 0, test_dlist, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 31, "B+tree", 1000000, 0, test_bptree, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict(OA)", 1000000, 0, test_dict_oa, 0},
{ 43,"DictStr", 1000000, 0, test_dict_str, 0},
{ 46, "dictLinear(OA)", 1000000, 0, test_dict_oa_linear, 0},
{ 47, "dictBig(OA)", 1000000, 0, test_dict_oa_big, 0},
{ 50, "Sort",10000000, 0, test_sort, 0},
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0},
{ 60,"Buffer", 1000000, 0, test_buffer, 0},
{ 61,"Queue MPMC", 1000000, 0, test_queue, 0},
{ 62,"Buffer(P=2)", SIZE_LIMIT+1000000, 0, test_buffer, 0},
{ 63,"Queue MPMC (P=2)", SIZE_LIMIT+1000000, 0, test_queue, 0},
{ 64,"Queue SPSC (P=2)", 1000000, 0, test_queue_single, 0},
{ 65,"Queue Concurrent", 1000000, 0, test_queue_concurrent, 0},
{ 66,"Queue SPSC(Bulk)", 1000000, 0, test_queue_single_bulk, 0},
{ 70,"M_HASH", 100000000, test_hash_prepare, test_hash, test_hash_final},
{ 71,"Core Hash", 100000000, test_hash_prepare, test_core_hash, test_hash_final}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 11)
test_function("DPList time",10000000, test_dlist);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
if (n == 31)
test_function("B+tree time", 1000000, test_bptree);
if (n == 40)
test_function("Dict time", 1000000, test_dict);
if (n == 42)
test_function("DictOA time", 1000000, test_dict_oa);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 43)
test_function("DictS time", 1000000, test_dict_str);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
if (n == 51)
test_function("Stable Sort time", 10000000, test_stable_sort);
if (n == 60)
test_function("Buffer time", 1000000, test_buffer);
if (n == 61)
test_function("Queue MPMC time", 1000000, test_queue);
if (n == 62)
test_function("Buffer time (P2)", SIZE_LIMIT+1000000, test_buffer);
if (n == 63)
test_function("Queue MPMC time (P2)", SIZE_LIMIT+1000000, test_queue);
if (n == 64)
test_function("Queue SPSC time", 1000000, test_queue_single);
if (n == 65)
test_function("Queue CONCURRENT time", 1000000, test_queue_concurrent);
if (n == 70) {
n = (argc > 2) ? atoi(argv[2]) : 100000000;
test_hash_prepare(n);
test_function("M_HASH time", n, test_hash);
test_hash_final();
}
if (n == 71) {
n = (argc > 2) ? atoi(argv[2]) : 100000000;
test_hash_prepare(n);
test_function("CORE_HASH time", n, test_core_hash);
test_hash_final();
}
test("M-LIB", numberof(table), table, argc, argv);
exit(0);
}
+5 -7
View File
@@ -87,15 +87,13 @@ test_dict_noalloc(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 45, "dict(noalloc)", 1000000, 0, test_dict_noalloc, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 40)
test_function("Dict time", 1000000, test_dict);
if (n == 45)
test_function("Dict No Alloc time", 1000000, test_dict_noalloc);
test("NEDTRIES", numberof(table), table, argc, argv);
exit(0);
}
+9 -7
View File
@@ -98,14 +98,16 @@ static void test_rbtree(size_t n)
/* qlibc hash functions only accept a string as the key */
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
test("QLIBC", numberof(table), table, argc, argv);
exit(0);
}
+11 -15
View File
@@ -162,22 +162,18 @@ static void test_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
{ 50, "Sort",10000000, 0, test_sort, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
if (n == 40)
test_function("Dict(m)time", 1000000, test_dict1);
if (n == 42)
test_function("Dict(u)time", 1000000, test_dict2);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test("QT", numberof(table), table, argc, argv);
exit(0);
}
+159
View File
@@ -0,0 +1,159 @@
#define NDEBUG
#include <vector>
#include <list>
#include <iterator>
#include <map>
#include <set>
#include <unordered_map>
#include <algorithm>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "rigtorp/HashMap.h"
using namespace rigtorp;
using namespace std;
#include "common.h"
/********************************************************************************************/
// Hash for using unsigned long as lookup key
struct UL_Hash {
size_t operator()(const unsigned long v) { return v * 7; }
};
// Equal comparison for using unsigned long as lookup key
struct UL_Equal {
bool operator()(const unsigned long lhs, const unsigned long rhs) {
return lhs == rhs;
}
};
static void
test_dict2(size_t n)
{
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal> dict(16, -1UL);
for (size_t i = 0; i < n; i++) {
dict[rand_get()] = rand_get();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal>::iterator it = dict.find(rand_get());
if (it != dict.end())
s += it->second;
}
g_result = s;
}
/********************************************************************************************/
struct char_array_s {
char a[256];
char_array_s () { a[0] = 0 ; }
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
};
namespace std {
template <> struct hash<char_array_s> {
std::size_t operator()(const char_array_s &k) const {
size_t hash = 0;
const char *s = k.a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
};
};
}
struct BIG_Hash {
size_t operator()(const char_array_s v) { return hash<char_array_s>()(v); }
};
struct BIG_Equal {
bool operator()(const char_array_s lhs, const char_array_s rhs) {
return lhs == rhs;
}
};
static void
test_dict_big(size_t n)
{
char_array_s empty;
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal> dict(16, empty);
for (size_t i = 0; i < n; i++) {
char_array_s s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
dict[s1] = s2;
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_s s1;
sprintf(s1.a, "%u", rand_get());
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
}
g_result = s;
}
/********************************************************************************************/
struct STR_Hash {
size_t operator()(const string v) { return hash<string>()(v); }
};
struct STR_Equal {
bool operator()(const string lhs, const string rhs) {
return lhs == rhs;
}
};
static void
test_dict_str(size_t n)
{
string empty;
HashMap<string, string, STR_Hash, STR_Equal> dict(16, empty);
for (size_t i = 0; i < n; i++) {
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
dict[s1] = s2;
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
HashMap<string, string, STR_Hash, STR_Equal>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
}
g_result = s;
}
/********************************************************************************************/
const config_func_t table[] = {
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict", 1000000, 0, test_dict2, 0},
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
};
int main(int argc, const char *argv[])
{
test("RIGTORP-HASHMAP", numberof(table), table, argc, argv);
exit(0);
}
+7 -7
View File
@@ -109,14 +109,14 @@ test_dict_str(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict", 1000000, 0, test_dict2, 0},
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 42)
test_function("Dict(u)time", 1000000, test_dict2);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 43)
test_function("DictS(u) time", 1000000, test_dict_str);
test("SPARSEPP", numberof(table), table, argc, argv);
exit(0);
}
+33 -21
View File
@@ -110,6 +110,24 @@ test_dict2(size_t n)
g_result = s;
}
static void
test_dict2_linear(size_t n)
{
unordered_map<unsigned long, unsigned long> dict;
for (size_t i = 0; i < n; i++) {
dict[i] = rand_get();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unordered_map<unsigned long, unsigned long>::iterator it = dict.find(i);
if (it != dict.end())
s += it->second;
}
g_result = s;
}
/********************************************************************************************/
struct char_array_s {
@@ -220,28 +238,22 @@ static void test_stable_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
{ 43,"DictStr(u)", 1000000, 0, test_dict_str, 0},
{ 44,"DictStr(m)", 1000000, 0, test_dict_str2, 0},
{ 46, "dictLinear(u)", 1000000, 0, test_dict2_linear, 0},
{ 50, "Sort",10000000, 0, test_sort, 0},
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
if (n == 40)
test_function("Dict(m)time", 1000000, test_dict1);
if (n == 42)
test_function("Dict(u)time", 1000000, test_dict2);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 43)
test_function("DictS(u) time", 1000000, test_dict_str);
if (n == 44)
test_function("DictS(m) time", 1000000, test_dict_str2);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
if (n == 51)
test_function("Stable Sort time", 10000000, test_stable_sort);
test("STL", numberof(table), table, argc, argv);
exit(0);
}
+338
View File
@@ -0,0 +1,338 @@
/*
* M*LIB - String Bench
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "common.h"
#include "m-string.h"
#ifdef BENCH_CAN_USE_SDS
extern "C" {
#include "sds.h"
};
#endif
#ifdef BENCH_CAN_USE_RAPIDSTRING
#include "rapidstring.h"
#endif
#ifdef BENCH_CAN_USE_BSTRLIB
#include "bstrlib.h"
#include "bstrwrap.h"
#include "bstraux.h"
#endif
/*
* BENCH
* 2000000
* 1. Generate N strings of random numbers 32 bits.
* 2. Concat all strings in a string usigned a random permutation of N.
* 3. Replace "1234" in the string into "WELL" until nothing remains.
* 4. Replace "56789" in the string into "DONE" until nothing remains.
* Return the final string length.
*/
unsigned *permutation_tab = NULL;
void random_permutation(unsigned n)
{
if (permutation_tab) free(permutation_tab);
permutation_tab = (unsigned *) malloc(n * sizeof(unsigned));
if (permutation_tab == NULL) abort();
for(unsigned i = 0; i < n; i++)
permutation_tab[i] = i;
for(unsigned i = 0; i < n; i++) {
unsigned j = rand_get() % n;
unsigned k = rand_get() % n;
unsigned l = permutation_tab[j];
permutation_tab[j] = permutation_tab[k];
permutation_tab[k] = l;
}
}
using namespace std;
size_t bench_stl(unsigned n)
{
vector<string> tab;
tab.resize(n);
for(unsigned i = 0; i < n; i++)
tab[i] = to_string(rand_get());
string str;
for(unsigned i = 0; i < n; i++) {
str += tab[permutation_tab[i]];
}
size_t pos = 0;
do {
pos = str.find("1234", pos);
if (pos != string::npos) {
str.replace (pos, 4, "WELL");
}
} while (pos != string::npos);
pos = 0;
do {
pos = str.find("56789", pos);
if (pos != string::npos) {
str.replace (pos, 5, "DONE");
}
} while (pos != string::npos);
return str.length();
}
size_t bench_mlib(unsigned n)
{
string_t *tab = (string_t*) malloc (n * sizeof (string_t));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
string_init(tab[i]);
string_printf(tab[i], "%u", rand_get());
}
// P2
string_t str;
string_init(str);
for(unsigned i = 0; i < n; i++) {
string_cat(str, tab[permutation_tab[i]]);
}
// P3
size_t i = 0;
do {
i = string_replace_str(str, "1234", "WELL", i);
} while (i != STRING_FAILURE);
// P4
i = 0;
do {
i = string_replace_str(str, "56789", "DONE", i);
} while (i != STRING_FAILURE);
size_t length = string_size(str);
// Clean
string_clear(str);
for(unsigned i = 0; i < n; i++) {
string_clear(tab[i]);
}
free(tab);
return length;
}
#ifdef BENCH_CAN_USE_BSTRLIB
size_t bench_bstrlib(unsigned n)
{
vector<CBString> tab;
tab.resize(n);
for(unsigned i = 0; i < n; i++) {
char tmp[10];
sprintf(tmp, "%u", rand_get());
tab[i] = tmp;
}
CBString str;
for(unsigned i = 0; i < n; i++) {
str += tab[permutation_tab[i]];
}
size_t pos = 0;
do {
pos = str.find("1234", pos);
if (pos != string::npos) {
str.replace (pos, 4, "WELL");
}
} while (pos != string::npos);
pos = 0;
do {
pos = str.find("56789", pos);
if (pos != string::npos) {
str.replace (pos, 5, "DONE");
}
} while (pos != string::npos);
return str.length();
}
#endif
#ifdef BENCH_CAN_USE_SDS
sds SDS_replace_at(sds str, size_t pos, size_t len, const char str2[])
{
// simple implementation as replace is not available
sds a = sdsnewlen((const void*) str, pos);
a = sdscat(a, str2);
a = sdscat(a, &str[pos+len]);
sdsfree(str);
return a;
}
size_t bench_sds(unsigned n)
{
sds *tab = (sds*) malloc (n * sizeof (sds));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
tab[i] = sdsfromlonglong(rand_get());
}
// P2
sds str = sdsempty();
for(unsigned i = 0; i < n; i++) {
str = sdscat(str, tab[permutation_tab[i]]);
}
// P3
size_t pos = 0;
do {
char *p = strstr(&str[pos], "1234");
pos = p == NULL ? -1U : (p - str);
if (pos != -1U) {
str = SDS_replace_at(str, pos, 4, "WELL");
}
} while (pos != -1U);
// P4
pos = 0;
do {
char *p = strstr(&str[pos], "56789");
pos = p == NULL ? -1U : (p - str);
if (pos != -1U) {
str = SDS_replace_at(str, pos, 5, "DONE");
}
} while (pos != -1U);
size_t length = sdslen(str);
// Clean
sdsfree(str);
for(unsigned i = 0; i < n; i++) {
sdsfree(tab[i]);
}
free(tab);
return length;
}
#endif
#ifdef BENCH_CAN_USE_RAPIDSTRING
void RAPIDSTRING_replace_at(rapidstring *str, size_t pos, size_t len, const char str2[])
{
// simple implementation as replace is not available
rapidstring a;
rs_init_w_n(&a, rs_data(str), pos);
rs_cat(&a, str2);
rs_cat(&a, &rs_data(str)[pos+len] );
rs_free(str);
*str = a;
}
size_t bench_rapidstring(unsigned n)
{
rapidstring *tab = (rapidstring*) malloc (n * sizeof (rapidstring));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
char tmp[10];
sprintf(tmp, "%u", rand_get());
rs_init_w(&tab[i], tmp);
}
// P2
rapidstring str;
rs_init(&str);
for(unsigned i = 0; i < n; i++) {
rs_cat_rs(&str, &tab[permutation_tab[i]]);
}
// P3
size_t pos = 0;
do {
char *p = strstr(&rs_data(&str)[pos], "1234");
pos = p == NULL ? -1U : (p - rs_data(&str));
if (pos != -1U) {
RAPIDSTRING_replace_at(&str, pos, 4, "WELL");
}
} while (pos != -1U);
// P4
pos = 0;
do {
char *p = strstr(&rs_data(&str)[pos], "56789");
pos = p == NULL ? -1U : (p - rs_data(&str));
if (pos != -1U) {
RAPIDSTRING_replace_at(&str, pos, 5, "DONE");
}
} while (pos != -1U);
size_t length = rs_len(&str);
// Clean
rs_free(&str);
for(unsigned i = 0; i < n; i++) {
rs_free(&tab[i]);
}
free(tab);
return length;
}
#endif
int main(int argc, const char *argv[])
{
unsigned length, n = atoi(argv[1]);
unsigned select = atoi(argv[2]);
const char *name;
random_permutation(n);
rand_init();
unsigned long long t0 = cputime();
switch (select) {
case 0:
length = bench_mlib(n);
name = "MLIB";
break;
case 1:
length = bench_stl(n);
name = "STL";
break;
#ifdef BENCH_CAN_USE_SDS
case 2:
length = bench_sds(n);
name = "SDS";
break;
#endif
#ifdef BENCH_CAN_USE_RAPIDSTRING
case 3:
length = bench_rapidstring(n);
name = "RAPID";
break;
#endif
#ifdef BENCH_CAN_USE_BSTRLIB
case 4:
length = bench_bstrlib(n);
name = "BSTRLIB";
break;
#endif
default:
exit(0);
}
unsigned long long t1 = cputime();
printf("%5.5s LENGTH=%u T= %f s\n", name, length, (t1-t0) / 1000000.0 );
return 0;
}
+522 -169
View File
File diff suppressed because it is too large. Load diff
+9 -11
View File
@@ -206,18 +206,16 @@ test_dict_big(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 30)
test_function("Rbtree time", 1000000, test_rbtree);
if (n == 40)
test_function("Dict time", 1000000, test_dict);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
test("TommyDS", numberof(table), table, argc, argv);
exit(0);
}
+10 -12
View File
@@ -199,19 +199,17 @@ static void test_sort(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0}
{ 50, "sort", 10000000, 0, test_sort, 0}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]): 0;
if (n == 10)
test_function("List time",10000000, test_list);
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 40 || n == 42)
test_function("Dict time", 1000000, test_dict);
if (n == 41)
test_function("DictB time", 1000000, test_dict_big);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test("UT-HASH", numberof(table), table, argc, argv);
exit(0);
}
+4 -9
View File
@@ -31,17 +31,12 @@ static void test_hash(size_t n)
/********************************************************************************************/
const config_func_t table[] = {
{ 70,"XXHASH64", 100000000, test_hash_prepare, test_hash, test_hash_final}
};
int main(int argc, const char *argv[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 70) {
n = (argc > 2) ? atoi(argv[2]) : 100000000;
test_hash_prepare(n);
test_function("XXHASH64 time", n, test_hash);
test_hash_final();
}
test("XXHASH", numberof(table), table, argc, argv);
exit(0);
}
+196
View File
@@ -0,0 +1,196 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include "common.h"
// The global result
unsigned long g_result;
// The rand value
#if defined(MULTI_THREAD_MEASURE)
M_THREAD_ATTR
#endif
unsigned int randValue = 0;
void compiler_barrier(void *p)
{
// Do nothing, but let the compiler thinks that we did.
(void) p;
// Can be used for FLTO building
// asm volatile("": "+r" (p) : :"memory");
}
struct parse_opt_s {
int test_function;
double from, to, step, grow;
unsigned repeat;
bool graph, best, average, quiet;
};
static void
parse_config(struct parse_opt_s *opt, int argc, const char *argv[])
{
char *end;
opt->test_function = 0;
opt->from = 0.0;
opt->to = 0.0;
opt->step = 0.0;
opt->grow = 1.1;
opt->graph = false;
opt->best = false;
opt->average = false;
opt->quiet = false;
opt->repeat = 1;
for(int i = 1; i < argc ; i++)
{
if (argv[i][0] == '-') {
if (strcmp(argv[i], "--from") == 0){
i++;
opt->from = strtod(argv[i], &end);
opt->to = opt->from;
} else if (strcmp(argv[i], "--to") == 0) {
i++;
opt->to = strtod(argv[i], &end);
opt->grow = 1.1;
} else if (strcmp(argv[i], "--step") == 0) {
i++;
opt->step = strtod(argv[i], &end);
opt->grow = 0;
} else if (strcmp(argv[i], "--grow") == 0) {
i++;
opt->grow = strtod(argv[i], &end);
opt->step = 0;
} else if (strcmp(argv[i], "--repeat") == 0) {
i++;
opt->repeat = strtol(argv[i], &end, 10);
} else if (strcmp(argv[i], "--graph") == 0) {
opt->graph = true;
} else if (strcmp(argv[i], "--best") == 0) {
opt->best = true;
opt->average = false;
} else if (strcmp(argv[i], "--average") == 0) {
opt->average = true;
opt->best = false;
} else if (strcmp(argv[i], "--quiet") == 0) {
opt->quiet = true;
} else {
fprintf(stderr, "ERROR: Option unkown: %s.\n",
argv[i]);
exit(-1);
}
if (*end != 0) {
fprintf(stderr, "ERROR: Cannot parse %s. Expected float\n",
argv[i]);
exit(-1);
}
} else {
opt->test_function = strtol (argv[i], &end, 10);
if (*end != 0) {
fprintf(stderr, "ERROR: Cannot parse %s. Expected function number\n",
argv[i]);
exit(-1);
}
}
}
}
static int
select_config(int func, size_t n, const config_func_t functions[])
{
for(size_t i = 0; i < n;i++) {
if (functions[i].num == func)
return i;
}
fprintf(stderr, "ERROR: Function number %d not found. Available functions are:\n", func);
for(size_t i = 0; i < n;i++) {
fprintf(stderr, " %d: %s\n", functions[i].num, functions[i].funcname);
}
fprintf(stderr, "USAGE: FUNC_NUMBER [--from number --to number (--grow number | --step number)] [--best|--average] [--quiet|--graph]\n");
exit(-1);
}
void
test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[])
{
// options
// NUMBER
// --from NUMBER --to NUMBER --step NUMBER --grow NUMBER
// --graph --best --average --quiet
// --repeat N
struct parse_opt_s arg;
parse_config(&arg, argc, argv);
int i = select_config(arg.test_function, n, functions);
double from = arg.from == 0 ? functions[i].default_n : arg.from;
double to = arg.from == 0 ? functions[i].default_n : arg.to;
FILE *graph_file = NULL;
if (arg.graph) {
char filename[100];
sprintf(filename, "plot-%s-%d.dat", library, arg.test_function);
graph_file = fopen(filename, "wt");
if (!graph_file) {
fprintf(stderr, "ERROR: Cannot create plot.dat\n");
exit(-2);
}
fprintf(graph_file, "# plotting %s-%d : %s\n# N T", library, arg.test_function, functions[i].funcname);
}
// Do the bench
for(double n = from; n <= to ; n = (arg.grow == 0 ? n + arg.step : n * arg.grow))
{
double best = (1.0/0.0);
double avg = 0.0;
if (functions[i].init != NULL)
functions[i].init(n);
for(unsigned r = 0; r < arg.repeat; r++) {
double t0 = test_function(arg.graph|arg.best|arg.average|arg.quiet ? NULL : functions[i].funcname, (size_t) n, functions[i].func);
best = t0 < best ? t0 : best;
avg += t0;
}
if (functions[i].clear != NULL)
functions[i].clear();
avg /= arg.repeat;
if (arg.graph) {
fprintf(graph_file, "%f %f\n", n, arg.average ? avg : best);
} else if (arg.quiet) {
printf("%lu\n", (unsigned long)(arg.average ? avg : best));
} else if (arg.repeat > 1) {
if (arg.average == false)
printf ("%20.20s time %lu ms for n = %lu *** BEST ***\n", functions[i].funcname, (unsigned long) best, (unsigned long) n);
if (arg.best == false)
printf ("%20.20s time %lu ms for n = %lu *** AVERAGE ***\n", functions[i].funcname, (unsigned long) avg, (unsigned long) n);
}
}
if (arg.graph) {
fclose(graph_file);
printf("File plot-%s-%d.dat generated.\n"
"Run in gnuplot the following command:\n"
"\tplot 'plot-%s-%d.dat' with linespoints linestyle 1\n",
library, arg.test_function, library, arg.test_function);
}
return;
}
+81 -32
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -25,35 +25,59 @@
#ifndef __COMMON_BENCH__H
#define __COMMON_BENCH__H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Needed for M_THREAD_ATTR
#if defined(MULTI_THREAD_MEASURE)
#include "m-mutex.h"
#endif
#if defined(_WIN32)
# include <windows.h>
#elif (defined(__APPLE__) && defined(__MACH__)) \
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
# include <sys/types.h>
# include <sys/resource.h>
# include <sys/time.h>
# include <sys/param.h>
# include <sys/sysctl.h>
# define USE_SYSCTL
#else
# include <sys/types.h>
# include <sys/resource.h>
# include <sys/time.h>
# include <unistd.h>
#endif
// Number of elements of a table
#define numberof(x) (sizeof(x)/sizeof(x[0]))
/* Get the current CPU time. */
#if defined(_WIN32)
/* WINDOWS variant */
#include <windows.h>
static unsigned long long
static inline unsigned long long
cputime (void)
{
LARGE_INTEGER freq, val;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&val);
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
LARGE_INTEGER freq, val;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&val);
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
}
#else
/* UNIX variant */
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
static unsigned long long
static inline unsigned long long
cputime (void)
{
#if defined(MULTI_THREAD_MEASURE)
// Multi thread
// Multi thread: Uses real time
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec * 1000000ULL + tv.tv_usec;
#else
// Single thread
// Single thread: Uses process usage time
struct rusage rus;
getrusage (0, &rus);
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
@@ -62,41 +86,61 @@ cputime (void)
#endif
unsigned long g_result;
unsigned int randValue = 0;
// The result of the bench
extern unsigned long g_result;
static void rand_init(void)
// The rand value
extern
#if defined(MULTI_THREAD_MEASURE)
M_THREAD_ATTR
#endif
unsigned int randValue;
// Pseudo rand value generator.
// Not very random but sufficient and fast enough for the bench
static inline void rand_init(void)
{
randValue = 0;
}
static unsigned int rand_get(void)
static inline unsigned int rand_get(void)
{
randValue = randValue * 31421U + 6927U;
return randValue;
}
static void test_function(const char str[], size_t n, void (*func)(size_t))
// The function that performs the bench
static inline double
test_function(const char *str, size_t n, void (*func)(size_t))
{
double result;
unsigned long long start, end;
rand_init();
start = cputime();
(*func)(n);
end = cputime();
result = (double)(end-start) / 1000.0;
end = (end - start) / 1000U;
printf ("%s %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
if (str != NULL) {
printf ("%20.20s time %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
}
return result;
}
#if defined(_WIN32)
# include <windows.h>
#elif (defined(__APPLE__) && defined(__MACH__)) \
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
# include <sys/param.h>
# include <sys/sysctl.h>
# define USE_SYSCTL
#else
# include <unistd.h>
// Configuration table
typedef struct {
int num;
const char *funcname;
size_t default_n;
void (*init)(size_t);
void (*func)(size_t);
void (*clear)(void);
} config_func_t;
// Perform a parsing of the given arguments and do the bench according to the table
#ifdef __cplusplus
extern "C"
#endif
void test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[]);
/* Return the number of CPU of the system */
static inline int get_cpu_count(void)
@@ -120,4 +164,9 @@ static inline int get_cpu_count(void)
#endif
}
#ifdef __cplusplus
extern "C"
#endif
void compiler_barrier(void *p);
#endif
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -59,7 +59,7 @@ static void find_max(void)
dict_int64_set_at (dict, get_rand(), get_rand());
n++;
if ((n & ((1<<20)-1)) == 0) {
printf ("n=%lu\n", n);
printf ("n=%zu\n", n);
}
}
dict_int64_clear(dict);
+4
View File
@@ -35,3 +35,7 @@ V0.2.*: * worker_start takes a 'worker_t' as an additional parameter
The provided _remove doesn't use an iterator but a 'key', incompatible with the one of array or list.
But is like the one provided by the priority queue.
V0.3.*: * Remove B+TREE _remove method as it has been deprecated (use _erase instead)
* Remove DICT _remove method as it has been deprecated (use _erase instead)
* Make _it_to_p method of B+TREE / RB-TREE as deprecated (uses _it_until_p instead ).
BIN
View File
Binary file not shown.
+37
View File
@@ -62,3 +62,40 @@ This will have the following gains:
Once one issue of ISSUES.org is taken into account,
its state shall be changed to DONE (the issue shall not be removed).
Debugging
===============
Debugging the library can be quite difficult
as it performs heavy preprocessing expansion
and expands all code in one line.
Different strategy for debugging the library itself are still possible:
1) Good old printf.
Add printf of interresting values in the debugged function.
2) Preprocess the library and then compile the preprocessed file:
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
The resulting executable can be debugged much more easily.
3) To debug the preprocessing itself, the Boot Wave library can be used
and in particular the [wave driver](https://www.boost.org/doc/libs/1_71_0/libs/wave/doc/wave_driver.html)
Add in the code the following pragmas around the code to debug:
#pragma wave trace(enable)
ARRAY_DEF (array_charp, char *)
#pragma wave trace(disable)
Run the wave driver:
./wave ex-array01.c -I .. --c99 -t test.trace
The different expansion performed by the library are described in test.trace
+172 -40
View File
@@ -1,29 +1,86 @@
*Long term issues*
------------------
* TODO #21: Generic Binary serialization :ENHANCEMENT:
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
Some kind of "binary serialization" on the model of get_str/parse_str
could be possible. It would be a great feature from the application
point-of-view: binary representation is more bandwidth-efficient if
used on network communications.
Specialize Hashmap for huge objects on supported hardware:
It will be good to have import/export methods to the
XML/JSON/MSGPACK/PROTOBUF/BINARY format.
However, adding all of them on by one in the M*LIB containers
doesn't seem satisfactory.
Instead, adding a generic interface for the serialization of data
that may be customized by the user to perform the import/export of
objects in whatever format they want into what they want (FILE/memory/...).
To simplify it, this interface could only support one kind of import/export
per compilation unit.
based on OA hash map of taggued pointer (needs space for tagging)
low bits = low bits of hash
pointer to object { key, hash, value }
Problem:
the interface is either too high level (limiting too much what we can do)
or too low level (exporting too many internal things of the library).
Can detect collision without dereferencing object with 1/2^lowbits chances.
Reduce cache usage ==> Shall be faster.
Use of pointer to avoid moving object on GC.
* TODO #25 : Support of error code model for error handling. :ENHANCEMENT:
Find a way to support error return code for the API in case of allocation
failure.
==> Any service that returns void shall return a "int".
==> In case of allocation failure, it shall return an error.
==> M_CALL macro shall stop its execution if the service returns an error code
and the error code represents an error (avoid rewritting everything)
and throw back the error code (stopping the execution flow).
services returning already something shall not be modified.
This model should be applied at the container level only and not globally.
Different containers may need different levels of error handling.
New operator: ERROR_MODEL which value is 0 (abort on error), 1 (throw on error)
or 2 (return code on error).
Find a way to make it while not making the code too complex
(try to keep as simple as possible).
* TODO #24 : Add MIN-MAX-HEAP container :ENHANCEMENT:
See https://en.wikipedia.org/wiki/Min-max_heap
as DPRIORITY_QUEUE_DEF ?
NOTE: Needs for such container?
* TODO #23: Force MOVE semantic :ENHANCEMENT:
Some type may need to have a force MOVE semantic (for example, they can store
pointer to themselves). Currently the INIT_MOVE & MOVE operators are more
a help for performance than a strict semantic usage.
** ARRAY container
The ARRAY container doesn't support strict MOVE semantic for example.
It is not a simple matter as it performs a realloc of the table, thus
moving the data before they can be moved using MOVE. Two solutions:
- New operator MOVE_SELF to fix a type after it has been moved.
- If MOVE defined, force another table and then copy by hand the type. This will be slower and consumme more memory.
** MOVE in OPLIST :ENHANCEMENT:
Proposal: do not export INIT_MOVE / MOVE operator in OPLIST if the MOVE operator is
compatible with a pure COPY semantic. An exported MOVE operator will tell
other containers than the type shall be carefully moved using the provided
MOVE operator.
For example for tuple, it shall
- create an init_move operator if no one has disabled INIT_MOVE,
- export the init_move operator if at least one has exported a INIT_MOVE and no one has disabled INIT_MOVE.
** DO_INIT_MOVE operator
DO_INIT_MOVE macro is not also fully working for structure
defined with [1] tricks but without an explicit INIT_MOVE / MOVE
operators as it uses MOVE_DEFAULT which is not (fully compatible).
==> Analyse limitation and possible constraint usages.
* TODO #20: Bucket priority queue :ENHANCEMENT:
@@ -46,6 +103,7 @@ implementation.
* TODO #19: Intrusive Red Black Tree :ENHANCEMENT:
Add intrusive red black tree.
Look also for AVL tree (NOTE: Is there a performance difference between the two?)
* TODO #18: Missing methods :ENHANCEMENT:
@@ -59,16 +117,17 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
A ressouce handler which shall associated a unique handle to a ressource.
The handle shall be unique for the ressource and shall not be reused.
It is typically a 64 bits integers always incremented (even the program
It is typically a 64 bits integers always incremented (even if the program
creates one billion ressources per second, the counter won't overflow
until 585 years).
The ressource handler shall also keep count on:
The ressource handler shall make an association between a HANDLE 64 bits and:
- how much real owners claim to own the ressource
(the ressource is only owned by the ressource handler, however
it acts as delegate of the real owner),
it acts as a delegate of the real owner),
- how much users keep a pointer to the ressource.
- pointer to the resource itself.
This may be a better alternative than shared_ptr & weak_ptr:
@@ -77,7 +136,12 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
- shared_ref & weak_ref becomes only HANDLE,
- all ressources can be freed in one pass.
Needs lock free dictionnary.
Needs lock free dictionnary or at least concurrent dictionnary.
How to handle multiple resource ?
* variant: works fine. Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
* embedded the type in the ressource handler: more work, API more complex. Memory usage seems better.
* TODO #16: Lock Free List :ENHANCEMENT:
@@ -85,28 +149,30 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
Implement a lock free list. Most of the difficulty is the memory reclamation part.
Typically this lock free list shall be compatible with RCU method.
A node can be in the following state:
** First step: backoff methods :DONE:
** Second step: lock free node pool : :DONE:
Done as m-c-mempool header.
- FREE: Can be used for allocation.
- IN_USE: The node is currently used in the list.
- TO_BE_RECLAIMED: The node is semantically removed, but some others may still be used it.
- CAN_BE_RECLAIMED: The node is semantically removed and no one still use it.
** Third step: Implement generic lock free list on top of it.
So separate lists shall be used for the memory handler.
It shall also be compatible with NUMA and avoid putting all nodes in the same lists if possible:
otherwise the head will become a hot spot and slow down everything.
The ABA problem is already taken into account by the memory alloctor
provided that the lock free list doesn't try to be smart.
backoff has be used when using CAS.
The ABA problem shall also be taken into account. Normaly with the provided state of the nodes,
it should not be a problem.
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be taken into account.
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be crefully analyzed when taken into account.
Questions:
- singly or doubly or dual push?
- needs to be logically deleted : needs a previous field
(NULL if not logically deleted) ? TBC
* TODO #15: Constructor / Destructor Prologue / Epilogue for Stack Exception Handling :ENHANCEMENT:
* TODO #15: Prologue / Epilogue for Constructor / Destructor for error handling :ENHANCEMENT:
Constructor (and destructor) need to use user-defined prologue / epilogue.
This is in order to register the constructed object into a proper Stack Exception Handling so that exceptions throwing may work reliably.
This is in order to register the constructed object into a proper Exception
Handling Stack so that throwing exceptions may work reliably.
Proposal:
@@ -115,15 +181,18 @@ Proposal:
- M_DESTRUCTOR_PROLOGUE(object, oplist);
- M_DESTRUCTOR_EPILOGUE(object, oplist);
Object creation will need to add all sub-objects into the stack, then unstack all to push instead the root object (which will recursively remove them).
Object creation will need to add all sub-objects into the stack,
then unstack all to push instead the root object (which recursively remove them).
See also http://freetype.sourceforge.net/david/reliable-c.html#cseh
How to handle like allocation of the object?
How to avoid calling the destructor multiple times? (It is needed?)
* TODO #14: Memory allocation enhancement :ENHANCEMENT:
Enhancement of the memory allocation scheme to find way to deal properly with advance allocators:
Enhancement of the memory allocation scheme to find way to deal properly with advanced allocators:
- non-default alignment requirements for types,
- instance-based allocator (may need instance based variable access),
@@ -144,6 +213,17 @@ Update:
API transformation support enables "instance-based allocator" to be made easily.
Needs some formal operator in the oplist to support it fully and an example.
Can be supported using another API extension, some more operators and forcing some names:
* API_N: call like FUNC(obj->extra_data, type)
'obj' is a forced named corresponding to an alias to an object in the function.
Operator needed:
- EXTRA_DATA: Add an extra-data field wihtin the container. Defines the type of data.
It is a kind of object inheritance where the container inherits some extra data from its base.
* TODO #12: Atomic shared pointer :ENHANCEMENT:
@@ -162,7 +242,10 @@ No _ref or direct _init: we need to init first a normal shared_ptr then the atom
** _atomic_set method:
It can be implemented by incrementing the non atomic shared pointer reference, then performs a compare_and_swap to the data of the atomic shared pointer, finally decrement and dec/free the swapped previous data of the atomic .
It can be implemented by incrementing the non atomic shared pointer reference,
then performs a compare_and_swap to the data of the atomic shared pointer,
finally decrement and dec/free the swapped previous data of the atomic like a normal shared pointer.
All 3 steps are safe.
** _set_atomic method:
@@ -261,3 +344,52 @@ Another solution is to create a huge list of items which is:
Hash table will only give quick access to items to this list. Expanding the table won't change the order of the list (so the items will remain at the same place and always accessible by other threads) but just add sentinel in the sentinel at the right place (there won't be any reallocation of the list). Inserting will need to insert an item at the right place.
Not sure it will really help. To analyse.
Concurrent dictionary is possible now with CONCURRENT_DEF + DICT_DEF.
But it uses a global lock for all dictionary access.
** Open Addressing Indirect Table with a lock free node pool used as transaction pool.
A good way maybe Open Addessing table used only for indirection and a freelist memory reclamation container for handle the entries
(like a transaction).
* DONE #22: Enhanced services for SPSC Queue :ENHANCEMENT:
Add services:
** DONE _push_bulk
Test the capacity of the queue and push as much as possible in the queue
with one check of the atomic structure.
** DONE _pop_bulk
Test the capacity of the queue and push as much as possible in the queue
with one check of the atomic structure.
** DONE _push_force:
If the capacity of the queue is full, pop one element and push it:
push always succeed and the queue always keep the youngest element.
* DONE #21: Generic Binary serialization :ENHANCEMENT:
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
Some kind of "binary serialization" on the model of get_str/parse_str
could be possible. It would be a great feature from the application
point-of-view: binary representation is more bandwidth-efficient if
used on network communications.
It will be good to have import/export methods to the
XML/JSON/MSGPACK/PROTOBUF/BINARY format.
However, adding all of them on by one in the M*LIB containers
doesn't seem satisfactory.
Instead, adding a generic interface for the serialization of data
that may be customized by the user to perform the import/export of
objects in whatever format they want into what they want (FILE/memory/...).
To simplify it, this interface could only support one kind of import/export
per compilation unit.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 99 KiB

+10 -4
View File
@@ -1,18 +1,24 @@
CC=cc -std=c99
CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function
GMP_DIR=
CPPFLAGS=-I..
LDFLAGS=-pthread -lgmp
LDFLAGS=-pthread
RM=rm -f
.SUFFIXES: .c .exe
.c.exe:
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -o $@
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) `cat config` $< -o $@
all:
all: config
$(MAKE) `ls ex-*.c|sed -e 's/\.c/\.exe/g'`
config:
printf "#include <gmp.h>\nint main(void){mpz_t z; mpz_init(z); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp config-test.c -o config-test.exe 2> /dev/null && echo "-DHAVE_GMP=1 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp" > config || echo "-DHAVE_GMP=0" > config
$(RM) config-test.*
clean:
$(RM) *.exe *.s *~ *.o ./a.dat
$(RM) *.exe *.s *~ *.o ./a.dat config
+619
View File
@@ -0,0 +1,619 @@
/* Include M*LIB header */
#include "m-array.h"
#include "m-string.h"
#include "m-tuple.h"
/* Provide several examples of different types usage with array,
with the oplist to use for each case.
Each time it creates and initializes an array,
fills it with some number,
computes its sum,
and returns it.
This is done for several basic C types:
- integers,
- floats,
- pointers,
- structures,
- tuples
*/
/* Generate prototypes and inline functions
for basic types.
For integer and floats, there is no need to specify
an oplist. M_DEFAULT_OPLIST will be used.
*/
ARRAY_DEF(r_bool, bool)
/* Basic usage of the type */
static int
test_bool(int n)
{
r_bool_t array;
r_bool_init(array);
for(int i = 0; i < n; i++) {
r_bool_push_back(array, i & 1);
}
int s = 0;
for(int i = 0; i < n; i++) {
s += *r_bool_get(array, i);
}
r_bool_clear(array);
return s;
}
/* Same with type char */
ARRAY_DEF(r_char, char)
static int
test_char(int n)
{
r_char_t array;
r_char_init(array);
for(int i = 0; i < n; i++) {
r_char_push_back(array, i & 255);
}
int s = 0;
for(int i = 0; i < n; i++) {
s += *r_char_get(array, i);
}
r_char_clear(array);
return s;
}
/* Same with type signed int. */
ARRAY_DEF(r_int, signed int)
static int
test_int(int n)
{
r_int_t array;
r_int_init(array);
for(int i = 0; i < n; i++) {
r_int_push_back(array, i);
}
int s = 0;
for(int i = 0; i < n; i++) {
s += *r_int_get(array, i);
}
r_int_clear(array);
return s;
}
/* Same with type signed int.
But with an explicit oplist.
This generates exactly the same code than r_int
*/
ARRAY_DEF(r_int2, signed int, M_DEFAULT_OPLIST)
static int
test_int2(int n)
{
r_int2_t array;
r_int2_init(array);
for(int i = 0; i < n; i++) {
r_int2_push_back(array, i);
}
int s = 0;
for(int i = 0; i < n; i++) {
s += *r_int2_get(array, i);
}
r_int2_clear(array);
return s;
}
/* Same with type unsigned long long */
ARRAY_DEF(r_ullong, unsigned long long)
static int
test_ullong(int n)
{
r_ullong_t array;
r_ullong_init(array);
for(int i = 0; i < n; i++) {
r_ullong_push_back(array, i);
}
int s = 0;
for(int i = 0; i < n; i++) {
s += *r_ullong_get(array, i);
}
r_ullong_clear(array);
return s;
}
/* Same with type float */
ARRAY_DEF(r_float, float)
static int
test_float(int n)
{
r_float_t array;
r_float_init(array);
for(int i = 0; i < n; i++) {
r_float_push_back(array, i/4.0);
}
float s = 0;
for(int i = 0; i < n; i++) {
s += *r_float_get(array, i);
}
r_float_clear(array);
return s;
}
/* Same with type double and an explicit oplist*/
ARRAY_DEF(r_double, double, M_DEFAULT_OPLIST)
static int
test_double(int n)
{
r_double_t array;
r_double_init(array);
for(int i = 0; i < n; i++) {
r_double_push_back(array, i/4.0);
}
float s = 0;
for(int i = 0; i < n; i++) {
s += *r_double_get(array, i);
}
r_double_clear(array);
return s;
}
/* Helper function strdup */
static char *my_strdup(const char *p)
{
size_t s = strlen(p);
char *d= malloc(s+1);
if(!d) abort();
strcpy(d, p);
return d;
}
/* Same with type const char *, representing a contant string.
We *** need *** an explicit oplist to tell M*LIB
to consider the type as a constant string.
The oplist to use is M_CSTR_OPLIST.
We could have used M_PTR_OPLIST too,
but in this case, we won't get proper string order or
string equality.
*/
ARRAY_DEF(r_cstring, const char *, M_CSTR_OPLIST)
static int
test_cstring(int n)
{
r_cstring_t array;
r_cstring_init(array);
for(int i = 0; i < n; i++) {
char buffer[10];
// Create a string from the integer
sprintf(buffer, "%d", i);
// We need to make an explicit copy of the string
// since it is not supported by the basic type.
char *p = my_strdup(buffer);
// Push the copy in the container.
r_cstring_push_back(array, p);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get back the string
const char *p = *r_cstring_get(array, i);
// Convert it to integer
int j = atoi(p);
s += j;
}
// We also need a special unalloc pass
// to free all constant strings we have allocated.
for(int i = 0; i < n; i++) {
const char *p = *r_cstring_get(array, i);
free((void*)(uintptr_t)p);
}
r_cstring_clear(array);
return s;
}
/* Same with type string_t of M*LIB, representing a variable string.
We don't need explicit oplist to tell M*LIB!
The oplist to use is STRING_OPLIST,
but it has been registered globaly, so we don't need to explicit
give it to array_def! */
ARRAY_DEF(r_string, string_t)
static int
test_string(int n)
{
r_string_t array;
string_t str;
r_string_init(array);
string_init (str);
for(int i = 0; i < n; i++) {
// create a string from an integer
string_printf(str, "%d", i);
// No need to make a copy in the container!
r_string_push_back(array, str);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get back the string and convert it back to an integer
int j = atoi(string_get_cstr(*r_string_get(array, i)));
s += j;
}
// No need to explicit dealloc !
string_clear(str);
r_string_clear(array);
return s;
}
/* Same with type string_t of M*LIB, representing a variable string
and its explicit oplist. And said above, it generates exactly the
same code. */
ARRAY_DEF(r_string2, string_t, STRING_OPLIST)
/* Same with type 'volatile unsigned int *'
The array stores pointers to another integer,
allocated somewhere else.
We need to tell M*LIB to handle the basic type
as pointers without caring for what the value of
the pointer is.
So here, we **need** to specify the oplist of the type
as M_PTR_OPLIST.
*/
ARRAY_DEF(r_vintptr, volatile unsigned int *, M_PTR_OPLIST)
static int
test_vintptr(int n)
{
r_vintptr_t array;
// Allocate a secondary table to store the value.
unsigned *tab = calloc(n, sizeof(volatile unsigned int));
if (!tab) abort();
r_vintptr_init(array);
for(int i = 0; i < n; i++) {
tab[i] = i*i - i;
// Push the pointer to this integer in the array
r_vintptr_push_back(array, &tab[i]);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the integer.
volatile unsigned *p = *r_vintptr_get(array, i);
s += *p;
}
r_vintptr_clear(array);
free(tab);
return s;
}
/* Same with a structure type.
We need to tell M*LIB to handle the basic type
as POD;
So here, we **need** to specify the oplist of the type
as M_POD_OPLIST.
*/
struct rock_me_out {
int n;
float other;
};
ARRAY_DEF(r_rockme, struct rock_me_out, M_POD_OPLIST)
static int
test_rockme(int n)
{
r_rockme_t array;
r_rockme_init(array);
for(int i = 0; i < n; i++) {
struct rock_me_out rock;
rock.n = i*i - i;
// Push the struct in the array
r_rockme_push_back(array, rock);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the struct
struct rock_me_out *p = r_rockme_get(array, i);
s += p->n;
}
r_rockme_clear(array);
return s;
}
/* Same with a structure type defined with [1]:
We need to tell M*LIB to handle the basic type
as a special array;
So here, we **need** to specify the oplist of the type
as M_A1_OPLIST.
*/
typedef struct rock_me_in {
int n;
float other;
} rock_me_in[1];
ARRAY_DEF(r_rockme2, rock_me_in, M_A1_OPLIST)
static int
test_rockme2(int n)
{
r_rockme2_t array;
r_rockme2_init(array);
for(int i = 0; i < n; i++) {
rock_me_in rock;
rock->n = i*i - i;
r_rockme2_push_back(array, rock);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the struct.
rock_me_in *p = r_rockme2_get(array, i);
s += (*p)->n;
}
r_rockme2_clear(array);
return s;
}
/* Same with a structure type defined with [1] and a global registeration.
We need to tell M*LIB to handle the basic type
as a special array.
To do so, we register the oplist of rock_us_t by defining a macro based
on M_OPL_ + type name + () which expands with the oplist of the type.
Then we define the array, giving it only the type.
M*LIB will look at the global registered oplist of the type, we don't
need to give it explictly.
Global registeration makes easier code.
Registeration should be done when the type is defined.
*/
#define M_OPL_rock_me_in() M_A1_OPLIST
ARRAY_DEF(r_rockme2b, rock_me_in)
static int
test_rockme2b(int n)
{
r_rockme2b_t array;
r_rockme2b_init(array);
for(int i = 0; i < n; i++) {
rock_me_in rock;
rock->n = i*i - i;
r_rockme2b_push_back(array, rock);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the struct.
rock_me_in *p = r_rockme2b_get(array, i);
s += (*p)->n;
}
r_rockme2b_clear(array);
return s;
}
/* Same with a structure type defined as a tuple.
We first define a tuple. No need to specify the oplist as it uses basic C types
so M_DEFAULT_OPLIST is used.
Then we define the array, giving it the oplist definition of the tuple:
the macro TUPLE_OPLIST is used to build it based on the tuple name,
and the oplists of the tuple.
*/
TUPLE_DEF2(rock_you, (n, int), (other, float))
ARRAY_DEF(r_rockme3, rock_you_t, TUPLE_OPLIST(rock_you, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST))
static int
test_rockme3(int n)
{
r_rockme3_t array;
rock_you_t x;
r_rockme3_init(array);
rock_you_init(x);
for(int i = 0; i < n; i++) {
rock_you_set_n(x, i*i - i);
r_rockme3_push_back(array, x);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the structure containing the integer.
rock_you_t *p = r_rockme3_get(array, i);
s += (*p)->n;
}
r_rockme3_clear(array);
rock_you_clear(x);
return s;
}
/* Same with a structure type defined as a tuple using global registeration.
We first define a tuple. No need to specify the oplist as it uses basic C types
so M_DEFAULT_OPLIST is used.
Then we register the oplist of rock_us_t by defining a macro based
on M_OPL_ + type name + () which expands with the oplist of the type.
Then we define the array, giving it only the type.
M*LIB will look at the global registered oplist of the type, we don't
need to give it explictly.
Global registeration makes easier code.
*/
TUPLE_DEF2(rock_us, (n, int), (other, float))
#define M_OPL_rock_us_t() TUPLE_OPLIST(rock_us, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
ARRAY_DEF(r_rockme4, rock_us_t)
static int
test_rockme4(int n)
{
r_rockme4_t array;
rock_us_t x;
r_rockme4_init(array);
rock_us_init(x);
for(int i = 0; i < n; i++) {
rock_us_set_n(x, i*i - i);
r_rockme4_push_back(array, x);
}
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the structure containing the integer.
rock_us_t *p = r_rockme4_get(array, i);
s += (*p)->n;
}
r_rockme4_clear(array);
rock_us_clear(x);
return s;
}
int main (int argc, const char *argv[])
{
int n = argc == 1 ? 10 : atoi(argv[1]);
int s;
s = test_bool(n);
printf ("S[bool] = %d\n", s);
s = test_char(n);
printf ("S[char] = %d\n", s);
s = test_int(n);
printf ("S[int] = %d\n", s);
s = test_int2(n);
printf ("S[int2] = %d\n", s);
s = test_ullong(n);
printf ("S[ullong] = %d\n", s);
s = test_float(n);
printf ("S[float] = %d\n", s);
s = test_double(n);
printf ("S[double] = %d\n", s);
s = test_cstring(n);
printf ("S[cstring] = %d\n", s);
s = test_string(n);
printf ("S[string] = %d\n", s);
s = test_vintptr(n);
printf ("S[vintptr] = %d\n", s);
s = test_rockme(n);
printf ("S[rockme] = %d\n", s);
s = test_rockme2(n);
printf ("S[rockme2] = %d\n", s);
s = test_rockme2b(n);
printf ("S[rockme2b] = %d\n", s);
s = test_rockme3(n);
printf ("S[rockme3] = %d\n", s);
s = test_rockme3(n);
printf ("S[rockme4] = %d\n", s);
exit(0);
}
+13 -1
View File
@@ -1,9 +1,13 @@
#if HAVE_GMP
#include <gmp.h>
#include "m-array.h"
#include "m-algo.h"
/* Define an array of mpz_t */
ARRAY_DEF(array_mpz, mpz_t, M_CLASSIC_OPLIST(mpz))
/* Define some algorithm functions on array_mpz_t */
ALGO_DEF(array_mpz, ARRAY_OPLIST(array_mpz, M_CLASSIC_OPLIST(mpz)))
static inline void my_mpz_inc(mpz_t *d, const mpz_t a){
@@ -19,14 +23,22 @@ int main(void)
mpz_init_set_ui(z, 1);
array_mpz_t a;
array_mpz_init(a);
array_mpz_push_back(a, z);
for(size_t i = 2 ; i < 1000; i++) {
mpz_mul_ui(z, z, i);
array_mpz_push_back(a, z);
}
/* z = sum (a[i]^2) */
/* Compute z = sum (a[i]^2) */
array_mpz_map_reduce (&z, a, my_mpz_inc, my_mpz_sqr);
gmp_printf ("Z=%Zd\n", z);
array_mpz_clear(a);
mpz_clear(z);
}
#else
// Nothing to do
int main(void) { return 0; }
#endif
+78
View File
@@ -0,0 +1,78 @@
#include "m-array.h"
#include "m-string.h"
/* This example show how to use complex structure with array
embedding another library */
/* This is a trivial library */
typedef struct lib_ext_struct {
int id;
// Other data
} lib_ext_struct;
static lib_ext_struct *lib_ext_struct_Duplicate(const lib_ext_struct *obj)
{
lib_ext_struct *p = malloc(sizeof(lib_ext_struct));
if (!p) abort();
p->id = obj->id;
return p;
}
static void lib_ext_struct_Delete(lib_ext_struct *obj)
{
free(obj);
}
/* This is the complex structure */
typedef struct {
uint32_t id;
string_t type;
lib_ext_struct* properties;
} data_node;
static void data_node_init(data_node *obj)
{
obj->id = 0;
string_init(obj->type);
obj->properties = NULL;
}
static void data_node_init_set(data_node *obj, const data_node *src)
{
obj->id = src->id;
string_init_set(obj->type, src->type);
if (src->properties)
obj->properties = lib_ext_struct_Duplicate(src->properties);
else
obj->properties = NULL;
}
static void data_node_set(data_node *obj, const data_node *src)
{
obj->id = src->id;
string_set(obj->type, src->type);
if (obj->properties)
lib_ext_struct_Delete(obj->properties);
if (src->properties)
obj->properties = lib_ext_struct_Duplicate(src->properties);
else
obj->properties = NULL;
}
static void data_node_clear(data_node *obj)
{
string_clear(obj->type);
if (obj->properties)
lib_ext_struct_Delete(obj->properties);
}
ARRAY_DEF(array_data_node, data_node, (INIT(API_2(data_node_init)),SET(API_6(data_node_set)),INIT_SET(API_6(data_node_init_set)),CLEAR(API_2(data_node_clear))))
array_data_node_t global_array;
int main(void)
{
array_data_node_init(global_array);
array_data_node_clear(global_array);
}
+60
View File
@@ -0,0 +1,60 @@
#include "m-bptree.h"
#include "m-string.h"
// Define a map int -> string
BPTREE_DEF2(map_employee_id, 5, int, string_t)
#define M_OPL_map_employee_id_t() BPTREE_OPLIST2(map_employee_id, M_DEFAULT_OPLIST, STRING_OPLIST)
// Define a map string -> int
BPTREE_DEF2(map_employee_name, 5, string_t, int)
#define M_OPL_map_employee_name_t() BPTREE_OPLIST2(map_employee_name, STRING_OPLIST, M_DEFAULT_OPLIST)
int main(void)
{
map_employee_id_t Employees;
map_employee_id_init(Employees);
// Assign
map_employee_id_set_at(Employees, 17425, STRING_CTE("Mike"));
map_employee_id_set_at(Employees, 7147, STRING_CTE("Tango"));
map_employee_id_set_at(Employees, 841, STRING_CTE("Charlie"));
map_employee_id_set_at(Employees, 11632, STRING_CTE("John Do"));
map_employee_id_set_at(Employees, 852, STRING_CTE("Peter"));
map_employee_id_set_at(Employees, 530, STRING_CTE("Amanda"));
map_employee_id_set_at(Employees, 4258, STRING_CTE("Elisabeth"));
printf("Number of employees: %zu\n", map_employee_id_size(Employees));
// Iterate over the employee list
printf("\nList of employees (sorted by id):\n");
map_employee_id_it_t it;
for(map_employee_id_it(it, Employees); !map_employee_id_end_p(it); map_employee_id_next(it)) {
// Get a pointer to this employee from the iterator
const map_employee_id_type_t *e = map_employee_id_cref(it);
// Print it
printf ("%d: %s\n", *(e->key_ptr), string_get_cstr(*(e->value_ptr)));
}
// Let's create a reverse map
M_LET(Employees_r, map_employee_name_t) {
// Let's fill in the reverse map with each employee
// (using macro M_EACH)
for M_EACH(e, Employees, map_employee_id_t) {
map_employee_name_set_at(Employees_r, *(e->value_ptr), *(e->key_ptr));
}
// Iterate over the employee list
printf("\nList of employees (sorted by name):\n");
for M_EACH(e, Employees_r, map_employee_name_t) {
printf ("%d: %s\n", *(e->value_ptr), string_get_cstr(*(e->key_ptr)));
}
// Print
printf("\nElisabeth has id = %d\n", *map_employee_name_get(Employees_r, STRING_CTE("Elisabeth")));
}
map_employee_id_clear(Employees);
return 0;
}
+64
View File
@@ -0,0 +1,64 @@
#include "m-serial-json.h"
#include "m-tuple.h"
#include "m-array.h"
// Serial json is not supported for standard types if not C11
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
// Let's define a 'person' composed of several fields.
TUPLE_DEF2(person,
(surname, string_t),
(age, int),
(present, bool)
)
#define M_OPL_person_t() TUPLE_OPLIST(person, M_DEFAULT_OPLIST, STRING_OPLIST, M_DEFAULT_OPLIST )
// Let's define an array of person
ARRAY_DEF(base, person_t)
#define M_OPL_base_t() ARRAY_OPLIST(base, M_OPL_person_t())
// Let's read an array of person from the given JSON file
static void read(base_t base, const char filename[])
{
m_serial_read_t in;
m_serial_return_code_t ret;
// Open the file
FILE *f = fopen(filename, "rt");
if (!f) {
fprintf(stderr, "ERROR: Cannot open file '%s'.\n", filename);
exit(2);
}
// Initialize the serializer with the file
m_serial_json_read_init(in, f);
// Read the array
ret = base_in_serial(base, in);
if (ret != M_SERIAL_OK_DONE) {
fprintf(stderr, "ERROR: Cannot read JSON data from file '%s'.\n", filename);
exit(2);
}
// Let's close & clear all
m_serial_json_read_clear(in);
fclose(f);
}
int main(void)
{
M_LET(base, base_t) {
read(base, "ex-json01.json");
printf ("List of presents:\n");
for M_EACH(el, base, base_t) {
if ((*el)->present) {
printf("%s\n", string_get_cstr((*el)->surname));
}
}
}
exit(0);
}
#else
int main(void)
{
exit(0);
}
#endif
+7
View File
@@ -0,0 +1,7 @@
[
{ "surname": "Angel", "age": 1, "present": false},
{ "surname": "Snake", "age": 2, "present": true},
{ "surname": "BallOfBase", "age": 5, "present": true}
]
+7
View File
@@ -1,3 +1,5 @@
#if HAVE_GMP
#include <stdio.h>
#include <gmp.h>
@@ -61,3 +63,8 @@ int main(void)
}
} /* All variables are automatically cleared beyond this point */
}
#else
// Nothing to do
int main(void) { return 0; }
#endif
+40
View File
@@ -0,0 +1,40 @@
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#include <stdio.h>
#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;
}
#else
int main(void) { return 0; }
#endif
+115 -76
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - ALGO module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -44,7 +44,7 @@
ALGOI_FOR_EACH_ARG(container, M_GLOBAL_OPLIST(cont_oplist), __VA_ARGS__ ))
/* Extract a subset of a container to fill in another container.
/* Extract a subset of a container to copy into another container.
USAGE:
ALGO_EXTRACT(contDst, contDstOplist, contSrc, contSrcOplist, function
[, extra arguments of function]) */
@@ -105,6 +105,8 @@
#define ALGOI_DEF_P2(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
M_IF_METHOD(EQUAL, type_oplist)( \
\
/* It supposes that the container is not sorted */ \
static inline void \
M_C(name, _find_again) (it_t it, type_t const data) \
@@ -117,20 +119,90 @@
} \
\
static inline void \
M_C(name, _find) (it_t it, container_t l, type_t const data) \
M_C(name, _find) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_FIRST(cont_oplist, it, l); \
M_C(name, _find_again)(it, data); \
} \
\
static inline bool \
M_C(name, _contain_p) (container_t l, type_t const data) \
M_C(name, _contain_p) (container_t const l, type_t const data) \
{ \
it_t it; \
M_C(name,_find)(it, l, data); \
return !M_CALL_IT_END_P(cont_oplist, it); \
} \
\
/* For the definition of _find_last, if the methods \
PREVIOUS & IT_LAST are defined, then search backwards */ \
M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \
( \
static inline void \
M_C(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
for (M_CALL_IT_LAST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_PREVIOUS(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
/* We can stop as soon as a match is found */ \
return; \
} \
} \
, \
/* Otherwise search forward, but don't stop on the first occurence */ \
static inline void \
M_C(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_END(cont_oplist, it, l); \
it_t it2; \
for (M_CALL_IT_FIRST(cont_oplist, it2, l); \
!M_CALL_IT_END_P(cont_oplist, it2) ; \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \
/* We cannot stop as soon as a match is found */ \
M_CALL_IT_SET(cont_oplist, it, it2) ; \
} \
} \
) /* End of alternative of _find_last */ \
\
static inline size_t \
M_C(name, _count) (container_t const l, type_t const data) \
{ \
it_t it; \
size_t count = 0; \
for (M_CALL_IT_FIRST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_NEXT(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
count++ ; \
} \
return count; \
} \
\
\
static inline void \
M_C(name, _mismatch_again) (it_t it1, it_t it2) \
{ \
for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
!M_CALL_IT_END_P(cont_oplist, it2); \
M_CALL_IT_NEXT(cont_oplist, it1), \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \
*M_CALL_IT_CREF(cont_oplist, it2))) \
break; \
} \
} \
\
static inline void \
M_C(name, _mismatch) (it_t it1, it_t it2, container_t const l1, container_t const l2 ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
M_CALL_IT_FIRST(cont_oplist, it2, l2); \
M_C(name, _mismatch_again)(it1, it2); \
} \
\
, /* NO EQUAL */) \
\
static inline void \
M_C(name, _find_again_if) (it_t it, bool (*func)(type_t const)) \
{ \
@@ -148,54 +220,9 @@
M_C(name, _find_again_if)(it, func); \
} \
\
/* For the definition of _find_last, if the methods \
PREVIOUS & IT_LAST are defined, then search backwards */ \
M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \
( \
static inline void \
M_C(name, _find_last) (it_t it, container_t l, type_t const data) \
{ \
for (M_CALL_IT_LAST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_PREVIOUS(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
/* We can stop as soon as a match is found */ \
return; \
} \
} \
, \
/* Otherwise search forward, but don't stop on the first occurence */ \
static inline void \
M_C(name, _find_last) (it_t it, container_t l, type_t const data) \
{ \
M_CALL_IT_END(cont_oplist, it, l); \
it_t it2; \
for (M_CALL_IT_FIRST(cont_oplist, it2, l); \
!M_CALL_IT_END_P(cont_oplist, it2) ; \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \
/* We cannot stop as soon as a match is found */ \
M_CALL_IT_SET(cont_oplist, it, it2) ; \
} \
} \
) /* End of alternative of _find_last */ \
\
static inline size_t \
M_C(name, _count) (container_t l, type_t const data) \
{ \
it_t it; \
size_t count = 0; \
for (M_CALL_IT_FIRST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_NEXT(cont_oplist, it)) { \
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
count++ ; \
} \
return count; \
} \
\
static inline size_t \
M_C(name, _count_if) (container_t l, bool (*func)(type_t const data)) \
M_C(name, _count_if) (container_t const l, bool (*func)(type_t const data)) \
{ \
it_t it; \
size_t count = 0; \
@@ -209,27 +236,6 @@
} \
\
static inline void \
M_C(name, _mismatch_again) (it_t it1, it_t it2) \
{ \
for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
!M_CALL_IT_END_P(cont_oplist, it2); \
M_CALL_IT_NEXT(cont_oplist, it1), \
M_CALL_IT_NEXT(cont_oplist, it2)) { \
if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \
*M_CALL_IT_CREF(cont_oplist, it2))) \
break; \
} \
} \
\
static inline void \
M_C(name, _mismatch) (it_t it1, it_t it2, container_t l1, container_t l2 ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
M_CALL_IT_FIRST(cont_oplist, it2, l2); \
M_C(name, _mismatch_again)(it1, it2); \
} \
\
static inline void \
M_C(name, _mismatch_again_if) (it_t it1, it_t it2, bool (*func)(type_t const, type_t const) ) \
{ \
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
@@ -243,7 +249,7 @@
} \
\
static inline void \
M_C(name, _mismatch_if) (it_t it1, it_t it2, container_t l1, \
M_C(name, _mismatch_if) (it_t it1, it_t it2, container_t const l1, \
container_t l2, bool (*func)(type_t const, type_t const) ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
@@ -260,7 +266,8 @@
} \
\
static inline void \
M_C(name, _reduce) (type_t *dest, container_t l, void (*f)(type_t *, type_t const) ) \
M_C(name, _reduce) (type_t *dest, container_t const l, \
void (*f)(type_t *, type_t const) ) \
{ \
bool initDone = false; \
for M_EACH(item, l, cont_oplist) { \
@@ -273,9 +280,10 @@
} \
} \
\
M_IF_METHOD(INIT, type_oplist)( \
static inline \
void M_C(name, _map_reduce) (type_t *dest, \
container_t l, \
const container_t l, \
void (*redFunc)(type_t*, type_t const), \
void (*mapFunc)(type_t*, type_t const) ) \
{ \
@@ -293,9 +301,10 @@
} \
M_CALL_CLEAR(type_oplist, tmp); \
} \
, ) \
\
static inline bool \
M_C(name, _any_of_p) (container_t l, bool (*f)(type_t const) ) \
M_C(name, _any_of_p) (container_t const l, bool (*f)(type_t const) ) \
{ \
for M_EACH(item, l, cont_oplist) { \
if (f(*item)) \
@@ -305,7 +314,7 @@
} \
\
static inline bool \
M_C(name, _all_of_p) (container_t l, bool (*f)(type_t const) ) \
M_C(name, _all_of_p) (container_t const l, bool (*f)(type_t const) ) \
{ \
for M_EACH(item, l, cont_oplist) { \
if (!f(*item)) \
@@ -391,6 +400,36 @@
M_CALL_IT_NEXT(cont_oplist, it2); \
} \
} \
} \
\
static inline void \
M_C(name, _remove_val)(container_t l, type_t const val) \
{ \
it_t it1; \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
while (!M_CALL_IT_END_P(cont_oplist, it1)) { \
type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \
if (M_CALL_EQUAL(type_oplist, *ref1, val)) { \
M_CALL_IT_REMOVE(cont_oplist, l, it1); \
} else { \
M_CALL_IT_NEXT(cont_oplist, it1); \
} \
} \
} \
\
static inline void \
M_C(name, _remove_if)(container_t l, bool (*func)(type_t const)) \
{ \
it_t it1; \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
while (!M_CALL_IT_END_P(cont_oplist, it1)) { \
type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \
if (func(*ref1)) { \
M_CALL_IT_REMOVE(cont_oplist, l, it1); \
} else { \
M_CALL_IT_NEXT(cont_oplist, it1); \
} \
} \
} \
, /* No IT_REMOVE method */) \
\
+112 -50
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic ARRAY module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -30,27 +30,32 @@
/* Define a dynamic array of the given type and its associated functions.
USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */
#define ARRAY_DEF(name, ...) \
ARRAYI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
ARRAYI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
/* Define the oplist of a dynamic array given its name and its oplist.
If no oplist is given it is assumed to be M_DEFAULT_OPLIST
USAGE: ARRAY_OPLIST(name[, oplist of the type]) */
#define ARRAY_OPLIST(...) \
ARRAYI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
ARRAYI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/********************************************************************************/
/********************************** INTERNAL ************************************/
/********************************************************************************/
// Deferred evaluation for the oplist definition.
#define ARRAYI_OPLIST(arg) ARRAYI_OPLIST2 arg
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define ARRAYI_OPLIST_P1(arg) ARRAYI_OPLIST_P2 arg
/* OPLIST definition of a dynamic array */
/* FIXME: Do we want to export some methods as they are slow and
are not fit to be used for building other methods (like _remove)? */
#define ARRAYI_OPLIST2(name, oplist) \
are not fit to be used for building other methods (like _it_remove)? */
#define ARRAYI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
@@ -85,6 +90,7 @@
,GET_KEY(M_C(name, _get)) \
,M_IF_METHOD(INIT, oplist)(GET_SET_KEY(M_C(name, _get_at)) ,) \
,ERASE_KEY(M_C(name, _erase)) \
,GET_SIZE(M_C(name, _size)) \
,PUSH(M_C(name,_push_back)) \
,POP(M_C(name,_pop_back)) \
,PUSH_MOVE(M_C(name,_push_move)) \
@@ -95,6 +101,8 @@
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
@@ -102,23 +110,30 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* Define the contract of an array */
/* Define the internal contract of an array */
#define ARRAYI_CONTRACT(a) do { \
assert (a != NULL); \
assert (a->size <= a->alloc); \
assert (a->size == 0 || a->ptr != NULL); \
} while (0)
// Deferred evaluation for the array definition.
#define ARRAYI_DEF(arg) ARRAYI_DEF2 arg
/* Deferred evaluation for the array definition,
so that all arguments are evaluated before further expansion */
#define ARRAYI_DEF_P1(arg) ARRAYI_DEF_P2 arg
// Internal definition.
#define ARRAYI_DEF2(name, type, oplist, array_t, array_it_t) \
/* Internal definition:
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the array
- array_t: alias for M_C(name, _t) [ type of the array ]
- it_t: alias for M_C(name, _it_t) [ iterator of the array ]
*/
#define ARRAYI_DEF_P2(name, type, oplist, array_t, it_t) \
\
typedef struct M_C(name, _s) { \
size_t size; /* Number of elements in the array */ \
size_t alloc; /* Allocated size for the array */ \
type *ptr; /* Pointer to the array */ \
size_t alloc; /* Allocated size for the array base */ \
type *ptr; /* Pointer to the array base */ \
} array_t[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
@@ -126,9 +141,9 @@
typedef type M_C(name, _type_t); \
\
typedef struct M_C(name, _it_s) { \
size_t index; \
const struct M_C(name, _s) *array; \
} array_it_t[1]; \
size_t index; /* Index of the element */ \
const struct M_C(name, _s) *array; /* Reference of the array */ \
} it_t[1]; \
\
static inline void \
M_C(name, _init)(array_t v) \
@@ -218,7 +233,7 @@
} \
\
static inline void \
M_C(name, _set_at)(array_t v, size_t i, type x) \
M_C(name, _set_at)(array_t v, size_t i, type const x) \
{ \
ARRAYI_CONTRACT(v); \
assert(v->size > 0 && v->ptr != NULL && i < v->size); \
@@ -434,7 +449,7 @@
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _pop_until)(array_t v, array_it_t pos) \
M_C(name, _pop_until)(array_t v, it_t pos) \
{ \
ARRAYI_CONTRACT(v); \
assert (v == pos->array); \
@@ -495,10 +510,17 @@
ARRAYI_CONTRACT(v); \
assert(i <= v->size); \
size_t size = v->size + num; \
/* Avoid overflow of v->size + num */ \
if (v->size > v->alloc-num) { \
/* Test for overflow of variable size */ \
if (M_UNLIKELY (size <= v->size)) { \
/* Unlikely case of nothing to do */ \
if (num == 0) return; \
M_MEMORY_FULL(sizeof (type) * v->size); \
return ; \
} \
/* Test if alloc array is sufficient */ \
if (size > v->alloc) { \
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
if (M_UNLIKELY (size <= v->size || alloc <= v->alloc)) { \
if (M_UNLIKELY (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
} \
@@ -554,16 +576,9 @@
ARRAYI_CONTRACT(v); \
assert(i < v->size && j < v->size && v->ptr != NULL); \
type tmp; \
M_IF_METHOD(INIT_MOVE, oplist) ( \
M_CALL_INIT_MOVE(oplist, tmp, v->ptr[i]); \
M_CALL_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
M_CALL_INIT_MOVE(oplist, v->ptr[j], tmp); \
, \
M_CALL_INIT_SET(oplist, tmp, v->ptr[i]); \
M_CALL_SET(oplist, v->ptr[i], v->ptr[j]); \
M_CALL_SET(oplist, v->ptr[j], tmp); \
M_CALL_CLEAR(oplist, tmp); \
) /* IF INIT_MOVE method */ \
M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]); \
M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
M_DO_INIT_MOVE(oplist, v->ptr[j], tmp); \
ARRAYI_CONTRACT(v); \
} \
\
@@ -592,7 +607,7 @@
} \
\
static inline void \
M_C(name, _it)(array_it_t it, const array_t v) \
M_C(name, _it)(it_t it, const array_t v) \
{ \
ARRAYI_CONTRACT(v); \
assert (it != NULL); \
@@ -601,7 +616,7 @@
} \
\
static inline void \
M_C(name, _it_last)(array_it_t it, const array_t v) \
M_C(name, _it_last)(it_t it, const array_t v) \
{ \
ARRAYI_CONTRACT(v); \
assert (it != NULL); \
@@ -610,7 +625,7 @@
} \
\
static inline void \
M_C(name, _it_end)(array_it_t it, const array_t v) \
M_C(name, _it_end)(it_t it, const array_t v) \
{ \
ARRAYI_CONTRACT(v); \
assert (it != NULL); \
@@ -619,7 +634,7 @@
} \
\
static inline void \
M_C(name, _it_set)(array_it_t it, const array_it_t org) \
M_C(name, _it_set)(it_t it, const it_t org) \
{ \
assert (it != NULL && org != NULL); \
it->index = org->index; \
@@ -628,14 +643,14 @@
} \
\
static inline bool \
M_C(name, _end_p)(const array_it_t it) \
M_C(name, _end_p)(const it_t it) \
{ \
assert(it != NULL && it->array != NULL); \
return it->index >= it->array->size; \
} \
\
static inline bool \
M_C(name, _last_p)(const array_it_t it) \
M_C(name, _last_p)(const it_t it) \
{ \
assert(it != NULL && it->array != NULL); \
/* NOTE: Can not compute 'size-1' due to potential overflow \
@@ -644,22 +659,22 @@
} \
\
static inline bool \
M_C(name, _it_equal_p)(const array_it_t it1, \
const array_it_t it2) \
M_C(name, _it_equal_p)(const it_t it1, \
const it_t it2) \
{ \
assert(it1 != NULL && it2 != NULL); \
return it1->array == it2->array && it1->index == it2->index; \
} \
\
static inline void \
M_C(name, _next)(array_it_t it) \
M_C(name, _next)(it_t it) \
{ \
assert(it != NULL && it->array != NULL); \
it->index ++; \
} \
\
static inline void \
M_C(name, _previous)(array_it_t it) \
M_C(name, _previous)(it_t it) \
{ \
assert(it != NULL && it->array != NULL); \
/* NOTE: In the case index=0, it will be set to (unsigned) -1 \
@@ -668,28 +683,28 @@
} \
\
static inline type * \
M_C(name, _ref)(const array_it_t it) \
M_C(name, _ref)(const it_t it) \
{ \
assert(it != NULL); \
return M_C(name, _get)(it->array, it->index); \
} \
\
static inline type const * \
M_C(name, _cref)(const array_it_t it) \
M_C(name, _cref)(const it_t it) \
{ \
assert(it != NULL); \
return M_C(name, _cget)(it->array, it->index); \
} \
\
static inline void \
M_C(name, _insert)(array_t a, array_it_t it, type const x) \
M_C(name, _insert)(array_t a, it_t it, type const x) \
{ \
assert (it != NULL && a == it->array); \
M_C(name, _push_at)(a, it->index + 1, x); \
} \
\
static inline void \
M_C(name, _remove)(array_t a, array_it_t it) \
M_C(name, _remove)(array_t a, it_t it) \
{ \
assert (it != NULL && a == it->array); \
M_C(name, _pop_at)(NULL, a, it->index); \
@@ -799,6 +814,7 @@
\
,) /* IF CMP oplist */ \
\
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, array_t const array, \
@@ -807,7 +823,7 @@
STRINGI_CONTRACT(str); \
ARRAYI_CONTRACT(array); \
(append ? string_cat_str : string_set_str) (str, "["); \
array_it_t it; \
it_t it; \
for (M_C(name, _it)(it, array) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
@@ -896,6 +912,52 @@
} \
, /* no IN_STR & INIT */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, const array_t array) \
{ \
ARRAYI_CONTRACT(array); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
ret = f->m_interface->write_array_start(local, f, array->size); \
for (size_t i = 0; i < array->size; i++) { \
type const *item = M_C(name, _cget)(array, i); \
if (i != 0) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
} \
ret |= f->m_interface->write_array_end(local, f); \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(array_t array, m_serial_read_t f) \
{ \
ARRAYI_CONTRACT(array); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
size_t estimated_size = 0; \
M_C(name,_clean)(array); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
if (estimated_size != 0) M_C(name, _reserve)(array, estimated_size); \
type item; \
M_CALL_INIT(oplist, item); \
do { \
ret = M_CALL_IN_SERIAL(oplist, item, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push_back)(array, item); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(oplist, item); \
ARRAYI_CONTRACT(array); \
return ret; \
} \
, /* no IN_SERIAL & INIT */ ) \
\
M_IF_METHOD(EQUAL, oplist)( \
static inline bool \
M_C(name, _equal_p)(const array_t array1, \
+18 -6
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin stdatomic wrapper for C++ compatibility
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -98,7 +98,7 @@ using std::memory_order_seq_cst;
#define _Atomic(T) std::atomic< T >
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|| ( defined(__GNUC__) && !defined(__clang__) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|| (defined(__clang__) && __clang_major__ >= 4)
@@ -106,6 +106,18 @@ using std::memory_order_seq_cst;
and doesn't provide its own stdatomic.h */
#include <stdatomic.h>
/* MSYS2 has a conflict between cdefs.h which defines a _Atomic macro (if not C11)
not compatible with the used stdatomic.h (from GCC).
Provide a configurable mechanism to undef it with auto-detection of msys2 / gcc */
#ifndef M_USE_UNDEF_ATOMIC
# if defined(__MSYS__) && defined(__GNUC__) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L)
# define M_USE_UNDEF_ATOMIC 1
# endif
#endif
#if defined(M_USE_UNDEF_ATOMIC) && M_USE_UNDEF_ATOMIC == 1
# undef _Atomic
#endif
#else
/* Non working atomic.h, nor working stdatomic.h found.
@@ -125,10 +137,10 @@ using std::memory_order_seq_cst;
*/
#define _Atomic(T) \
struct { \
volatile T _val; \
T _zero; \
T _previous; \
m_mutex_t _lock; \
T volatile _val; \
T _zero; \
T _previous; \
m_mutex_t _lock; \
}
/* Even if memory order is defined, only the strongest constraint is used */
+22 -18
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - BITSET module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -30,33 +30,21 @@
/********************************** INTERNAL ************************************/
// Define a limb of a bitset
// Define the limb of a bitset
typedef uint64_t bitset_limb;
// And its size
#define BITSET_LIMB_BIT (sizeof(bitset_limb) * CHAR_BIT)
typedef struct bitset_s {
size_t size; // Size is the number of bits
size_t alloc; // Alloc is the number of allocated limbs
bitset_limb *ptr; // Pointer to the allocated limbs
} bitset_t[1];
typedef struct bitset_s *bitset_ptr;
typedef const struct bitset_s *bitset_srcptr;
typedef struct bitset_it_s {
size_t index; // index to the array of bit
bool value; // value used for _ref & _cref to store the value
struct bitset_s *set; // the associated bitset
} bitset_it_t[1];
// bitset grow policy
// bitset grow policy. n is limb size
#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 4 ? 4 : (n) * 2)
// Compute the number of allocated limbs needed to handle 'n' bits.
#define BITSETI_TO_ALLOC(n) (((n) + BITSET_LIMB_BIT - 1) / BITSET_LIMB_BIT)
// Compute the number of bits available from the allocated size
// Compute the number of bits available from the allocated size in limbs
#define BITSETI_FROM_ALLOC(n) ((n) * BITSET_LIMB_BIT)
// Contract of a bitset
#define BITSETI_CONTRACT(t) do { \
assert (t != NULL); \
assert (t->size <= BITSETI_FROM_ALLOC (t->alloc)); \
@@ -68,6 +56,21 @@ typedef struct bitset_it_s {
/********************************** EXTERNAL ************************************/
typedef struct bitset_s {
size_t size; // Size is the number of bits
size_t alloc; // Alloc is the number of allocated limbs
bitset_limb *ptr; // Pointer to the allocated limbs
} bitset_t[1];
typedef struct bitset_s *bitset_ptr;
typedef const struct bitset_s *bitset_srcptr;
typedef struct bitset_it_s {
size_t index; // index to the array of bit
bool value; // value used for _ref & _cref to store the value
struct bitset_s *set; // the associated bitset
} bitset_it_t[1];
static inline void
bitset_init(bitset_t t)
{
@@ -689,6 +692,7 @@ bitset_clz(const bitset_t set)
,TYPE(bitset_t) \
,SUBTYPE(bool) \
,TEST_EMPTY(bitset_empty_p), \
,GET_SIZE(bitset_size) \
,IT_TYPE(bitset_it_t) \
,IT_FIRST(bitset_it) \
,IT_SET(bitset_it_set) \
+300 -129
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - B+TREE module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -27,7 +27,7 @@
#include "m-core.h"
/* Define a B+tree which maps a key to a value, of size N
/* Define a B+tree of size 'N' that maps a 'key' to a 'value'
with its associated functions.
USAGE:
BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
@@ -36,8 +36,8 @@
*/
#define BPTREE_DEF2(name, N, key_type, ...) \
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
(name, N, key_type, __VA_ARGS__, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
(name, N, key_type, __VA_ARGS__, 1, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
/* Define a B+tree of a given type, of size N.
@@ -45,20 +45,45 @@
USAGE: BPTREE_DEF(name, N, type, [, oplist_of_the_type]) */
#define BPTREE_DEF(name, N, ...) \
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
(name, N, __VA_ARGS__, __VA_ARGS__, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
/* Define a B+tree of size 'N' that maps a 'key' to a 'value',
allowing multiple equal keys to exist,
with its associated functions.
USAGE:
BPTREE_MULTI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
OR
BPTREE_MULTI_DEF2(name, N, key_t, value_t)
*/
#define BPTREE_MULTI_DEF2(name, N, key_type, ...) \
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
(name, N, key_type, __VA_ARGS__, 1, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
/* Define a B+tree of a given type, of size N.
allowing multiple equal keys to exist,
with its associated functions
USAGE: BPTREE_MULTI_DEF(name, N, type, [, oplist_of_the_type]) */
#define BPTREE_MULTI_DEF(name, N, ...) \
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
/* Define the oplist of a rbtree of type (from BPTREE_DEF).
USAGE: BPTREE_OPLIST(name [, oplist_of_the_type]) */
#define BPTREE_OPLIST(...) \
BPTREEI_KEY_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
BPTREEI_KEY_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLISTR ), \
(__VA_ARGS__ )))
/* Define the oplist of a rbtree of type (from BPTREE_DEF2).
USAGE: BPTREE_OPLIST2(name, key_oplist, value_oplist) */
#define BPTREE_OPLIST2(name, key_oplist, value_oplist) \
#define BPTREE_OPLIST2(name, key_oplist, value_oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
SET(M_C(name, _set)), \
@@ -85,7 +110,6 @@
VALUE_TYPE(M_C(name, _value_type_t)), \
SET_KEY(M_C(name, _set_at)), \
GET_KEY(M_C(name, _get)), \
/*GET_SET_KEY(M_C(name, _get_at))*/, \
ERASE_KEY(M_C(name, _erase)), \
KEY_OPLIST(key_oplist), \
VALUE_OPLIST(value_oplist), \
@@ -93,6 +117,8 @@
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_C(name, _equal_p)),), \
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, key_oplist)(NEW(M_GET_NEW oplist),) \
@@ -104,10 +130,12 @@
/********************************** INTERNAL ************************************/
// deferred evaluation
#define BPTREEI_KEY_OPLIST(arg) BPTREEI_KEY_OPLIST2 arg
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define BPTREEI_KEY_OPLIST_P1(arg) BPTREEI_KEY_OPLIST_P2 arg
#define BPTREEI_KEY_OPLIST2(name, oplist) \
/* OPLIST definition of a b+tree */
#define BPTREEI_KEY_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
SET(M_C(name, _set)), \
@@ -134,6 +162,8 @@
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
@@ -141,10 +171,11 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* Internal contract of a B+TREE node */
#ifdef NDEBUG
# define BPTREEI_NODE_CONTRACT(N, key_oplist, node, root) do { } while (0)
# define BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { } while (0)
#else
# define BPTREEI_NODE_CONTRACT(N, key_oplist, node, root) do { \
# define BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { \
assert ((node) != NULL); \
assert ((root) != NULL); \
int num2 = (node)->num; \
@@ -159,18 +190,18 @@
assert (c2 <= num2 && num2 <= N); \
} \
for(int i2 = 1; i2 < num2 ; i2++) { \
assert (M_CALL_CMP(key_oplist, (node)->key[i2-1], (node)->key[i2]) < 0); \
assert (M_CALL_CMP(key_oplist, (node)->key[i2-1], (node)->key[i2]) M_IF(isMulti)(<=, <) 0); \
} \
if ((node)->next != NULL) { \
assert (num2 >= 1); \
assert (M_CALL_CMP(key_oplist, (node)->key[num2-1], (node)->next->key[0]) < 0); \
assert (M_CALL_CMP(key_oplist, (node)->key[num2-1], (node)->next->key[0]) M_IF(isMulti)(<=, <) 0); \
} \
} while (0)
#endif
#define BPTREEI_CONTRACT(N, key_oplist, b) do { \
#define BPTREEI_CONTRACT(N, isMulti, key_oplist, b) do { \
assert (N >= 3); /* TBC: 2 instead ? */ \
BPTREEI_NODE_CONTRACT(N, key_oplist, (b)->root, (b)->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, (b)->root, (b)->root); \
assert ((b)->root->next == NULL); \
if ((b)->root->num <= 0) assert (-(b)->root->num == (int) (b)->size); \
} while (0)
@@ -178,18 +209,36 @@
/* Max depth of any B+tree */
#define BPTREEI_MAX_STACK ((int)(CHAR_BIT*sizeof (size_t)))
// Deferred evaluation.
/* Deferred evaluation for the b+tree definition,
so that all arguments are evaluated before further expansion */
#define BPTREEI_DEF_P1(arg) BPTREEI_DEF_P2 arg
#define BPTREEI_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, tree_t, node_t, pit_t, it_t) \
/* Internal b+tree definition
- name: prefix to be used
- N: size of the node
- key_t: key type of the elements of the container
- key_oplist: oplist of the key type of the elements of the container
- value_t: value type of the elements of the container
- value_oplist: oplist of the value type of the elements of the container
- isMap: true if map, false if set
- isMulti: true if multimap/multiset, false otherwise
- tree_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
- pit_t: alias for M_C(name, _pit_t) [parent iterator]
*/
#define BPTREEI_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t) \
\
M_IF(isMap)( \
typedef struct M_C(name, _pair_s) { \
key_t *key_ptr; \
value_t *value_ptr; \
} M_C(name, _type_t); \
, \
typedef key_t M_C(name, _type_t); \
/* Type returned by the iterator. Due to having key and value \
separated in their own array in the node, it is pointers to \
the objects, not a global pointer to both objects. */ \
typedef struct M_C(name, _pair_s) { \
key_t *key_ptr; \
value_t *value_ptr; \
} M_C(name, _type_t); \
, \
typedef key_t M_C(name, _type_t); \
) \
typedef key_t M_C(name, _key_type_t); \
typedef value_t M_C(name, _value_type_t); \
@@ -197,8 +246,8 @@
/* Node of a B+TREE */ \
typedef struct M_C(name, _node_s) { \
int num; /* Abs=Number of keys. Sign <0 is leaf */ \
key_t key[N+1]; /* We can temporary push one more key */ \
struct M_C(name, _node_s) *next; /* next node */ \
key_t key[N+1]; /* We can temporary push one more key */ \
struct M_C(name, _node_s) *next; /* next node reference */ \
union M_C(name, _kind_s) { /* either value or pointer to other nodes */ \
M_IF(isMap)(value_t value[N+1];,) \
struct M_C(name, _node_s) *node[N+2]; \
@@ -226,7 +275,7 @@
int idx; \
} it_t[1]; \
\
/* Can be optimized later to alloc for leaf or for node */ \
/* TODO: Can be optimized to alloc for leaf or for node */ \
static inline node_t M_C(name, _new_node)(void) \
{ \
node_t n = M_CALL_NEW(key_oplist, struct M_C(name, _node_s)); \
@@ -243,7 +292,7 @@
{ \
b->root = M_C(name, _new_node)(); \
b->size = 0; \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline bool M_C(name, _is_leaf)(node_t n) \
@@ -262,7 +311,7 @@
\
static inline void M_C(name, _clean)(tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
node_t next, n = b->root; \
pit_t pit; \
int np = 0; \
@@ -298,12 +347,12 @@
/* Clean root */ \
b->root->num = 0; \
b->size = 0; \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void M_C(name, _clear)(tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
M_C(name, _clean)(b); \
/* Once the tree is clean, only the root remains */ \
M_CALL_DEL(key_oplist, b->root); \
@@ -342,17 +391,17 @@
} \
} \
} \
BPTREEI_NODE_CONTRACT(N, key_oplist, n, (o==root) ? n : root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, (o==root) ? n : root); \
return n; \
} \
\
static inline void M_C(name, _init_set)(tree_t b, const tree_t o) \
{ \
BPTREEI_CONTRACT(N, key_oplist, o); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, o); \
assert (b != NULL); \
b->root = M_C(name, _copy_node)(o->root, o->root); \
b->size = o->size; \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void M_C(name, _set)(tree_t b, const tree_t o) \
@@ -363,22 +412,22 @@
\
static inline bool M_C(name, _empty_p)(const tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
/* root shall be an empty leaf */ \
return b->size == 0; \
} \
\
static inline size_t M_C(name, _size)(const tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
return b->size; \
} \
\
static inline node_t M_C(name, _search_leaf)(pit_t pit, const tree_t b, key_t const key) \
static inline node_t M_C(name, _search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
{ \
node_t n = b->root; \
int np = 0; \
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
while (!M_C(name, _is_leaf)(n)) { \
assert (np <= BPTREEI_MAX_STACK); \
int i, hi = n->num; \
@@ -388,25 +437,25 @@
the choosen type and size , it simply means that the \
size of B+TREE is too big (TBC) and shall be reduced. */ \
for(i = 0; i < hi; i++) { \
if (M_CALL_CMP(key_oplist, key, n->key[i]) < 0) \
if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \
break; \
} \
pit->parent[np++] = n; \
n = n->kind.node[i]; \
assert (n != NULL); \
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
} \
pit->num = np; \
return n; \
} \
\
static inline value_t *M_C(name, _get)(tree_t b, key_t const key) \
static inline value_t *M_C(name, _get)(const tree_t b, key_t const key) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
pit_t pit; \
node_t n = M_C(name, _search_leaf)(pit, b, key); \
node_t n = M_C(name, _search_for_leaf)(pit, b, key); \
int cmp = 0; \
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
for(int i = 0; cmp >= 0 && i < -n->num; i++) { \
cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
if (cmp == 0) \
@@ -415,12 +464,14 @@
return NULL; \
} \
\
static inline const value_t *M_C(name, _cget)(tree_t b, key_t const key) \
static inline const value_t *M_C(name, _cget)(const tree_t b, key_t const key) \
{ \
return M_CONST_CAST(value_t, M_C(name, _get)(b, key)); \
} \
\
static inline int M_C(name, _search_and_insert_leaf)(node_t n, key_t const key M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
static inline int \
M_C(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
{ \
assert (M_C(name, _is_leaf)(n)); \
int i, num = M_C(name, _get_num)(n); \
@@ -428,10 +479,13 @@
for(i = 0; i < num; i++) { \
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
if (cmp <= 0) { \
if (M_UNLIKELY (cmp == 0)) { \
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
M_IF(isMulti)( /* Nothing to do : fallthrough */, \
/* Update value if keys are equal */ \
if (M_UNLIKELY (cmp == 0)) { \
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
return -1; \
} \
} \
) \
/* Move tables to make space for insertion */ \
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(num-i)); \
M_IF(isMap)(memmove(&n->kind.value[i+1], &n->kind.value[i], sizeof(value_t)*(num-i));,) \
@@ -444,48 +498,55 @@
return i; \
} \
\
static inline int M_C(name, _search_and_insert_node)(node_t n, key_t key) \
static inline int \
M_C(name, _search_and_insert_in_node)(node_t n, node_t l, key_t key) \
{ \
assert (!M_C(name, _is_leaf)(n)); \
int i, num = M_C(name, _get_num)(n); \
assert (num <= N); \
for(i = 0; i < num; i++) { \
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
assert (cmp != 0); \
if (cmp < 0) { \
if (n->kind.node[i] == l) { \
/* Move tables to make space for insertion */ \
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(num-i)); \
memmove(&n->kind.node[i+1], &n->kind.node[i], sizeof(node_t)*(num-i+1)); \
break; \
} \
} \
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
M_CALL_INIT_SET(key_oplist, n->key[i], key); \
n->num += 1; \
return i; \
} \
\
static inline void M_IF(isMap)(M_C(name, _set_at),M_C(name,_push))(tree_t b, key_t const key M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
static inline void \
M_IF(isMap)(M_C(name, _set_at),M_C(name,_push))(tree_t b, key_t const key \
M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
{ \
pit_t pit; \
BPTREEI_CONTRACT(N, key_oplist, b); \
node_t leaf = M_C(name, _search_leaf)(pit, b, key); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
node_t leaf = M_C(name, _search_for_leaf)(pit, b, key); \
/* Insert key into the leaf.*/ \
/* NOTE: Even if there is N elements, we can still add one more.*/ \
int i = M_C(name, _search_and_insert_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
int i = M_C(name, _search_and_insert_in_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
if (i < 0) { \
BPTREEI_CONTRACT(N, key_oplist, b); \
/* Nothing to do anymore. key already exists in the tree. \
value has been updated if needed */ \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
return; \
} \
b->size ++; \
/* Most likely case: leaf can accept key!*/ \
/* Most likely case: leaf can accept key */ \
int num = -leaf->num; \
assert (num > 0); \
if (M_LIKELY (num <= N)) { \
BPTREEI_CONTRACT(N, key_oplist, b); \
/* nothing more to do */ \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
return; \
} \
assert (num == N+1); \
/* leaf is full: need to slip it in two */ \
\
/* Needs to rebalance the B+TREE */ \
/* leaf is full: need to slip the leaf in two */ \
int nnum = (N + 1) / 2; \
num = N + 1 - nnum; \
node_t nleaf = M_C(name, _new_node)(); \
@@ -496,39 +557,41 @@
nleaf->num = -nnum; \
nleaf->next = leaf->next; \
leaf->next = nleaf; \
BPTREEI_NODE_CONTRACT(N, key_oplist, leaf, b->root); \
BPTREEI_NODE_CONTRACT(N, key_oplist, nleaf, b->root); \
/* Update parent to inject *key_ptr which split between (leaf, nleaf) */ \
key_t *key_ptr = &nleaf->key[0]; \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, leaf, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, nleaf, b->root); \
/* Update parent to inject *key_ptr that splits between (leaf, nleaf) */ \
key_t *key_ptr = &leaf->key[num-1]; \
while (true) { \
if (pit->num == 0) { \
/* We reach root. Need to increase the height of the tree.*/ \
/* We reach root ==> Need to increase the height of the tree.*/ \
node_t parent = M_C(name, _new_node)(); \
parent->num = 1; \
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
parent->kind.node[0] = leaf; \
parent->kind.node[1] = nleaf; \
b->root = parent; \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
return; \
} \
/* Non root node. Get parent */ \
/* Non root node. Get the parent node */ \
node_t parent = pit->parent[--pit->num]; \
/* Insert into parent (It is big enough to receive temporary one more) */ \
i = M_C(name, _search_and_insert_node)(parent, *key_ptr); \
i = M_C(name, _search_and_insert_in_node)(parent, leaf, *key_ptr); \
parent->kind.node[i] = leaf; \
parent->kind.node[i+1] = nleaf; \
/* Test if parent node is full? */ \
if (M_LIKELY (parent->num <= N)) { \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
return; /* No need to split parent.*/ \
} \
assert (parent->num == N+1); \
/* Need to split parent in {np} {1} {nnp} */ \
/* Need to split parent in {np} {med} {nnp} */ \
int nnp = N / 2; \
int np = N - nnp; \
assert (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
node_t nparent = M_C(name, _new_node)(); \
/* Move half items to new node (Like a classic B-TREEE) \
/* Move half items to new node (Like a classic B-TREE) \
and the median key to the grand-parent*/ \
memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*nnp); \
memmove(&nparent->kind.node[0], &parent->kind.node[np+1], sizeof(node_t)*(nnp+1)); \
@@ -536,8 +599,8 @@
nparent->num = nnp; \
nparent->next = parent->next; \
parent->next = nparent; \
BPTREEI_NODE_CONTRACT(N, key_oplist, parent, b->root); \
BPTREEI_NODE_CONTRACT(N, key_oplist, nparent, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, parent, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, nparent, b->root); \
/* Prepare for the next step */ \
key_ptr = &parent->key[np]; \
leaf = parent; \
@@ -545,13 +608,15 @@
} \
} \
\
static inline int M_C(name, _search_and_remove_leaf)(node_t n, key_t const key) \
static inline int \
M_C(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \
{ \
assert(M_C(name, _is_leaf)(n)); \
int num = M_C(name, _get_num)(n); \
const int num = M_C(name, _get_num)(n); \
for(int i = 0; i < num; i++) { \
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
if (cmp == 0) { \
/* found key ==> delete it */ \
M_CALL_CLEAR(key_oplist, n->key[i]); \
M_IF(isMap)(M_CALL_CLEAR(value_oplist, n->kind.value[i]);,) \
memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(num-1-i)); \
@@ -574,6 +639,7 @@
int num_right = M_C(name, _get_num)(right); \
assert (num_left > N/2); \
assert (num_right < N/2); \
\
/* Move one item from the left node to the right node */ \
memmove(&right->key[1], &right->key[0], sizeof(key_t)*num_right); \
if (M_C(name, _is_leaf)(left)) { \
@@ -582,10 +648,10 @@
M_IF(isMap)(memmove (&right->kind.value[0], &left->kind.value[num_left-1], sizeof (value_t));,) \
right->num = -num_right - 1; \
left->num = -num_left + 1; \
M_CALL_SET(key_oplist, parent->key[k], right->key[0]); \
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left-2]); \
} else { \
memmove(&right->kind.node[1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
/* parent[k] is move to right[0] (clear). parent[k] is therefore clear */ \
/* parent[k] is moved to right[0] (clear). parent[k] is therefore clear */ \
memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \
right->kind.node[0] = left->kind.node[num_left]; \
right->num = num_right + 1; \
@@ -608,7 +674,8 @@
int num_right = M_C(name, _get_num)(right); \
assert (num_left < N/2); \
assert (num_right > N/2); \
/* Move one item from right to left. */ \
\
/* Move one item from the right node to the left node. */ \
if (M_C(name, _is_leaf)(right)) { \
memmove (&left->key[num_left], &right->key[0], sizeof(key_t)); \
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(num_right-1)); \
@@ -616,7 +683,7 @@
M_IF(isMap)(memmove (&right->kind.value[0], &right->kind.value[1], sizeof(value_t)*(num_right-1));,) \
right->num = -num_right + 1; \
left->num = -num_left - 1; \
M_CALL_SET(key_oplist, parent->key[k], right->key[0]); \
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left]); \
} else { \
memmove (&left->key[num_left], &parent->key[k], sizeof (key_t)); \
memmove (&parent->key[k], &right->key[0], sizeof (key_t)); \
@@ -641,13 +708,14 @@
int num_left = M_C(name, _get_num)(left); \
int num_right = M_C(name, _get_num)(right); \
\
/* Merge node 'k' and 'k+1' into a single one */ \
if (leaf) { \
assert (num_left + num_right <= N); \
memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*num_right); \
M_IF(isMap)(memmove(&left->kind.value[num_left], &right->kind.value[0], sizeof(value_t)*num_right);,) \
left->num = -num_left - num_right; \
} else { \
assert (num_left + num_right + 1 <= N); \
assert (num_left + num_right <= N -1); \
memmove(&left->key[num_left+1], &right->key[0], sizeof(key_t)*num_right); \
memmove(&left->kind.node[num_left+1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
M_CALL_INIT_SET(key_oplist, left->key[num_left], parent->key[k]); \
@@ -664,10 +732,11 @@
\
/* We can also cache the index when we descend the tree. \
TODO: Bench if this is worth the effort.*/ \
static inline int M_C(name, _search_node)(node_t parent, node_t child) \
static inline int \
M_C(name, _search_for_node)(node_t parent, node_t child) \
{ \
assert (!M_C(name, _is_leaf)(parent)); \
int num = M_C(name, _get_num)(parent); \
const int num = M_C(name, _get_num)(parent); \
for(int i = 0; i <= num; i++) { \
if (parent->kind.node[i] == child) \
return i; \
@@ -678,14 +747,17 @@
\
static inline bool M_C(name, _erase)(tree_t b, key_t const key) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
pit_t pit; \
node_t leaf = M_C(name, _search_leaf)(pit, b, key); \
int k = M_C(name, _search_and_remove_leaf)(leaf, key); \
/* If not found, or number of keys greater than N>2 or root */ \
node_t leaf = M_C(name, _search_for_leaf)(pit, b, key); \
int k = M_C(name, _search_and_remove_in_leaf)(leaf, key); \
/* If key is not found ==> erase failed */ \
if (k < 0) return false; \
/* Remove one item from the B+TREE */ \
b->size --; \
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || pit->num == 0) return true; \
/* If number of keys greater than N>2 or root ==> Nothing more to do */ \
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || pit->num == 0) \
return true; \
/* Leaf is too small. Needs rebalancing */ \
assert (M_C(name, _get_num)(leaf) == N/2-1); \
bool pass1 = true; \
@@ -694,9 +766,9 @@
/* Search for node 'leaf' in parent */ \
node_t parent = pit->parent[--pit->num]; \
assert (parent != NULL); \
k = M_C(name, _search_node)(parent, leaf); \
k = M_C(name, _search_for_node)(parent, leaf); \
/* Look for the neighboor of the removed key. */ \
/* if we can still one key to keep our node balanced */ \
/* if we can steal one key from them to keep our node balanced */ \
if (k > 0 && M_C(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
M_C(name, _left_shift)(parent, k-1); \
return true; \
@@ -711,8 +783,9 @@
assert(k >= 0 && k < M_C(name, _get_num)(parent)); \
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
M_C(name, _merge_node)(parent, k, pass1); \
/* Check if we need to backport */ \
if (M_C(name, _get_num)(parent) >= N/2) return true; \
/* Check if we need to continue */ \
if (M_C(name, _get_num)(parent) >= N/2) \
return true; \
if (pit->num == 0) { \
/* We reach the root */ \
if (M_C(name, _get_num)(parent) == 0) { \
@@ -728,12 +801,6 @@
} \
} \
\
static inline bool M_ATTR_DEPRECATED \
M_C(name, _remove)(tree_t b, key_t const key) \
{ \
return M_C(name, _erase)(b, key); \
} \
\
static inline bool M_C(name, _pop_at)(value_t *ptr, tree_t b, key_t const key) \
{ \
if (ptr != NULL) { \
@@ -749,7 +816,7 @@
static inline void \
M_C(name, _it)(it_t it, const tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
assert (it != NULL); \
node_t n = b->root; \
/* Scan down the nodes */ \
@@ -763,7 +830,7 @@
static inline void \
M_C(name, _it_end)(it_t it, const tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
assert (it != NULL); \
node_t n = b->root; \
/* Scan down the nodes */ \
@@ -831,13 +898,13 @@
static inline void \
M_C(name, _it_from)(it_t it, const tree_t b, key_t const key) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
assert (it != NULL); \
pit_t pit; \
node_t n = M_C(name, _search_leaf)(pit, b, key); \
node_t n = M_C(name, _search_for_leaf)(pit, b, key); \
it->node = n; \
int i; \
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
for(i = 0; i < -n->num; i++) { \
if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \
break; \
@@ -850,7 +917,7 @@
} \
\
static inline bool \
M_C(name, _it_to_p)(it_t it, key_t const key) \
M_C(name, _it_until_p)(it_t it, key_t const key) \
{ \
assert (it != NULL); \
node_t n = it->node; \
@@ -859,10 +926,26 @@
return (cmp >= 0); \
} \
\
static inline bool \
M_C(name, _it_while_p)(it_t it, key_t const key) \
{ \
assert (it != NULL); \
node_t n = it->node; \
if (it->idx >= -n->num) return false; \
int cmp = M_CALL_CMP(key_oplist, n->key[it->idx], key); \
return (cmp <= 0); \
} \
\
static inline bool M_ATTR_DEPRECATED \
M_C(name, _it_to_p)(it_t it, key_t const key) \
{ \
return M_C(name, _it_until_p)(it, key); \
} \
\
static inline value_t * \
M_C(name, _min)(const tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
assert (b->size > 0); \
node_t n = b->root; \
/* Scan down the nodes */ \
@@ -875,7 +958,7 @@
static inline value_t * \
M_C(name, _max)(const tree_t b) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
node_t n = b->root; \
/* Scan down the nodes */ \
while (!M_C(name, _is_leaf)(n)) { \
@@ -899,46 +982,47 @@
static inline void \
M_C(name, _init_move)(tree_t b, tree_t ref) \
{ \
BPTREEI_CONTRACT(N, key_oplist, ref); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, ref); \
assert (b != NULL && b != ref); \
b->size = ref->size; \
b->root = ref->root; \
ref->root = NULL; \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void \
M_C(name, _move)(tree_t b, tree_t ref) \
{ \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, key_oplist, ref); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, ref); \
assert (b != ref); \
M_C(name,_clear)(b); \
M_C(name,_init_move)(b, ref); \
BPTREEI_CONTRACT(N, key_oplist, b); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void \
M_C(name, _swap)(tree_t tree1, tree_t tree2) \
{ \
BPTREEI_CONTRACT(N, key_oplist, tree1); \
BPTREEI_CONTRACT(N, key_oplist, tree2); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree2); \
M_SWAP(size_t, tree1->size, tree2->size); \
M_SWAP(node_t, tree1->root, tree2->root); \
BPTREEI_CONTRACT(N, key_oplist, tree1); \
BPTREEI_CONTRACT(N, key_oplist, tree2); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree2); \
} \
\
BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, tree_t, node_t, pit_t, it_t)
BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t)
/* Define additional functions. Do not used any fields but the already defined methods */
#define BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, tree_t, node_t, pit_t, it_t) \
/* Define additional functions.
Do not used any fields but the already defined methods */
#define BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t) \
\
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)( \
static inline bool M_C(name,_equal_p)(const tree_t t1, const tree_t t2) { \
BPTREEI_CONTRACT(N, key_oplist, t1); \
BPTREEI_CONTRACT(N, key_oplist, t2); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t2); \
if (t1->size != t2->size) return false; \
if (t1->size == 0) return true; \
/* Slow comparaison */ \
@@ -973,7 +1057,7 @@
\
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)( \
static inline size_t M_C(name,_hash)(const tree_t t1) { \
BPTREEI_CONTRACT(N, key_oplist, t1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
M_HASH_DECL(hash); \
/* NOTE: We can't compute the hash directly for the same reason \
than for EQUAL operator. */ \
@@ -996,7 +1080,7 @@
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
static inline void M_C(name, _get_str)(string_t str, \
const tree_t t1, bool append) { \
BPTREEI_CONTRACT(N, key_oplist, t1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
assert(str != NULL); \
(append ? string_cat_str : string_set_str) (str, "["); \
/* NOTE: The print is really naive, and not really efficient */ \
@@ -1025,7 +1109,7 @@
static inline void \
M_C(name, _out_str)(FILE *file, tree_t const t1) \
{ \
BPTREEI_CONTRACT(N, key_oplist, t1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
assert (file != NULL); \
fputc ('[', file); \
bool commaToPrint = false; \
@@ -1053,7 +1137,7 @@
static inline bool \
M_C(name, _parse_str)(tree_t t1, const char str[], const char **endp) \
{ \
BPTREEI_CONTRACT(N, key_oplist, t1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
assert (str != NULL); \
M_C(name,_clean)(t1); \
bool success = false; \
@@ -1095,7 +1179,7 @@
static inline bool \
M_C(name, _in_str)(tree_t t1, FILE *file) \
{ \
BPTREEI_CONTRACT(N, key_oplist, t1); \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
assert (file != NULL); \
M_C(name,_clean)(t1); \
int c = fgetc(file); \
@@ -1128,6 +1212,93 @@
return c == ']'; \
} \
, /* no in_str */ ) \
\
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
{ \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
const M_C(name, _type_t) *item; \
bool first_done = false; \
it_t it; \
/* Format is different between associative container \
& set container */ \
M_IF(isMap)( \
ret = f->m_interface->write_map_start(local, f, t1->size); \
for (M_C(name, _it)(it, t1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_map_next(local, f); \
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item->key_ptr); \
ret |= f->m_interface->write_map_value(local, f); \
ret |= M_CALL_OUT_SERIAL(value_oplist, f, *item->value_ptr); \
first_done = true; \
} \
ret |= f->m_interface->write_map_end(local, f); \
, \
ret = f->m_interface->write_array_start(local, f, t1->size); \
for (M_C(name, _it)(it, t1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \
first_done = true; \
} \
ret |= f->m_interface->write_array_end(local, f); \
) \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(tree_t t1, m_serial_read_t f) \
{ \
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
key_t key; \
M_C(name,_clean)(t1); \
M_IF(isMap)( \
value_t value; \
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(key_oplist, key); \
M_CALL_INIT (value_oplist, value); \
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
ret = f->m_interface->read_map_value(local, f); \
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
M_C(name, _set_at)(t1, key, value); \
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(key_oplist, key); \
M_CALL_CLEAR(value_oplist, value); \
, \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(key_oplist, key); \
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push)(t1, key); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(key_oplist, key); \
) /* End of IF isMap */ \
return ret; \
} \
, /* no in_serial */ ) \
\
#endif
+147 -44
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Fixed size BUFFER & STACK interface
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -29,12 +29,12 @@
#include "m-mutex.h"
#include "m-atomic.h"
/* Define the different kind of policy a buffer can have:
/* Define the different kind of policy a lock-based buffer can have:
* - the buffer can be either a queue (policy is FIFO) or a stack (policy is FILO),
* - if the push is blocking (waiting for the buffer to has some space) or not,
* - if the pop is blocking (waiting for the buffer to has some space) or not,
* - if both push / pop are blocking,
* - if it shall be thread safe or not (i.e. remove mutex and atomic costs),
* - if the push method is by default blocking (waiting for the buffer to has some space) or not,
* - if the pop method is by default blocking (waiting for the buffer to has some data) or not,
* - if both methods are blocking,
* - if it shall be thread safe or not (i.e. remove the mutex lock and atomic costs),
* - if the buffer has to be init with empty elements, or if it shall init an element when it is pushed (and moved when popped),
* - if the buffer has to overwrite the last element if the buffer is full,
* - if the pop of an element is not complete until the call to pop_release (preventing push until this call).
@@ -53,14 +53,14 @@ typedef enum {
/* Define a lock based buffer.
USAGE: BUFFER_DEF(name, type, size_of_buffer_or_0, policy[, oplist]) */
#define BUFFER_DEF(name, type, m_size, ... ) \
BUFFERI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
BUFFERI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, type, m_size,__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), M_C(name,_t)), \
(name, type, m_size,__VA_ARGS__, M_C(name,_t))))
/* Define the oplist of a lock based buffer given its name and its oplist.
USAGE: BUFFER_OPLIST(name[, oplist of the type]) */
#define BUFFER_OPLIST(...) \
BUFFERI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
BUFFERI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
@@ -69,7 +69,7 @@ typedef enum {
Size of created queue can only a power of 2.
*/
#define QUEUE_MPMC_DEF(name, type, ...) \
QUEUEI_MPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
QUEUEI_MPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), M_C(name,_t)), \
(name, type, __VA_ARGS__, M_C(name,_t))))
@@ -78,7 +78,7 @@ typedef enum {
Size of created queue can only a power of 2.
*/
#define QUEUE_SPSC_DEF(name, type, ...) \
QUEUEI_SPSC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
QUEUEI_SPSC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), M_C(name,_t)), \
(name, type, __VA_ARGS__, M_C(name,_t))))
@@ -102,9 +102,11 @@ typedef enum {
assert (atomic_load(&buffer->number[0]) <= BUFFERI_SIZE(size)); \
} while (0)
#define BUFFERI_DEF(arg) BUFFERI_DEF2 arg
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define BUFFERI_DEF_P1(arg) BUFFERI_DEF_P2 arg
#define BUFFERI_DEF2(name, type, m_size, policy, oplist, buffer_t) \
#define BUFFERI_DEF_P2(name, type, m_size, policy, oplist, buffer_t) \
\
typedef struct M_C(name, _s) { \
m_mutex_t mutexPush; /* MUTEX used for pushing elements */ \
@@ -566,24 +568,6 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
// Deferred evaluation for the oplist definition.
#define BUFFERI_OPLIST(arg) BUFFERI_OPLIST2 arg
/* OPLIST defininition of a buffer */
#define BUFFERI_OPLIST2(name, oplist) \
(INIT(M_C(name, _int_init)) \
,INIT_SET(M_C(name, _init_set)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,TYPE(M_C(name,_t)) \
,SUBTYPE(M_C(name, _type_t)) \
,CLEAN(M_C(name,_clean)) \
,PUSH(M_C(name,_push)) \
,POP(M_C(name,_pop)) \
,OPLIST(oplist) \
)
/* Definition of a a QUEUE for Many Produccer / Many Consummer
for high bandwidth scenario:
@@ -595,7 +579,10 @@ M_C(name, _init)(buffer_t v, size_t size) \
* size of queue is always a power of 2
* no overwriting.
*/
#define QUEUEI_MPMC_DEF(arg) QUEUEI_MPMC_DEF2 arg
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define QUEUEI_MPMC_DEF_P1(arg) QUEUEI_MPMC_DEF_P2 arg
#ifdef NDEBUG
# define QUEUEI_MPMC_CONTRACT(v) /* nothing */
@@ -611,7 +598,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
} while (0)
#endif
#define QUEUEI_MPMC_DEF2(name, type, policy, oplist, buffer_t) \
#define QUEUEI_MPMC_DEF_P2(name, type, policy, oplist, buffer_t) \
\
/* The sequence number of an element will be equal to either \
- 2* the index of the production which creates it, \
@@ -624,22 +611,22 @@ M_C(name, _init)(buffer_t v, size_t size) \
typedef struct M_C(name, _el_s) { \
atomic_uint seq; /* Can only increase until overflow */ \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_uint)+sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_uint)-sizeof(type) : 1]; \
M_CACHELINE_ALIGN(align, atomic_uint, type); \
} M_C(name, _el_t); \
\
/* If there is only one producer and one consummer, then they won't \
typically use the same cache line, increasing performance. */ \
typedef struct M_C(name, _s) { \
atomic_uint ProdIdx; /* Can only increase until overflow */ \
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_CACHELINE_ALIGN(align1, atomic_uint); \
atomic_uint ConsoIdx; /* Can only increase until overflow */ \
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_CACHELINE_ALIGN(align2, atomic_uint); \
M_C(name, _el_t) *Tab; \
unsigned int size; \
} buffer_t[1]; \
\
static inline bool \
M_C(name, _push)(buffer_t table, type x) \
M_C(name, _push)(buffer_t table, type const x) \
{ \
QUEUEI_MPMC_CONTRACT(table); \
unsigned int idx = atomic_load_explicit(&table->ProdIdx, \
@@ -783,7 +770,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
/* Definition of a a QUEUE for Single Produccer / Single Consummer
/* Definition of a a QUEUE for Single Producer / Single Consummer
for high bandwidth scenario:
* wait-free,
* quite fast
@@ -792,7 +779,10 @@ M_C(name, _init)(buffer_t v, size_t size) \
* size of queue is always a power of 2
* no overwriting.
*/
#define QUEUEI_SPSC_DEF(arg) QUEUEI_SPSC_DEF2 arg
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define QUEUEI_SPSC_DEF_P1(arg) QUEUEI_SPSC_DEF_P2 arg
#ifdef NDEBUG
#define QUEUEI_SPSC_CONTRACT(table) do { } while (0)
@@ -808,7 +798,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
} while (0)
#endif
#define QUEUEI_SPSC_DEF2(name, type, policy, oplist, buffer_t) \
#define QUEUEI_SPSC_DEF_P2(name, type, policy, oplist, buffer_t) \
\
typedef struct M_C(name, _el_s) { \
type x; \
@@ -818,12 +808,12 @@ M_C(name, _init)(buffer_t v, size_t size) \
atomic_uint consoIdx; /* Can only increase until overflow */ \
unsigned int size; \
M_C(name, _el_t) *Tab; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_uint) +sizeof(size_t) + sizeof(void*) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_uint) -sizeof(size_t)-sizeof(void*): 1]; \
M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_C(name, _el_t) *); \
atomic_uint prodIdx; /* Can only increase until overflow */ \
} buffer_t[1]; \
\
static inline bool \
M_C(name, _push)(buffer_t table, type x) \
M_C(name, _push)(buffer_t table, type const x) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -844,6 +834,27 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
\
static inline bool \
M_C(name, _push_move)(buffer_t table, type x) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_acquire); \
if (w-r >= table->size) \
return false; \
unsigned int i = w & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_DO_MOVE(oplist, table->Tab[i].x, x); \
} else { \
M_DO_INIT_MOVE(oplist, table->Tab[i].x, x); \
} \
atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
return true; \
} \
\
static inline bool \
M_C(name, _pop)(type *ptr, buffer_t table) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
@@ -865,6 +876,81 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline unsigned \
M_C(name, _push_bulk)(buffer_t table, unsigned n, type const x[]) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
assert (x != NULL); \
assert (n <= table->size); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_acquire); \
unsigned int max = M_MIN(n, table->size - (w-r) ); \
if (max == 0) \
return 0; \
for(unsigned int k = 0; k < max; k++) { \
unsigned int i = (w+k) & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, table->Tab[i].x, x[k]); \
} else { \
M_CALL_INIT_SET(oplist, table->Tab[i].x, x[k]); \
} \
} \
atomic_store_explicit(&table->prodIdx, w+max, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
return max; \
} \
\
static inline unsigned \
M_C(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
assert (ptr != NULL); \
assert (n <= table->size); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_relaxed); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_acquire); \
if (w-r == 0) \
return 0; \
unsigned int max = M_MIN(w-r, n); \
for(unsigned int k = 0; k < max; k++) { \
unsigned int i = (r+k) & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, ptr[k], table->Tab[i].x); \
} else { \
M_DO_INIT_MOVE (oplist, ptr[k], table->Tab[i].x); \
} \
} \
atomic_store_explicit(&table->consoIdx, r+max, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
return max; \
} \
\
static inline void \
M_C(name, _push_force)(buffer_t table, type const x) \
{ \
QUEUEI_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_acquire); \
/* If no place in queue, try to skip the last one */ \
while (w-r >= table->size) { \
bool b = atomic_compare_exchange_strong(&table->consoIdx, &r, r+1); \
r += b; \
} \
unsigned int i = w & (table->size -1); \
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, table->Tab[i].x, x); \
} else { \
M_CALL_INIT_SET(oplist, table->Tab[i].x, x); \
} \
atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \
QUEUEI_SPSC_CONTRACT(table); \
} \
\
static inline size_t \
M_C(name, _size)(buffer_t table) \
{ \
@@ -952,8 +1038,25 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
\
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define BUFFERI_OPLIST_P1(arg) BUFFERI_OPLIST_P2 arg
/* OPLIST defininition of a buffer */
#define BUFFERI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _int_init)) \
,INIT_SET(M_C(name, _init_set)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,TYPE(M_C(name,_t)) \
,SUBTYPE(M_C(name, _type_t)) \
,CLEAN(M_C(name,_clean)) \
,PUSH(M_C(name,_push)) \
,POP(M_C(name,_pop)) \
,OPLIST(oplist) \
,TEST_EMPTY(M_C(name, _empty_p)), \
,GET_SIZE(M_C(name, _size)) \
)
// NOTE: SET & INIT_SET are deliberatly missing. TBC if it the right way.
// NOTE: There is no oplist defined for this container.
#endif
+826
View File
@@ -0,0 +1,826 @@
/*
* M*LIB - Concurrent memory pool allocator
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_CONCURRENT_MEMPOOL_H
#define MSTARLIB_CONCURRENT_MEMPOOL_H
#include "m-core.h"
#include "m-atomic.h"
#include "m-genint.h"
/* Minimum number of nodes per group of nodes */
#define C_MEMPOOL_MIN_NODE_PER_GROUP 16
#define C_MEMPOOL_DEF(name, type_t) \
C_MEMPOOL_DEF_SINGLY_LIST(name, type_t) \
C_MEMPOOL_DEF_LF_QUEUE(name, type_t) \
C_MEMPOOL_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
C_MEMPOOL_DEF_SYSTEM_ALLOC(name, type_t) \
C_MEMPOOL_DEF_LF_MEMPOOL(name, type_t)
/* Classic Singly List without allocation */
#define C_MEMPOOL_DEF_SINGLY_LIST(name, type_t) \
\
typedef struct M_C(name, _slist_node_s) { \
struct M_C(name, _slist_node_s) *next; \
type_t data; \
} M_C(name, _slist_node_t); \
\
typedef struct M_C(name, _slist_node_s) *M_C(name, _slist_t)[1]; \
\
static inline void \
M_C(name, _slist_init)(M_C(name, _slist_t) list) \
{ \
*list = NULL; \
} \
\
static inline void \
M_C(name, _slist_push)(M_C(name, _slist_t) list, \
M_C(name, _slist_node_t) *node) \
{ \
node->next = *list; \
*list = node; \
} \
\
static inline M_C(name, _slist_node_t) * \
M_C(name, _slist_pop)(M_C(name, _slist_t) list) \
{ \
assert (*list != NULL); \
M_C(name, _slist_node_t) *node = *list; \
*list = node->next; \
M_IF_DEBUG(node->next = NULL;) \
return node; \
} \
\
static inline bool \
M_C(name, _slist_empty_p)(M_C(name, _slist_t) list) \
{ \
return *list == NULL; \
} \
\
static inline void \
M_C(name, _slist_move)(M_C(name, _slist_t) list, \
M_C(name, _slist_t) src) \
{ \
*list = *src; \
*src = NULL; \
} \
\
static inline void \
M_C(name, _slist_clear)(M_C(name, _slist_t) list) \
{ \
M_C(name, _slist_node_t) *it = *list, *next; \
while (it) { \
next = it->next; \
M_MEMORY_DEL(it); \
it = next; \
} \
*list = NULL; \
} \
/* Lock Free free queue list (not generic one) of lists without allocation
Based on Michael & Scott Lock Free Queue List algorithm.
Each list is considered empty if there is only one node within.
This LF Queue List doesn't try to prevent the ABA problem. It is up to the
caller to avoid recycling the nodes too fast.
Each list has its own unique NIL ptr in order to avoid issues when
migrating a node from a Q to another: in the following scenario,
- Thread 1 performs a PUSH of N in Q1 with Q1 empty (only node is NA)
NA.next is NIL.
- Thread 1 is interrupted just before the CAS on NA.next
- Thread 2 performs a sucessfull push of NB in Q1. NA.next is set to NB.
- Thread 2 performs a sucessfull pop of NA in Q1
- Thread 2 performs a sucessfull push of NA in Q2. NA.next is set to NIL.
- Thread 1 is restored and will succeed as NA.next is once again NIL.
In order to prevent the last CAS to succeed, each queue uses its own NIL pointer.
It is a derived problem of the ABA problem.
*/
/* TODO: Optimize alignement to reduce memory consumption. NIL object can use []
to reduce memory consumption too (non compatible with C++ ...) */
#define C_MEMPOOL_DEF_LF_QUEUE(name, type_t) \
\
typedef struct M_C(name, _lf_node_s) { \
M_ATTR_EXTENSION _Atomic(struct M_C(name, _lf_node_s) *) next; \
m_gc_atomic_ticket_t cpt; \
M_C(name, _slist_t) list; \
} M_C(name, _lf_node_t); \
\
typedef struct M_C(name, _lflist_s) { \
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) head; \
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) tail; \
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_C(name, _lf_node_t) nil; \
} M_C(name, _lflist_t)[1]; \
\
static inline void \
M_C(name, _lflist_init)(M_C(name, _lflist_t) list, \
M_C(name, _lf_node_t) *node) \
{ \
atomic_init(&list->head, node); \
atomic_init(&list->tail, node); \
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
} \
\
static inline bool \
M_C(name, _lflist_empty_p)(M_C(name, _lflist_t) list) \
{ \
return atomic_load(&list->tail) == atomic_load(&list->head); \
} \
\
static inline void \
M_C(name, _lflist_push)(M_C(name, _lflist_t) list, \
M_C(name, _lf_node_t) *node, m_backoff_t bkoff) \
{ \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
\
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
m_backoff_reset(bkoff); \
while (true) { \
tail = atomic_load(&list->tail); \
next = atomic_load_explicit(&tail->next, memory_order_acquire); \
if (M_UNLIKELY(next != &list->nil)) { \
/* Tail was not pointing to the last node \
Try to swing Tail to the next node */ \
atomic_compare_exchange_weak_explicit(&list->tail, \
&tail, next, \
memory_order_release, \
memory_order_relaxed); \
} else { \
/* Try to link node at the end of the linked list */ \
if (atomic_compare_exchange_strong_explicit(&tail->next, \
&next, node, \
memory_order_release, \
memory_order_relaxed)) \
break; \
m_backoff_wait(bkoff); \
} \
} \
/* Enqueue is done. Try to swing Tail to the inserted node \
If it fails, someone else will do it or has already did it. */ \
atomic_compare_exchange_strong_explicit(&list->tail, &tail, node, \
memory_order_acq_rel, \
memory_order_relaxed); \
} \
\
static inline M_C(name, _lf_node_t) * \
M_C(name, _lflist_pop)(M_C(name, _lflist_t) list, m_backoff_t bkoff) \
{ \
M_C(name, _lf_node_t) *head; \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
\
/* Reinitialize backoff */ \
m_backoff_reset(bkoff); \
while (true) { \
head = atomic_load(&list->head); \
tail = atomic_load(&list->tail); \
next = atomic_load(&head->next); \
/* Are head, tail, and next consistent?*/ \
if (M_LIKELY(head == \
atomic_load_explicit(&list->head, memory_order_relaxed))) \
{ \
/* Is queue empty or Tail falling behind? */ \
if (head == tail) { \
/* Is queue empty? */ \
if (next == &list->nil) \
return NULL; \
/* Tail is falling behind. Try to advance it */ \
atomic_compare_exchange_strong_explicit(&list->tail, &tail, \
next, \
memory_order_release, \
memory_order_relaxed); \
} else { \
/* Try to swing Head to the next node */ \
if (atomic_compare_exchange_strong_explicit(&list->head, \
&head, next, \
memory_order_release, \
memory_order_relaxed)) { \
break; \
} \
/* Failure: perform a random exponential backoff */ \
m_backoff_wait(bkoff); \
} \
} \
} \
/* dequeue returns an element that becomes the new dummy element (the new head), \
and the former dummy element (the former head) is removed: \
Since we want a link of free list, and we don't care about the content itsef, \
provided that the node we return is older than the one we should return, \
Therefore, we return the previous dummy head. \
As such, it is not the original MSqueue algorithm. */ \
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
return head; \
} \
\
/* Dequeue a node if the node is old enough */ \
static inline M_C(name, _lf_node_t) * \
M_C(name, _lflist_pop_if)(M_C(name, _lflist_t) list, \
m_gc_ticket_t age, m_backoff_t bkoff) \
{ \
M_C(name, _lf_node_t) *head; \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
\
m_backoff_reset(bkoff); \
while (true) { \
head = atomic_load(&list->head); \
tail = atomic_load(&list->tail); \
next = atomic_load(&head->next); \
if (M_LIKELY(head == atomic_load_explicit(&list->head, memory_order_relaxed))) \
{ \
if (head == tail) { \
if (next == &list->nil) \
return NULL; \
atomic_compare_exchange_strong_explicit(&list->tail, &tail, next, \
memory_order_release, \
memory_order_relaxed); \
} else { \
/* Test if the node is old enought to be popped */ \
if (atomic_load_explicit(&next->cpt, memory_order_relaxed) >= age) \
return NULL; \
/* Try to swing Head to the next node */ \
if (atomic_compare_exchange_strong_explicit(&list->head, \
&head, next, \
memory_order_release, \
memory_order_relaxed)) { \
break; \
} \
m_backoff_wait(bkoff); \
} \
} \
} \
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
return head; \
} \
\
static inline void \
M_C(name, _lflist_clear)(M_C(name, _lflist_t) list) \
{ \
m_backoff_t bkoff; \
m_backoff_init(bkoff); \
while (true) { \
M_C(name, _lf_node_t) *node = M_C(name, _lflist_pop)(list, bkoff); \
if (node == NULL) break; \
M_C(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
memory_order_relaxed); \
M_C(name, _slist_clear)(node->list); \
M_MEMORY_DEL(node); \
node = next; \
} \
/* Dummy node to free too */ \
M_C(name, _lf_node_t) *dummy; \
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
M_C(name, _slist_clear)(dummy->list); \
M_MEMORY_DEL(dummy); \
} \
/* System node allocator: request memory to the system.
As such it is a non Lock-Free path. */
#define C_MEMPOOL_DEF_SYSTEM_ALLOC(name, type_t) \
\
static inline M_C(name, _lf_node_t) * \
M_C(name, _alloc_node)(unsigned int initial) \
{ \
M_C(name, _lf_node_t) * node; \
node = M_MEMORY_ALLOC(M_C(name, _lf_node_t)); \
if (M_UNLIKELY(node == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
return NULL; \
} \
atomic_init(&node->next, (M_C(name, _lf_node_t) *) 0); \
atomic_init(&node->cpt, 0UL); \
M_C(name, _slist_init)(node->list); \
for(unsigned i = 0; i < initial; i++) { \
M_C(name, _slist_node_t) *n; \
n = M_MEMORY_ALLOC(M_C(name, _slist_node_t)); \
if (M_UNLIKELY(n == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
return NULL; \
} \
M_C(name, _slist_push)(node->list, n); \
} \
return node; \
} \
/* Concurrent Memory pool
The data structure is the following.
Each thread has its own pool of nodes (local) that only it can
access (it is a singly list). If there is no longer any node in this
pool, it requests a new pool to the lock free queue of pool (group of
nodes). If it fails, it requests a new pool to the system allocator
(and from there it is no longer lock free).
This memory pool can only be lock free if the initial state is
sufficiently dimensionned to avoid calling the system allocator during
the normal processing.
Then each thread pushs its deleted node into another pool of nodes,
where the node is logically deleted (no contain of the node is destroyed
at this point and the node can be freely accessed by other threads).
Once the thread mempool is put to sleep, the age of the pool of logical
deleted nodes is computed and this pool is move to the Lock Free Queue
List of pools to be reclaimed. Then A Garbage Collector is performed
on this Lock Free Queue list to reclaim all pools thare are sufficiently
aged (taking into account the grace period of the pool) to be moved back
to the Lock Free Queue of the free pools.
Each pool of nodes can be in the following state:
* FREE state if it is present in the Lock Free Queue of free pools.
* EMPTY state if it is present in the Lock Free Queue of empty pools
which means that the nodes present in it has been USED directly by a thread,
* TO_BE_RECLAIMED state if it is present in the Lock Free Queue of TBR pools
A pool of nodes will go to the following state:
FREE --> EMPTY --> TO_BE_RECLAIMED
^ |
+----------------------+
The ABA problem is taken into account as a node cannot be reused in the
same queue without performing a full cycle of its state. Moreover
it can only move from TO_BE_RECLAIMED to FREE if and only if a grace
period is finished (and then we are sure that no thread references any
older node).
Each thread has its own backoff structure (with local pseudo-random
generator).
The grace period is detected through a global age counter (ticket)
that is incremented each time a thread is awaken / sleep.
Each thread has its own age that is set to the global ticket on sleep/awaken.
The age of the pool to be reclaimed is also set to this global age counter.
To ensure that the grace period is finished, it tests if all threads
are younger than the age of the pool to be reclaimed.
From a performance point of view, this puts a bottleneck on the global
age counter that is shared and incremented by all threads. However,
the sleep/awaken operations are much less frequent than other operations.
Thus, it shall not have a huge impact on the performance if the user
code is intelligent with the sleep/awaken operations.
As such it won't support more than ULONG_MAX sleep for all threads.
*/
#define C_MEMPOOL_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
\
typedef struct M_C(name, _lfmp_thread_s) { \
M_C(name, _slist_t) free; \
M_C(name, _slist_t) to_be_reclaimed; \
M_CACHELINE_ALIGN(align1, M_C(name, _slist_t), M_C(name, _slist_t)); \
} M_C(name, _lfmp_thread_t); \
\
static inline void \
M_C(name, _lfmp_thread_init)(M_C(name, _lfmp_thread_t) *t) \
{ \
M_C(name, _slist_init)(t->free); \
M_C(name, _slist_init)(t->to_be_reclaimed); \
} \
\
static inline void \
M_C(name, _lfmp_thread_clear)(M_C(name, _lfmp_thread_t) *t) \
{ \
assert(M_C(name, _slist_empty_p)(t->to_be_reclaimed)); \
M_C(name, _slist_clear)(t->free); \
M_C(name, _slist_clear)(t->to_be_reclaimed); \
} \
/* NOTE: once a node is deleted, its data are kept readable until the future GC */
#define C_MEMPOOL_DEF_LF_MEMPOOL(name, type_t) \
\
typedef struct M_C(name, _s) { \
unsigned initial; \
M_C(name, _lfmp_thread_t) *thread_data; \
M_C(name, _lflist_t) free; \
M_C(name, _lflist_t) to_be_reclaimed; \
M_C(name, _lflist_t) empty; \
m_gc_mempool_list_t mempool_node; \
struct m_gc_s *gc_mem; \
} M_C(name, _t)[1]; \
\
/* Garbage collect of the nodes of the mempool on sleep */ \
static inline void \
M_C(name, _int_gc_on_sleep)(m_gc_t gc_mem, m_gc_mempool_list_t *data, \
m_gc_tid_t id, m_gc_ticket_t ticket, m_gc_ticket_t min_ticket) \
{ \
/* Get back the mempool from the node */ \
struct M_C(name, _s) *mempool = \
M_TYPE_FROM_FIELD(struct M_C(name, _s), data, m_gc_mempool_list_t, mempool_node); \
\
/* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \
if (!M_C(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
M_C(name, _lf_node_t) *node; \
/* Get a new empty group of nodes */ \
node = M_C(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \
if (M_UNLIKELY (node == NULL)) { \
/* Fail to get an empty group of node. \
Alloc a new one from the system */ \
node = M_C(name, _alloc_node)(0); \
assert(node != NULL); \
} \
assert(M_C(name, _slist_empty_p)(node->list)); \
M_C(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \
M_C(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \
} \
\
/* Perform a GC of the freelist of nodes */ \
while (true) { \
M_C(name, _lf_node_t) *node; \
node = M_C(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
min_ticket, gc_mem->thread_data[id].bkoff); \
if (node == NULL) break; \
M_C(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_C(name, _init)(M_C(name, _t) mem, m_gc_t gc_mem, \
unsigned init_node_count, unsigned init_group_count) \
{ \
const unsigned long max_thread = gc_mem->max_thread; \
/* Initialize the thread data of the mempool */ \
mem->thread_data = M_MEMORY_REALLOC(M_C(name, _lfmp_thread_t), NULL, max_thread); \
if (mem->thread_data == NULL) { \
M_MEMORY_FULL(max_thread * sizeof(M_C(name, _lfmp_thread_t))); \
return; \
} \
for(unsigned i = 0; i < max_thread;i++) { \
M_C(name, _lfmp_thread_init)(&mem->thread_data[i]); \
} \
/* Preallocate some group of nodes for the mempool */ \
mem->initial = M_MAX(C_MEMPOOL_MIN_NODE_PER_GROUP, init_node_count); \
M_C(name, _lflist_init)(mem->free, M_C(name, _alloc_node)(init_node_count)); \
M_C(name, _lflist_init)(mem->to_be_reclaimed, M_C(name, _alloc_node)(init_node_count)); \
M_C(name, _lflist_init)(mem->empty, M_C(name, _alloc_node)(0)); \
for(unsigned i = 1; i < init_group_count; i++) { \
M_C(name, _lflist_push)(mem->free, M_C(name, _alloc_node)(init_node_count), \
gc_mem->thread_data[0].bkoff); \
M_C(name, _lflist_push)(mem->empty, M_C(name, _alloc_node)(0), \
gc_mem->thread_data[0].bkoff); \
} \
/* Register the mempool in the GC */ \
mem->mempool_node.gc_on_sleep = M_C(name, _int_gc_on_sleep); \
mem->mempool_node.next = gc_mem->mempool_list; \
gc_mem->mempool_list = &mem->mempool_node; \
mem->gc_mem = gc_mem; \
} \
\
static inline void \
M_C(name, _clear)(M_C(name, _t) mem) \
{ \
const unsigned max_thread = mem->gc_mem->max_thread; \
for(unsigned i = 0; i < max_thread;i++) { \
M_C(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
} \
M_MEMORY_FREE(mem->thread_data); \
mem->thread_data = NULL; \
M_C(name, _lflist_clear)(mem->empty); \
M_C(name, _lflist_clear)(mem->free); \
assert(M_C(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
M_C(name, _lflist_clear)(mem->to_be_reclaimed); \
/* TODO: Unregister from the GC? */ \
} \
\
static inline type_t * \
M_C(name, _new)(M_C(name, _t) mem, m_gc_tid_t id) \
{ \
M_C(name, _slist_node_t) *snode; \
M_C(name, _lf_node_t) *node; \
while (true) { \
/* Fast & likely path where we access the thread pool of nodes */ \
if (M_LIKELY(!M_C(name, _slist_empty_p)(mem->thread_data[id].free))) { \
snode = M_C(name, _slist_pop)(mem->thread_data[id].free); \
return &snode->data; \
} \
/* Request a group node to the freelist of groups */ \
node = M_C(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \
if (M_UNLIKELY (node == NULL)) { \
/* Request a new group to the system. Non Lock Free path */ \
assert(mem->initial > 0); \
node = M_C(name, _alloc_node)(mem->initial); \
assert(node != NULL); \
assert(!M_C(name, _slist_empty_p)(node->list)); \
} \
M_C(name, _slist_move)(mem->thread_data[id].free, node->list); \
/* Push back the empty group */ \
assert (M_C(name, _slist_empty_p)(node->list)); \
M_C(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_C(name, _del)(M_C(name, _t) mem, type_t *d, m_gc_tid_t id) \
{ \
M_C(name, _slist_node_t) *snode; \
assert( d != NULL); \
snode = M_TYPE_FROM_FIELD(M_C(name, _slist_node_t), d, type_t, data); \
M_C(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
} \
/***********************************************************************/
/* Define the ID of a thread */
typedef int m_gc_tid_t;
/* Define the age of a node */
/* TODO: Compute if sufficient (worst cast ULONG_MAX is 32 bits) */
typedef unsigned long m_gc_ticket_t;
typedef atomic_ulong m_gc_atomic_ticket_t;
/* Define the Linked List of mempools that are registered in the GC */
struct m_gc_s;
typedef struct m_gc_mempool_list_s {
struct m_gc_mempool_list_s *next;
void (*gc_on_sleep)(struct m_gc_s *gc_mem,
struct m_gc_mempool_list_s *data, m_gc_tid_t id,
m_gc_ticket_t ticket, m_gc_ticket_t min_ticket);
void *data;
} m_gc_mempool_list_t;
/* Define the Garbage collector thread data */
typedef struct m_gc_lfmp_thread_s {
m_gc_atomic_ticket_t ticket;
m_backoff_t bkoff;
M_CACHELINE_ALIGN(align1, atomic_ulong, m_backoff_t);
} m_gc_lfmp_thread_t;
/* Define the Garbage collector coordinator */
typedef struct m_gc_s {
m_gc_atomic_ticket_t ticket;
m_gc_tid_t max_thread;
genint_t thread_alloc;
m_gc_lfmp_thread_t *thread_data;
m_gc_mempool_list_t *mempool_list;
} m_gc_t[1];
static inline void
m_gc_init(m_gc_t gc_mem, unsigned long max_thread)
{
assert(gc_mem != NULL);
assert(max_thread > 0 && max_thread < INT_MAX);
atomic_init(&gc_mem->ticket, 0UL);
genint_init(gc_mem->thread_alloc, max_thread);
gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_t, NULL, max_thread);
if (gc_mem->thread_data == NULL) {
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_t));
return;
}
for(unsigned i = 0; i < max_thread;i++) {
atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX);
m_backoff_init(gc_mem->thread_data[i].bkoff);
}
gc_mem->max_thread = max_thread;
gc_mem->mempool_list = NULL;
}
static inline void
m_gc_clear(m_gc_t gc_mem)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) {
m_backoff_clear(gc_mem->thread_data[i].bkoff);
}
M_MEMORY_FREE(gc_mem->thread_data);
gc_mem->thread_data = NULL;
genint_clear(gc_mem->thread_alloc);
}
static inline m_gc_tid_t
m_gc_attach_thread(m_gc_t gc_mem)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
unsigned id = genint_pop(gc_mem->thread_alloc);
return M_ASSIGN_CAST(m_gc_tid_t, id);
}
static inline void
m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
assert(id >= 0 && id < gc_mem->max_thread);
assert(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
genint_push(gc_mem->thread_alloc, id);
}
static inline void
m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
{
assert(gc_mem != NULL && gc_mem->max_thread > 0);
assert(id >= 0 && id < gc_mem->max_thread);
assert(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
m_gc_ticket_t t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1;
atomic_store(&gc_mem->thread_data[id].ticket, t);
}
static inline m_gc_ticket_t
m_gc_int_min_ticket(m_gc_t gc_mem)
{
m_gc_ticket_t min = atomic_load(&gc_mem->thread_data[0].ticket);
for(m_gc_tid_t i = 1; i < gc_mem->max_thread; i++) {
m_gc_ticket_t t = atomic_load(&gc_mem->thread_data[i].ticket);
min = M_MIN(t, min);
}
return min;
}
static inline void
m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id)
{
/* Increase life time of the thread */
m_gc_ticket_t t = atomic_fetch_add(&gc_mem->ticket, 1UL);
atomic_store(&gc_mem->thread_data[id].ticket, t+1);
const m_gc_ticket_t min_ticket = m_gc_int_min_ticket(gc_mem);
/* Iterate over all registered mempools */
m_gc_mempool_list_t *it = gc_mem->mempool_list;
while (it) {
/* Perform a garbage collect of the mempool */
it->gc_on_sleep(gc_mem, it, id, t, min_ticket);
/* Next mempool to scan for GC */
it = it->next;
}
/* Sleep the thread */
atomic_store(&gc_mem->thread_data[id].ticket, ULONG_MAX);
}
/***********************************************************************/
/* */
/* VLA MEMPOOL */
/* */
/***********************************************************************/
C_MEMPOOL_DEF_SINGLY_LIST(m_vlapool, char)
C_MEMPOOL_DEF_LF_QUEUE(m_vlapool, char)
C_MEMPOOL_DEF_SYSTEM_ALLOC(m_vlapool, char)
typedef struct m_vlapool_lfmp_thread_s {
m_vlapool_slist_t to_be_reclaimed;
M_CACHELINE_ALIGN(align1, m_vlapool_slist_t);
} m_vlapool_lfmp_thread_t;
static inline void
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_t *t)
{
m_vlapool_slist_init(t->to_be_reclaimed);
}
static inline void
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_t *t)
{
assert(m_vlapool_slist_empty_p(t->to_be_reclaimed));
m_vlapool_slist_clear(t->to_be_reclaimed);
}
typedef struct m_vlapool_s {
m_vlapool_lflist_t to_be_reclaimed;
m_vlapool_lflist_t empty;
m_vlapool_lfmp_thread_t *thread_data;
m_gc_mempool_list_t mvla_node;
struct m_gc_s *gc_mem;
} m_vlapool_t[1];
/* Garbage collect of the nodes of the vla mempool on sleep */
static inline void
m_vlapool_int_gc_on_sleep(m_gc_t gc_mem, m_gc_mempool_list_t *data,
m_gc_tid_t id, m_gc_ticket_t ticket, m_gc_ticket_t min_ticket)
{
/* Get back the mempool from the node */
struct m_vlapool_s *vlapool =
M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_gc_mempool_list_t, mvla_node);
/* Move the local nodes of the vlapool to be reclaimed to the thread into the global pool */
if (!m_vlapool_slist_empty_p(vlapool->thread_data[id].to_be_reclaimed)) {
m_vlapool_lf_node_t *node;
/* Get a new empty group of nodes */
node = m_vlapool_lflist_pop(vlapool->empty, gc_mem->thread_data[id].bkoff);
if (M_UNLIKELY (node == NULL)) {
/* Fail to get an empty group of node.
Alloc a new one from the system */
node = m_vlapool_alloc_node(0);
assert(node != NULL);
}
assert(m_vlapool_slist_empty_p(node->list));
m_vlapool_slist_move(node->list, vlapool->thread_data[id].to_be_reclaimed);
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed);
m_vlapool_lflist_push(vlapool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff);
}
/* Perform a GC of the freelist of nodes */
while (true) {
m_vlapool_lf_node_t *node;
node = m_vlapool_lflist_pop_if(vlapool->to_be_reclaimed,
min_ticket, gc_mem->thread_data[id].bkoff);
if (node == NULL) break;
// No reuse of VLA nodes. Free physically the node back to the system
m_vlapool_slist_clear(node->list);
// Add back the empty group of nodes
m_vlapool_slist_init(node->list);
m_vlapool_lflist_push(vlapool->empty, node, gc_mem->thread_data[id].bkoff);
}
}
static inline void
m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem)
{
const unsigned long max_thread = gc_mem->max_thread;
/* Initialize the thread data of the vlapool */
mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_t, NULL, max_thread);
if (mem->thread_data == NULL) {
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_t));
return;
}
for(unsigned i = 0; i < max_thread;i++) {
m_vlapool_lfmp_thread_init(&mem->thread_data[i]);
}
/* Initialize the lists */
m_vlapool_lflist_init(mem->to_be_reclaimed, m_vlapool_alloc_node(0));
m_vlapool_lflist_init(mem->empty, m_vlapool_alloc_node(0));
/* Register the mempool in the GC */
mem->mvla_node.gc_on_sleep = m_vlapool_int_gc_on_sleep;
mem->mvla_node.next = gc_mem->mempool_list;
gc_mem->mempool_list = &mem->mvla_node;
mem->gc_mem = gc_mem;
}
static inline void
m_vlapool_clear(m_vlapool_t mem)
{
const unsigned max_thread = mem->gc_mem->max_thread;
for(unsigned i = 0; i < max_thread;i++) {
m_vlapool_lfmp_thread_clear(&mem->thread_data[i]);
}
M_MEMORY_FREE(mem->thread_data);
mem->thread_data = NULL;
m_vlapool_lflist_clear(mem->empty);
assert(m_vlapool_lflist_empty_p(mem->to_be_reclaimed));
m_vlapool_lflist_clear(mem->to_be_reclaimed);
/* TODO: Unregister from the GC? */
}
static inline void *
m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size)
{
assert(mem != NULL && mem->gc_mem != NULL);
assert(id >= 0 && id < mem->gc_mem->max_thread);
assert( atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
// Nothing to do with theses parameters yet
(void) mem;
(void) id;
// Ensure the size is big enough to also represent a node
size += offsetof(struct m_vlapool_slist_node_s, data);
// Simply wrap around a system call to get the memory
char *ptr = M_MEMORY_REALLOC(char, NULL, size);
return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data));
}
static inline void
m_vlapool_del(m_vlapool_t mem, void *d, m_gc_tid_t id)
{
assert(mem != NULL && mem->gc_mem != NULL);
assert(id >= 0 && id < mem->gc_mem->max_thread);
assert(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
assert(d != NULL);
// Get back the pointer to a struct m_vlapool_slist_node_s.
d = M_ASSIGN_CAST(void *, M_ASSIGN_CAST(char *, d) - offsetof(struct m_vlapool_slist_node_s, data));
m_vlapool_slist_node_t *snode = M_ASSIGN_CAST(m_vlapool_slist_node_t *, d);
// Push the logicaly free memory into the list of the nodes to be reclaimed.
m_vlapool_slist_push(mem->thread_data[id].to_be_reclaimed, snode);
}
#endif
+375 -102
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Basic Protected Concurrent module over container.
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -27,35 +27,47 @@
#include "m-core.h"
#include "m-mutex.h"
#include "m-atomic.h"
/* Define a protected concurrent container and its associated functions.
/* Define a protected concurrent container and its associated functions
based on the given container.
USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */
#define CONCURRENT_DEF(name, ...) \
CONCURRENTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
#define CONCURRENT_DEF(name, ...) \
CONCURRENTI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t) ), \
(name, __VA_ARGS__, M_C(name,_t) )))
/* Define the oplist of a protected concurrent container given its name and its oplist.
USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */
#define CONCURRENT_OPLIST(...) \
CONCURRENTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
CONCURRENTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/* Define a protected concurrent container and its associated functions
based on its given container. Operations that perform only read of the container
can be done in parallel.
USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */
#define CONCURRENT_RP_DEF(name, ...) \
CONCURRENTI_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t) ), \
(name, __VA_ARGS__, M_C(name,_t) )))
/********************************** INTERNAL ************************************/
// Deferred evaluation for the oplist definition.
#define CONCURRENTI_OPLIST(arg) CONCURRENTI_OPLIST2 arg
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define CONCURRENTI_OPLIST_P1(arg) CONCURRENTI_OPLIST_P2 arg
/* OPLIST definition
GET_KEY is not present as its interface is not compatible with a concurrent
container (_get returns a pointer to an internal data, data that may be
destroyed by another thread).
*/
#define CONCURRENTI_OPLIST2(name, oplist) \
#define CONCURRENTI_OPLIST_P2(name, oplist) \
(M_IF_METHOD(INIT, oplist)(INIT(M_C(name, _init)),) \
,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_C(name, _init_set)),) \
,M_IF_METHOD(SET, oplist)(SET(M_C(name, _set)),) \
@@ -67,6 +79,7 @@
,SUBTYPE(M_C(name, _type_t)) \
,OPLIST(oplist) \
,M_IF_METHOD(TEST_EMPTY, oplist)(TEST_EMPTY(M_C(name,_empty_p)),) \
,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_C(name,_size)),) \
,M_IF_METHOD(CLEAN, oplist)(CLEAN(M_C(name,_clean)),) \
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \
@@ -82,6 +95,8 @@
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
@@ -89,15 +104,24 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* Internal contract
Nothing notable. Can't check too much without locking the structure itself
*/
#define CONCURRENTI_CONTRACT(c) do { \
assert ((c)->self == c); \
assert ((c) != NULL); \
} while (0)
// Deferred evaluation for the concurrent definition.
#define CONCURRENTI_DEF(arg) CONCURRENTI_DEF2 arg
/* Deferred evaluation for the concurrent definition,
so that all arguments are evaluated before further expansion */
#define CONCURRENTI_DEF_P1(arg) CONCURRENTI_DEF_P2 arg
// Internal definition.
#define CONCURRENTI_DEF2(name, type, oplist, concurrent_t, concurrent_it_t) \
/* Internal concurrent definition
- name: prefix to be used
- type: type of the sub container
- oplist: oplist of the type of the sub container
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
*/
#define CONCURRENTI_DEF_P2(name, type, oplist, concurrent_t) \
\
typedef struct M_C(name, _s) { \
struct M_C(name, _s) *self; \
@@ -111,14 +135,85 @@
\
typedef type M_C(name, _type_t); \
\
\
M_IF_METHOD(INIT, oplist)( \
/* Define the lock strategy (global & shared lock) */ \
static inline void \
M_C(name, _init)(concurrent_t out) \
M_C(name, _internal_init)(concurrent_t out) \
{ \
m_mutex_init(out->lock); \
m_cond_init(out->there_is_data); \
out->self = out; \
} \
\
static inline void \
M_C(name, _internal_clear)(concurrent_t out) \
{ \
assert (out->self == out); \
m_mutex_clear(out->lock); \
m_cond_clear(out->there_is_data); \
out->self = NULL; \
} \
\
static inline void \
M_C(name, _read_lock)(const concurrent_t out) \
{ \
assert (out->self == out); \
m_mutex_lock (out->self->lock); \
} \
\
static inline void \
M_C(name, _read_unlock)(const concurrent_t out) \
{ \
assert (out->self == out); \
m_mutex_unlock (out->self->lock); \
} \
\
static inline void \
M_C(name, _read_wait)(const concurrent_t out) \
{ \
assert (out->self == out); \
m_cond_wait(out->self->there_is_data, out->self->lock); \
} \
\
static inline void \
M_C(name, _write_lock)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
} \
\
static inline void \
M_C(name, _write_unlock)(concurrent_t out) \
{ \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _write_wait)(const concurrent_t out) \
{ \
m_cond_wait(out->self->there_is_data, out->self->lock); \
} \
\
static inline void \
M_C(name, _write_signal)(concurrent_t out) \
{ \
m_cond_broadcast(out->there_is_data); \
} \
\
CONCURRENTI_DEF_FUNC_P3(name, type, oplist, concurrent_t)
/* Internal definition of the functions commons to concurrent and rp-concurrent
- name: prefix to be used
- type: type of the sub container
- oplist: oplist of the type of the sub container
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
*/
#define CONCURRENTI_DEF_FUNC_P3(name, type, oplist, concurrent_t) \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _init)(concurrent_t out) \
{ \
M_C(name, _internal_init)(out); \
M_CALL_INIT(oplist, out->data); \
CONCURRENTI_CONTRACT(out); \
} \
@@ -130,12 +225,10 @@
{ \
CONCURRENTI_CONTRACT(src); \
assert (out != src); \
m_mutex_init(out->lock); \
m_cond_init(out->there_is_data); \
out->self = out; \
m_mutex_lock (src->self->lock); \
M_C(name, _internal_init)(out); \
M_C(name, _read_lock)(src); \
M_CALL_INIT_SET(oplist, out->data, src->data); \
m_mutex_unlock(src->self->lock); \
M_C(name, _read_unlock)(src); \
CONCURRENTI_CONTRACT(out); \
} \
,) \
@@ -147,19 +240,19 @@
CONCURRENTI_CONTRACT(out); \
if (out == src) return; \
if (out < src) { \
m_mutex_lock (out->lock); \
m_mutex_lock (src->self->lock); \
M_C(name, _write_lock)(out); \
M_C(name, _read_lock)(src); \
} else { \
m_mutex_lock (src->self->lock); \
m_mutex_lock (out->lock); \
M_C(name, _read_lock)(src); \
M_C(name, _write_lock)(out); \
} \
M_CALL_SET(oplist, out->data, src->data); \
if (out < src) { \
m_mutex_unlock (src->self->lock); \
m_mutex_unlock (out->lock); \
M_C(name, _read_lock)(src); \
M_C(name, _write_unlock)(out); \
} else { \
m_mutex_unlock (out->lock); \
m_mutex_unlock (src->self->lock); \
M_C(name, _write_unlock)(out); \
M_C(name, _read_unlock)(src); \
} \
CONCURRENTI_CONTRACT(out); \
} \
@@ -172,8 +265,7 @@
CONCURRENTI_CONTRACT(out); \
/* No need to lock */ \
M_CALL_CLEAR(oplist, out->data); \
m_mutex_clear (out->lock); \
m_cond_clear(out->there_is_data); \
M_C(name, _internal_clear)(out); \
} \
,) \
\
@@ -184,12 +276,9 @@
CONCURRENTI_CONTRACT(src); \
assert (out != src); \
/* No need to lock 'src' ? */ \
m_mutex_init (out->lock); \
m_cond_init (out->there_is_data); \
out->self = out; \
M_C(name, _internal_init)(out); \
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
m_mutex_clear (src->lock); \
m_cond_clear (src->there_is_data); \
M_C(name, _internal_clear)(src); \
CONCURRENTI_CONTRACT(out); \
} \
,) \
@@ -201,11 +290,10 @@
CONCURRENTI_CONTRACT(out); \
CONCURRENTI_CONTRACT(src); \
/* No need to lock 'src' ? */ \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_MOVE(oplist, out->data, src->data); \
m_mutex_unlock (out->lock); \
m_mutex_clear (src->lock); \
m_cond_clear (src->there_is_data); \
M_C(name, _write_unlock)(out); \
M_C(name, _internal_clear)(src); \
CONCURRENTI_CONTRACT(out); \
} \
,) \
@@ -217,19 +305,19 @@
CONCURRENTI_CONTRACT(out); \
CONCURRENTI_CONTRACT(src); \
if (out < src) { \
m_mutex_lock (out->lock); \
m_mutex_lock (src->lock); \
M_C(name, _write_lock)(out); \
M_C(name, _write_lock)(src); \
} else { \
m_mutex_lock (src->lock); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(src); \
M_C(name, _write_lock)(out); \
} \
M_CALL_SWAP(oplist, out->data, src->data); \
if (out < src) { \
m_mutex_unlock (src->lock); \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(src); \
M_C(name, _write_unlock)(out); \
} else { \
m_mutex_unlock (out->lock); \
m_mutex_unlock (src->lock); \
M_C(name, _write_unlock)(out); \
M_C(name, _write_unlock)(src); \
} \
} \
,) \
@@ -239,9 +327,9 @@
M_C(name, _clean)(concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_CLEAN(oplist, out->data); \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
@@ -250,11 +338,23 @@
M_C(name, _empty_p)(concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->self->lock); \
M_C(name, _read_lock)(out); \
bool b = M_CALL_TEST_EMPTY(oplist, out->data); \
m_mutex_unlock (out->self->lock); \
M_C(name, _read_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(GET_SIZE, oplist)( \
static inline size_t \
M_C(name, _size)(concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _read_lock)(out); \
size_t r = M_CALL_GET_SIZE(oplist, out->data); \
M_C(name, _read_unlock)(out); \
return r; \
} \
,) \
\
M_IF_METHOD(SET_KEY, oplist)( \
@@ -262,25 +362,25 @@
M_C(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_SET_KEY(oplist, out->data, key, data); \
m_cond_broadcast(out->there_is_data); \
m_mutex_unlock (out->lock); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(GET_KEY, oplist)( \
static inline bool \
M_C(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
M_C(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
CONCURRENTI_CONTRACT(out); \
assert (out_data != NULL); \
m_mutex_lock (out->lock); \
M_C(name, _read_lock)(out); \
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
if (p != NULL) { \
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
} \
m_mutex_unlock (out->lock); \
M_C(name, _read_unlock)(out); \
return p != NULL; \
} \
,) \
@@ -291,11 +391,11 @@
{ \
CONCURRENTI_CONTRACT(out); \
assert (out_data != NULL); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_SET_KEY(oplist, out->data, key); \
assert (p != NULL); \
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
@@ -304,9 +404,9 @@
M_C(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
return b; \
} \
,) \
@@ -316,10 +416,10 @@
M_C(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_PUSH(oplist, out->data, data); \
m_cond_broadcast(out->there_is_data); \
m_mutex_unlock (out->lock); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
@@ -328,9 +428,9 @@
M_C(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_POP(oplist, p, out->data); \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
@@ -339,10 +439,10 @@
M_C(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_PUSH_MOVE(oplist, out->data, data); \
m_cond_broadcast(out->there_is_data); \
m_mutex_unlock (out->lock); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
@@ -351,9 +451,9 @@
M_C(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
M_CALL_POP_MOVE(oplist, p, out->data); \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
@@ -362,9 +462,9 @@
M_C(name, _get_str)(string_t str, concurrent_t const out, bool a) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->self->lock); \
M_C(name, _read_lock)(out); \
M_CALL_GET_STR(oplist, str, out->data, a); \
m_mutex_unlock (out->self->lock); \
M_C(name, _read_unlock)(out); \
} \
,) \
\
@@ -373,9 +473,9 @@
M_C(name, _out_str)(FILE *f, concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->self->lock); \
M_C(name, _read_lock)(out); \
M_CALL_OUT_STR(oplist, f, out->data); \
m_mutex_unlock (out->self->lock); \
M_C(name, _read_unlock)(out); \
} \
,) \
\
@@ -384,9 +484,10 @@
M_C(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
m_mutex_unlock (out->lock); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
return b; \
} \
,) \
@@ -396,11 +497,37 @@
M_C(name, _in_str)(concurrent_t out, FILE *f) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
bool b = M_CALL_IN_STR(oplist, out->data, f); \
m_mutex_unlock (out->lock); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _read_lock)(out); \
m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \
M_C(name, _read_unlock)(out); \
return r; \
} \
,) \
\
M_IF_METHOD(IN_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(concurrent_t out, m_serial_read_t f) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
return r; \
} \
,) \
\
M_IF_METHOD(EQUAL, oplist)( \
@@ -411,19 +538,19 @@
CONCURRENTI_CONTRACT(out2); \
if (out1 == out2) return true; \
if (out1 < out2) { \
m_mutex_lock (out1->self->lock); \
m_mutex_lock (out2->self->lock); \
M_C(name, _read_lock)(out1); \
M_C(name, _read_lock)(out2); \
} else { \
m_mutex_lock (out2->self->lock); \
m_mutex_lock (out1->self->lock); \
M_C(name, _read_lock)(out2); \
M_C(name, _read_lock)(out1); \
} \
bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \
if (out1 < out2) { \
m_mutex_unlock (out2->self->lock); \
m_mutex_unlock (out1->self->lock); \
M_C(name, _read_unlock)(out2); \
M_C(name, _read_unlock)(out1); \
} else { \
m_mutex_unlock (out1->self->lock); \
m_mutex_unlock (out2->self->lock); \
M_C(name, _read_unlock)(out1); \
M_C(name, _read_unlock)(out2); \
} \
return b; \
} \
@@ -431,12 +558,12 @@
\
M_IF_METHOD(GET_KEY, oplist)( \
static inline bool \
M_C(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
M_C(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
{ \
CONCURRENTI_CONTRACT(out); \
assert (out_data != NULL); \
bool ret = false; \
m_mutex_lock (out->lock); \
M_C(name, _read_lock)(out); \
while (true) { \
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
if (p != NULL) { \
@@ -445,9 +572,9 @@
break; \
} \
if (blocking == false) break; \
m_cond_wait(out->there_is_data, out->lock); \
M_C(name, _read_wait)(out); \
} \
m_mutex_unlock (out->lock); \
M_C(name, _read_unlock)(out); \
return ret; \
} \
,) \
@@ -459,7 +586,7 @@
CONCURRENTI_CONTRACT(out); \
assert (p != NULL); \
bool ret = false; \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
while (true) { \
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
M_CALL_POP(oplist, p, out->data); \
@@ -467,9 +594,9 @@
break; \
} \
if (blocking == false) break; \
m_cond_wait(out->there_is_data, out->lock); \
M_C(name, _write_wait)(out); \
} \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
return ret; \
} \
,) \
@@ -481,7 +608,7 @@
CONCURRENTI_CONTRACT(out); \
assert (p != NULL); \
bool ret = false; \
m_mutex_lock (out->lock); \
M_C(name, _write_lock)(out); \
while (true) { \
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
M_CALL_POP_MOVE(oplist, p, out->data); \
@@ -489,9 +616,9 @@
break; \
} \
if (blocking == false) break; \
m_cond_wait(out->there_is_data, out->lock); \
M_C(name, _write_wait)(out); \
} \
m_mutex_unlock (out->lock); \
M_C(name, _write_unlock)(out); \
return ret; \
} \
,) \
@@ -501,11 +628,157 @@
M_C(name, _hash)(concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
m_mutex_lock (out->self->lock); \
M_C(name, _read_lock)(out); \
size_t h = M_CALL_HASH(oplist, out->data); \
m_mutex_unlock (out->self->lock); \
M_C(name, _read_unlock)(out); \
return h; \
} \
,) \
/* Deferred evaluation for the RP concurrent definition,
so that all arguments are evaluated before further expansion */
#define CONCURRENTI_RP_DEF_P1(arg) CONCURRENTI_RP_DEF_P2 arg
/* Internal RP concurrent definition
- name: prefix to be used
- type: type of the sub container
- oplist: oplist of the type of the sub container
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
*/
#define CONCURRENTI_RP_DEF_P2(name, type, oplist, concurrent_t) \
\
typedef struct M_C(name, _s) { \
struct M_C(name, _s) *self; \
m_mutex_t lock; \
m_cond_t rw_done; \
size_t read_count; \
bool writer_waiting; \
m_cond_t there_is_data; /* condition raised when there is data */ \
type data; \
} concurrent_t[1]; \
\
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
\
typedef type M_C(name, _type_t); \
\
/* Define the lock strategy (multi lock) */ \
static inline void \
M_C(name, _internal_init)(concurrent_t out) \
{ \
m_mutex_init(out->lock); \
m_cond_init(out->rw_done); \
m_cond_init(out->there_is_data); \
out->self = out; \
out->read_count = 0; \
out->writer_waiting = false; \
} \
\
static inline void \
M_C(name, _internal_clear)(concurrent_t out) \
{ \
assert (out->self == out); \
m_mutex_clear(out->lock); \
m_cond_clear(out->rw_done); \
m_cond_clear(out->there_is_data); \
out->self = NULL; \
} \
\
static inline void \
M_C(name, _read_lock)(const concurrent_t out) \
{ \
struct M_C(name, _s) *self = out->self; \
assert (self == out); \
m_mutex_lock (self->lock); \
while (self->writer_waiting == true) { \
m_cond_wait(self->rw_done, self->lock); \
} \
self->read_count ++; \
m_mutex_unlock (self->lock); \
} \
\
static inline void \
M_C(name, _read_unlock)(const concurrent_t out) \
{ \
struct M_C(name, _s) *self = out->self; \
assert (self == out); \
m_mutex_lock (self->lock); \
self->read_count --; \
if (self->read_count == 0) { \
m_cond_broadcast (self->rw_done); \
} \
m_mutex_unlock (self->lock); \
} \
\
static inline void \
M_C(name, _write_lock)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
while (out->writer_waiting == true) { \
m_cond_wait(out->rw_done, out->lock); \
} \
out->writer_waiting = true; \
while (out->read_count > 0) { \
m_cond_wait(out->rw_done, out->lock); \
} \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _write_unlock)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
out->writer_waiting = false; \
m_cond_broadcast (out->rw_done); \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _read_wait)(const concurrent_t out) \
{ \
struct M_C(name, _s) *self = out->self; \
assert (self == out); \
m_mutex_lock (out->self->lock); \
self->read_count --; \
if (self->read_count == 0) { \
m_cond_broadcast (self->rw_done); \
} \
m_cond_wait(self->there_is_data, self->lock); \
while (self->writer_waiting == true) { \
m_cond_wait(self->rw_done, self->lock); \
} \
self->read_count ++; \
m_mutex_unlock (out->self->lock); \
} \
\
static inline void \
M_C(name, _write_wait)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
out->writer_waiting = false; \
m_cond_broadcast (out->rw_done); \
m_cond_wait(out->there_is_data, out->lock); \
while (out->writer_waiting == true) { \
m_cond_wait(out->rw_done, out->lock); \
} \
out->writer_waiting = true; \
while (out->read_count > 0) { \
m_cond_wait(out->rw_done, out->lock); \
} \
m_mutex_unlock (out->lock); \
} \
\
static inline void \
M_C(name, _write_signal)(concurrent_t out) \
{ \
m_mutex_lock (out->lock); \
m_cond_broadcast(out->there_is_data); \
m_mutex_unlock (out->lock); \
} \
\
CONCURRENTI_DEF_FUNC_P3(name, type, oplist, concurrent_t)
#endif
+531 -90
View File
File diff suppressed because it is too large. Load diff
+108 -24
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - DEQUE module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -30,30 +30,26 @@
/* Define a deque of a given type and its associated functions.
USAGE: DEQUE_DEF(name, type [, oplist_of_the_type]) */
#define DEQUE_DEF(name, ...) \
DEQUEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t))))
DEQUEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t))))
/* Define the oplist of a deque of a type.
USAGE: DEQUE_OPLIST(name[, oplist of the type]) */
#define DEQUE_OPLIST(...) \
DEQUEI_OPLIST (M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
DEQUEI_OPLIST_P1 (M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
/* Default initial size of a bucket of items */
#ifndef DEQUEUI_DEFAULT_SIZE
#define DEQUEUI_DEFAULT_SIZE 8
/* C++ doesn't support flexible array within a structure.
Let's define at least one element. */
#ifdef __cplusplus
# define DEQUEI_MIN_ARRAY_SIZE 1
#else
# define DEQUEI_MIN_ARRAY_SIZE
#endif
/* Define the internal contract of a deque */
#define DEQUEI_CONTRACT(d) do { \
assert ((d) != NULL); \
assert ((d)->default_size >= DEQUEUI_DEFAULT_SIZE); \
@@ -67,29 +63,55 @@
(d)->back->index - (d)->front->index == (d)->count); \
} while (0)
#define DEQUEI_DEF(arg) DEQUEI_DEF2 arg
/* Deferred evaluation for the deque definition,
so that all arguments are evaluated before further expansion */
#define DEQUEI_DEF_P1(arg) DEQUEI_DEF_P2 arg
#define DEQUEI_DEF2(name, type, oplist, deque_t, it_t, node_t) \
/* Internal deque definition
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- deque_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define DEQUEI_DEF_P2(name, type, oplist, deque_t, it_t, node_t) \
\
/* It is a linked list of buckets of the types, \
each new created bucket size grows compared to the previous one \
resulting in: \
strict O(1) for push/pop \
O(ln(n)) for random access. \
No insert / delete operations are planned. \
[Could be done in O(n) complexity if needed] \
Define the bucket (aka node) structure. \
*/ \
typedef struct M_C(name, _node_s) { \
ILIST_INTERFACE(M_C(name, _node_list), struct M_C(name, _node_s)); \
size_t size; \
type data[DEQUEI_MIN_ARRAY_SIZE]; \
type data[M_MIN_FLEX_ARRAY_SIZE]; \
} node_t; \
\
/* Each node is allocated with a variable size (so we use \
M_GET_REALLOC for the allocation). But we need to delete the nodes \
\
/* Each node is allocated with a variable size bucket (so we use \
M_GET_REALLOC for the allocation). But we want to delete the nodes \
automatically with the intrusive list used for storing the nodes: \
so we register as a DEL operator the FREE operator of the oplist. \
The interfaces are compatible. \
*/ \
ILIST_DEF(M_C(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \
\
/* Define an internal iterator */ \
typedef struct M_C(name, _it2_s) { \
node_t *node; \
size_t index; \
} M_C(name, _it2_t)[1]; \
\
/* Define the deque type: \
- 'list' if the list of buckets containing the objects. \
- 'front' and 'back' are iterators to the first and last elements. \
- 'default_size' is the size used for the creation of a new bucket \
- 'count' is the number of elements in the container. \
*/ \
typedef struct M_C(name, _s) { \
M_C(name, _node_list_t) list; \
M_C(name, _it2_t) front; \
@@ -97,6 +119,7 @@
size_t default_size; \
size_t count; \
} deque_t[1]; \
\
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
\
@@ -112,19 +135,23 @@
M_C(name, _int_new_node)(deque_t d) \
{ \
size_t def = d->default_size; \
/* Test for overflow of the size computation */ \
if (M_UNLIKELY (def > SIZE_MAX / sizeof (type) - sizeof(M_C(name, _node_t)))) { \
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
return NULL; \
} \
/* Alloc a new node */ \
M_C(name, _node_t)*n = (M_C(name, _node_t)*) (void*) \
M_CALL_REALLOC(oplist, char, NULL, \
M_CALL_REALLOC(oplist, char, NULL, \
sizeof(M_C(name, _node_t)) + def * sizeof(type) ); \
if (n==NULL) { \
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
return NULL; \
} \
/* Initialize the node */ \
n->size = def; \
M_C(name, _node_list_init_field)(n); \
/* Increase the next bucket allocation */ \
/* Do not increase it too much if there are few items */ \
def = M_MIN(def, d->count); \
d->default_size = M_CALL_INC_ALLOC(oplist, def); \
@@ -192,8 +219,10 @@
M_C(name, _node_t) *n = d->back->node; \
size_t index = d->back->index; \
if (M_UNLIKELY (n->size <= index)) { \
/* try to get an already allocated node */ \
n = M_C(name, _node_list_next_obj)(d->list, n); \
if (n == NULL) { \
/* No node exists, allocate a new one */ \
n = M_C(name, _int_new_node)(d); \
if (M_UNLIKELY (n == NULL)) return NULL; \
M_C(name, _node_list_push_back)(d->list, n); \
@@ -859,11 +888,63 @@
return c == ']'; \
} \
, /* no IN_STR */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, const deque_t deque) \
{ \
DEQUEI_CONTRACT(deque); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
bool first_done = false; \
ret = f->m_interface->write_array_start(local, f, deque->count); \
M_C(name, _it_t) it; \
for (M_C(name, _it)(it, deque) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
type const *item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
first_done = true; \
} \
ret |= f->m_interface->write_array_end(local, f); \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(deque_t deque, m_serial_read_t f) \
{ \
DEQUEI_CONTRACT(deque); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
M_C(name,_clean)(deque); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
type item; \
M_CALL_INIT(oplist, item); \
do { \
ret = M_CALL_IN_SERIAL(oplist, item, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push_back)(deque, item); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(oplist, item); \
return ret; \
} \
, /* no IN_SERIAL & INIT */ ) \
\
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define DEQUEI_OPLIST_P1(arg) DEQUEI_OPLIST_P2 arg
#define DEQUEI_OPLIST(arg) DEQUEI_OPLIST2 arg
#define DEQUEI_OPLIST2(name, oplist) \
/* OPLIST definition of a deque */
#define DEQUEI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
@@ -888,6 +969,7 @@
,IT_REF(M_C(name,_ref)) \
,IT_CREF(M_C(name,_cref)) \
,CLEAN(M_C(name,_clean)) \
,GET_SIZE(M_C(name, _size)) \
,PUSH(M_C(name,_push_back)) \
,POP(M_C(name,_pop_back)) \
,OPLIST(oplist) \
@@ -895,6 +977,8 @@
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
+293 -148
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - DICTIONARY Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -29,7 +29,8 @@
#include "m-array.h"
#include "m-tuple.h"
/* Define a dictionary with the key key_type to the value value_type and its associated functions.
/* Define a dictionary associating the key key_type to the value value_type and its associated functions.
USAGE:
DICT_DEF2(name, key_type, key_oplist, value_type, value_oplist)
OR
@@ -41,7 +42,7 @@
(name, key_type, __VA_ARGS__)))
/* Define a dictionary with the key key_type to the value value_type and its associated functions.
/* Define a dictionary asssociating the key key_type to the value value_type and its associated functions.
It stores the computed hash value, avoiding the need of recomputing it but increasing memory
consumption.
USAGE:
@@ -55,19 +56,9 @@
(name, key_type, __VA_ARGS__)))
/* Define a set with the key key_type and its associated functions.
The set is unordered.
USAGE: DICT_SET_DEF(name, key_type[, key_oplist])
*/
#define DICT_SET_DEF(name, ...) \
DICTI_SET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), \
(name, __VA_ARGS__)))
/* Define a dictionary with the key key_type to the value value_type
with an Open Addressing implementation and its associated functions.
KEY_OPLIST needs the operators OOR_EQUAL & OOR_SET.
/* Define a dictionary associating the key key_type to the value value_type
with an Open Addressing implementation and its associated functions.
KEY_OPLIST needs the operators OOR_EQUAL & OOR_SET.
USAGE:
DICT_OA_DEF2(name, key_type, key_oplist, value_type, value_oplist)
OR
@@ -79,6 +70,27 @@
(name, key_type, __VA_ARGS__)))
/* Define a set of the key key_type and its associated functions.
The set is unordered.
USAGE: DICT_SET_DEF(name, key_type[, key_oplist])
*/
#define DICT_SET_DEF(name, ...) \
DICTI_SET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), \
(name, __VA_ARGS__)))
/* Define a set of the key key_type
with an Open Addressing implementation and its associated functions.
The set is unordered.
USAGE: DICT_OASET_DEF(name, key_type[, key_oplist])
*/
#define DICT_OASET_DEF(name, ...) \
DICTI_OASET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), \
(name, __VA_ARGS__)))
/* Define the oplist of a dictionnary (DICT_DEF2, DICT_STOREHASH_DEF2 or DICT_OA_DEF2).
USAGE:
DICT_OPLIST(name, oplist of the key type, oplist of the value type)
@@ -87,16 +99,16 @@
*/
#define DICT_OPLIST(...) \
M_IF_NARGS_EQ1(__VA_ARGS__) \
(DICTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST ), \
DICTI_OPLIST(__VA_ARGS__ ))
(DICTI_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST )), \
DICTI_OPLIST_P1((__VA_ARGS__ )))
/* Define the oplist of a dictionnary (DICT_SET_DEF).
USAGE: DICT_SET_OPLIST(name[, oplist of the key type]) */
#define DICT_SET_OPLIST(...) \
M_IF_NARGS_EQ1(__VA_ARGS__) \
(DICTI_SET_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST), \
DICTI_SET_OPLIST(__VA_ARGS__ ))
(DICTI_SET_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST)), \
DICTI_SET_OPLIST_P1((__VA_ARGS__ )))
@@ -178,14 +190,15 @@
LIST_OPLIST(M_C(name, _list_pair), TUPLE_OPLIST(M_C(name, _pair), key_oplist))) \
\
DICTI_FUNC_DEF2(name, key_type, key_oplist, key_type, \
(INIT(M_EMPTY_DEFAULT), INIT_SET(M_EMPTY_DEFAULT), SET(M_EMPTY_DEFAULT), CLEAR(M_EMPTY_DEFAULT), \
EQUAL(M_EMPTY_DEFAULT), GET_STR(M_EMPTY_DEFAULT), OUT_STR(M_EMPTY_DEFAULT), IN_STR(M_TRUE_DEFAULT), PARSE_STR(M_TRUE_DEFAULT)), \
1, 0, M_C(name, _t), M_C(name, _it_t))
M_EMPTY_OPLIST, 1, 0, M_C(name, _t), M_C(name, _it_t))
/* Define the structure and a chained dictionnary */
/* Define the structure of a chained dictionnary */
#define DICTI_FUNC_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, isStoreHash, dict_t, dict_it_t) \
\
/* NOTE: \
if isSet is true, all methods of value_oplist are NOP methods */ \
\
typedef struct M_C(name, _s) { \
size_t used, lower_limit, upper_limit; \
M_C(name, _array_list_pair_t) table; \
@@ -519,6 +532,13 @@
M_C(name, _list_pair_it_set)(it->list_it, ref->list_it); \
} \
\
static inline void \
M_C(name, _it_end)(dict_it_t it, const dict_t d) \
{ \
DICTI_CONTRACT(name, d); \
M_C(name, _array_list_pair_it_end)(it->array_it, d->table); \
} \
\
static inline bool \
M_C(name, _end_p)(const dict_it_t it) \
{ \
@@ -556,7 +576,7 @@
{ \
assert (it1 != NULL && it2 != NULL); \
return M_C(name, _list_pair_it_equal_p)(it1->list_it, \
it2->list_it); \
it2->list_it); \
} \
\
static inline M_C(name, _type_t) * \
@@ -590,7 +610,7 @@
assert (dict1 != NULL && dict2 != NULL); \
/* NOTE: Key type has mandatory equal operator */ \
/* First the easy cases */ \
if (M_LIKELY (dict1->used != dict2-> used)) \
if (M_LIKELY (dict1->used != dict2->used)) \
return false; \
if (M_UNLIKELY (dict1->used == 0)) \
return true; \
@@ -603,34 +623,23 @@
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)) { \
const M_C(name, _type_t) *item = M_C(name, _cref)(it); \
M_IF(isSet)( \
value_type *ptr = M_C(name, _get)(dict2, *item); \
if (ptr == NULL) \
return false; \
, \
value_type *ptr = M_C(name, _get)(dict2, item->key); \
if (ptr == NULL) \
return false; \
if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \
return false; \
) \
} \
value_type *ptr = M_C(name, _get)(dict2, M_IF(isSet)(*item, item->key)); \
if (ptr == NULL) \
return false; \
if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \
return false; \
} \
return true; \
} \
, /* no value equal */ ) \
\
DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t)
/* Define additional functions for dictionnary.
/* Define additional functions for dictionnary (Common for all kinds of dictionnary).
Do not used any fields of the dictionnary but the public API */
#define DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t) \
\
static inline bool M_ATTR_DEPRECATED \
M_C(name, _remove)(dict_t map, key_type const key) \
{ \
return M_C(name, _erase)(map, key); \
} \
\
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, const dict_t dict, const bool append) \
@@ -704,7 +713,7 @@
M_CALL_INIT(key_oplist, key); \
M_CALL_INIT(value_oplist, value); \
do { \
while (isspace(*str)) str++; \
while (isspace((int) *str)) str++; \
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
M_IF(isSet)( \
if (b == false) { break; } \
@@ -740,9 +749,11 @@
if (M_UNLIKELY (c == EOF)) return true; \
ungetc(c, file); \
key_type key; \
M_IF(isSet)( ,value_type value); \
M_CALL_INIT(key_oplist, key); \
M_CALL_INIT(value_oplist, value); \
M_IF(isSet)(, \
value_type value; \
M_CALL_INIT(value_oplist, value); \
) \
do { \
do { c = fgetc(file); } while (isspace(c)); \
ungetc(c, file); \
@@ -760,11 +771,98 @@
) \
do { c = fgetc(file); } while (isspace(c)); \
} while (c == ','); \
M_CALL_CLEAR(key_oplist, key); \
M_CALL_CLEAR(value_oplist, value); \
M_CALL_CLEAR(key_oplist, key); \
M_IF(isSet)(, \
M_CALL_CLEAR(value_oplist, value); \
) \
return c == '}'; \
} \
, /* no IN_STR */ ) \
\
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, dict_t const t1) \
{ \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
const M_C(name, _type_t) *item; \
bool first_done = false; \
dict_it_t it; \
/* Format is different between associative container \
& set container */ \
M_IF(isSet)( \
ret = f->m_interface->write_array_start(local, f, M_C(name, _size)(t1)); \
for (M_C(name, _it)(it, t1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \
first_done = true; \
} \
ret |= f->m_interface->write_array_end(local, f); \
, \
ret = f->m_interface->write_map_start(local, f, M_C(name, _size)(t1)); \
for (M_C(name, _it)(it, t1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_map_next(local, f); \
ret |= M_CALL_OUT_SERIAL(key_oplist, f, item->key); \
ret |= f->m_interface->write_map_value(local, f); \
ret |= M_CALL_OUT_SERIAL(value_oplist, f, item->value); \
first_done = true; \
} \
ret |= f->m_interface->write_map_end(local, f); \
) \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(dict_t t1, m_serial_read_t f) \
{ \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
key_type key; \
M_C(name,_clean)(t1); \
M_IF(isSet)( \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(key_oplist, key); \
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push)(t1, key); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(key_oplist, key); \
, \
value_type value; \
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(key_oplist, key); \
M_CALL_INIT (value_oplist, value); \
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
ret = f->m_interface->read_map_value(local, f); \
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
M_C(name, _set_at)(t1, key, value); \
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(key_oplist, key); \
M_CALL_CLEAR(value_oplist, value); \
) /* End of IF isSet */ \
return ret; \
} \
, /* no in_serial */ ) \
\
M_IF(isSet)( \
static inline void \
@@ -813,8 +911,14 @@
Or if the table of the dictionnary has different values (this may \
be avoided). */
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define DICTI_OPLIST_P1(arg) DICTI_OPLIST_P2 arg
/* Define the oplist of a dictionnary */
#define DICTI_OPLIST(name, key_oplist, value_oplist) \
#define DICTI_OPLIST_P2(name, key_oplist, value_oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
SET(M_C(name, _set)), \
@@ -829,6 +933,7 @@
IT_TYPE(M_C(name, _it_t)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name, _it_set)), \
IT_END(M_C(name,_it_end)), \
IT_END_P(M_C(name,_end_p)), \
IT_LAST_P(M_C(name,_last_p)), \
IT_NEXT(M_C(name,_next)), \
@@ -842,19 +947,25 @@
,ERASE_KEY(M_C(name, _erase)) \
,KEY_OPLIST(key_oplist) \
,VALUE_OPLIST(value_oplist) \
,GET_SIZE(M_C(name, _size)) \
,M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)(GET_STR(M_C(name, _get_str)),) \
,M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,M_IF_METHOD(EQUAL, value_oplist)(EQUAL(M_C(name, _equal_p)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW key_oplist),) \
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC key_oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL key_oplist),) \
)
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define DICTI_SET_OPLIST_P1(arg) DICTI_SET_OPLIST_P2 arg
/* Define the oplist of a set */
#define DICTI_SET_OPLIST(name, oplist) \
#define DICTI_SET_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
INIT_WITH(API_1(M_INIT_VAI)), \
@@ -875,9 +986,11 @@
ERASE_KEY(M_C(name, _erase)), \
KEY_OPLIST(oplist), \
VALUE_OPLIST(oplist), \
GET_SIZE(M_C(name, _size)), \
IT_TYPE(M_C(name, _it_t)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name, _it_set)), \
IT_END(M_C(name,_it_end)), \
IT_END_P(M_C(name,_end_p)), \
IT_LAST_P(M_C(name,_last_p)), \
IT_NEXT(M_C(name,_next)), \
@@ -888,15 +1001,28 @@
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,EQUAL(M_C(name, _equal_p)), \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* Define Lower Bound for hash table (TODO: Common macro for both implementation) */
#ifndef DICTI_LOWER_BOUND
#define DICTI_LOWER_BOUND(s) ((s) <= DICTI_INITIAL_SIZE ? 0 : (s) * 1 / 4)
#endif
/* Define Lower Bound for hash table (TODO: Common macro for both implementation) */
#ifndef DICTI_UPPER_BOUND
#define DICTI_UPPER_BOUND(s) ((s) * 2 / 3)
#endif
/* Define initial size of the hash table */
#ifndef DICTI_INITIAL_SIZE
#define DICTI_INITIAL_SIZE 16
#endif
#define DICTI_CONTRACT(name, map) do { \
assert(map != NULL); \
@@ -921,6 +1047,15 @@ typedef enum {
#define DICTI_OA_PROBING(s) ((s)++)
#endif
/* Lower Bound of the hash table (TODO: Common macro for both dictionnary) */
#ifndef DICTI_OA_LOWER_BOUND
#define DICTI_OA_LOWER_BOUND 0.2
#endif
/* Upper Bound of the hash table (TODO: Common macro for both dictionnary) */
#ifndef DICTI_OA_UPPER_BOUND
#define DICTI_OA_UPPER_BOUND 0.7
#endif
#define DICTI_OA_CONTRACT(dict) do { \
assert ( (dict) != NULL); \
assert( (dict)->lower_limit <= (dict)->count && (dict)->count <= (dict)->upper_limit ); \
@@ -932,18 +1067,32 @@ typedef enum {
#define DICTI_OA_DEF_P1(args) DICTI_OA_DEF_P2 args
#define DICTI_OA_DEF_P2(name, key_type, key_oplist, value_type, value_oplist) \
DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, \
M_GET_OOR_EQUAL key_oplist, M_GET_OOR_SET key_oplist, \
0.2, 0.7, M_C(name,_t), M_C(name, _it_t) )
DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, 0, \
DICTI_OA_LOWER_BOUND, DICTI_OA_UPPER_BOUND, M_C(name,_t), M_C(name, _it_t) )
#define DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, oor_equal_p, oor_set, coeff_down, coeff_up, dict_t, dict_it_t) \
#define DICTI_OASET_DEF_P1(args) DICTI_OASET_DEF_P2 args
#define DICTI_OASET_DEF_P2(name, key_type, key_oplist) \
DICTI_OA_DEF_P3(name, key_type, key_oplist, key_type, M_EMPTY_OPLIST, 1, \
DICTI_OA_LOWER_BOUND, DICTI_OA_UPPER_BOUND, M_C(name,_t), M_C(name, _it_t) )
#define DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, isSet, coeff_down, coeff_up, dict_t, dict_it_t) \
\
/* NOTE: \
if isSet is true, all methods of value_oplist are NOP methods */ \
\
typedef struct M_C(name, _pair_s) { \
key_type key; \
value_type value; \
M_IF(isSet)( , value_type value;) \
} M_C(name, _pair_t); \
\
/* NOTE: We don't want a real oplist for this type */ \
M_IF(isSet)( \
typedef key_type M_C(name, _type_t); \
, \
typedef struct M_C(name, _pair_s) M_C(name, _type_t); ) \
typedef key_type M_C(name, _key_type_t); \
typedef value_type M_C(name, _value_type_t); \
\
/* NOTE: We don't want a real oplist for this sub type */ \
ARRAY_DEF(M_C(name, _array_pair), M_C(name, _pair_t), \
(INIT(M_NOTHING_DEFAULT), SET(M_MEMCPY_DEFAULT), \
INIT_SET(M_MEMCPY_DEFAULT), CLEAR(M_NOTHING_DEFAULT))) \
@@ -955,7 +1104,6 @@ typedef enum {
} dict_t[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_C(name, _pair_s) M_C(name, _type_t); \
\
typedef struct M_C(name, _it_s) { \
const struct M_C(name,_s) *dict; \
@@ -979,11 +1127,11 @@ typedef enum {
M_C(name,_int_limit)(dict, DICTI_INITIAL_SIZE); \
dict->data = M_CALL_REALLOC(key_oplist, M_C(name, _pair_t), NULL, DICTI_INITIAL_SIZE); \
if (dict->data == NULL) { \
M_MEMORY_FULL(sizeof (M_C(name, _pair_t)) * DICTI_INITIAL_SIZE); \
M_MEMORY_FULL(sizeof (M_C(name, _pair_t)) * DICTI_INITIAL_SIZE); \
return ; \
} \
for(size_t i = 0; i < DICTI_INITIAL_SIZE; i++) { \
oor_set(dict->data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, dict->data[i].key, DICTI_OA_EMPTY); \
} \
DICTI_OA_CONTRACT(dict); \
} \
@@ -993,8 +1141,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
for(size_t i = 0; i <= dict->mask; i++) { \
if (!oor_equal_p(dict->data[i].key, DICTI_OA_EMPTY) \
&& !oor_equal_p(dict->data[i].key, DICTI_OA_DELETED)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, DICTI_OA_EMPTY) \
&& !M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, DICTI_OA_DELETED)) { \
M_CALL_CLEAR(key_oplist, dict->data[i].key); \
M_CALL_CLEAR(value_oplist, dict->data[i].value); \
} \
@@ -1010,8 +1158,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: Key can not be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
@@ -1019,8 +1167,8 @@ typedef enum {
\
/* Random access, and probably cache miss */ \
if (M_LIKELY (M_CALL_EQUAL(key_oplist, data[p].key, key)) ) \
return &data[p].value; \
else if (M_LIKELY (oor_equal_p (data[p].key, DICTI_OA_EMPTY)) ) \
return &data[p].M_IF(isSet)(key, value); \
else if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) ) \
return NULL; \
\
/* Unlikely case */ \
@@ -1028,9 +1176,9 @@ typedef enum {
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) \
return &data[p].value; \
return &data[p].M_IF(isSet)(key, value); \
assert (s <= dict->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
\
return NULL; \
} \
@@ -1050,8 +1198,8 @@ typedef enum {
size_t empty = 0; \
size_t del = 0; \
for(size_t i = 0 ; i <= h->mask ; i++) { \
empty += oor_equal_p(data[i].key, DICTI_OA_EMPTY); \
del += oor_equal_p(data[i].key, DICTI_OA_DELETED); \
empty += M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY); \
del += M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED); \
} \
assert(del == 0); \
assert(empty + h->count == h->mask + 1); \
@@ -1076,7 +1224,7 @@ typedef enum {
\
/* First mark the extended space as empty */ \
for(size_t i = oldSize ; i < newSize; i++) \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
\
/* Then let's rehash all the entries in their **exact** position. \
@@ -1089,10 +1237,10 @@ typedef enum {
const size_t mask = (newSize -1); \
\
for(size_t i = 0 ; i < oldSize; i++) { \
if (!oor_equal_p(data[i].key, DICTI_OA_DELETED) && !oor_equal_p(data[i].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED) && !M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) { \
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
if (p != i) { \
if (M_LIKELY (oor_equal_p(data[p].key, DICTI_OA_EMPTY) || oor_equal_p(data[p].key, DICTI_OA_DELETED))) { \
if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) || M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED))) { \
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} else { \
@@ -1100,30 +1248,28 @@ typedef enum {
M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
} \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
} else { \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
} \
\
/* Let's put back the entries in the tmp array in their right place */ \
/* NOTE: There should be very few entries in this array \
which contains what we weren't be able to fit in the first pass */ \
for(size_t i = 0; i < M_C(name, _array_pair_size)(tmp); i++) { \
M_C(name, _pair_t) *item = M_C(name, _array_pair_get)(tmp, i); \
while (M_C(name, _array_pair_size)(tmp) > 0) { \
M_C(name, _pair_t) const *item = M_C(name, _array_pair_back)(tmp); \
size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \
/* NOTE: since the first pass, the bucket might be free now */ \
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
assert (s <= h->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
} \
/* FIXME: How can I use INIT_MOVE without garbaging the array? ==> POP_INIT_MOVE ! */ \
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
M_CALL_INIT_SET(value_oplist, data[p].value, item->value); \
M_C(name, _array_pair_pop_move)(&data[p], tmp); \
} \
\
M_C(name, _array_pair_clear) (tmp); \
@@ -1138,13 +1284,14 @@ typedef enum {
} \
\
static inline void \
M_C(name,_set_at)(dict_t dict, key_type const key, \
value_type const value) \
M_IF(isSet)(M_C(name, _push), M_C(name,_set_at)) \
(dict_t dict, key_type const key \
M_IF(isSet)(, M_DEFERRED_COMMA value_type const value) ) \
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: key can not be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
@@ -1155,9 +1302,9 @@ typedef enum {
M_CALL_SET(value_oplist, data[p].value, value); \
return; \
} \
if (M_UNLIKELY (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) ) { \
if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) ) { \
size_t delPos = -1; \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED)) delPos = p; \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) delPos = p; \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
@@ -1166,8 +1313,8 @@ typedef enum {
return; \
} \
assert (s <= dict->mask); \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
if (delPos != (size_t) -1) { \
p = delPos; \
dict->count_delete --; \
@@ -1197,28 +1344,28 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: key can not be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
size_t p = M_CALL_HASH(key_oplist, key) & mask; \
\
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \
return &data[p].value; \
return &data[p].M_IF(isSet)(key, value); \
} \
if (M_UNLIKELY (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) ) { \
if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) ) { \
size_t delPos = -1; \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED)) delPos = p; \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) delPos = p; \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \
return &data[p].value; \
return &data[p].M_IF(isSet)(key, value); \
} \
assert (s <= dict->mask); \
if (oor_equal_p(data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
if (delPos != (size_t) -1) { \
p = delPos; \
dict->count_delete --; \
@@ -1243,7 +1390,7 @@ typedef enum {
return M_C(name, _get)(dict, key); \
} \
DICTI_OA_CONTRACT(dict); \
return &data[p].value; \
return &data[p].M_IF(isSet)(key, value); \
} \
\
static inline void \
@@ -1260,15 +1407,15 @@ typedef enum {
\
/* Pass 1: scan lower entries, and move them if needed */ \
for(size_t i = 0; i < newSize; i++) { \
if (oor_equal_p(data[i].key, DICTI_OA_EMPTY)) \
if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) \
continue; \
if (oor_equal_p(data[i].key, DICTI_OA_DELETED)) { \
oor_set(data[i].key, DICTI_OA_EMPTY); \
if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED)) { \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
continue; \
} \
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
if (p != i) { \
if (oor_equal_p(data[p].key, DICTI_OA_EMPTY) || oor_equal_p(data[p].key, DICTI_OA_DELETED)) { \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) || M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) { \
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} else { \
@@ -1276,39 +1423,38 @@ typedef enum {
M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
} \
oor_set(data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
} \
} \
/* Pass 2: scan upper entries and move them back */ \
for(size_t i = newSize; i < oldSize; i++) { \
if (!oor_equal_p(data[i].key, DICTI_OA_DELETED) \
&& !oor_equal_p(data[i].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED) \
&& !M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) { \
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
assert (p < i); \
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
assert (s <= h->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
} \
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} \
} \
/* Pass 3: scan moved entries and move them back */ \
for(size_t i = 0; i < M_C(name, _array_pair_size)(tmp); i++) { \
M_C(name, _pair_t) *item = M_C(name, _array_pair_get)(tmp, i); \
while (M_C(name, _array_pair_size)(tmp) > 0) { \
M_C(name, _pair_t) const *item = M_C(name, _array_pair_back)(tmp); \
size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
assert (s <= h->mask); \
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
} \
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
M_CALL_INIT_SET(value_oplist, data[p].value, item->value); \
M_C(name, _array_pair_pop_move)(&data[p], tmp); \
} \
\
M_C(name, _array_pair_clear) (tmp); \
@@ -1329,28 +1475,28 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(dict); \
/* NOTE: key can't be the representation of empty or deleted */ \
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
\
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
\
M_C(name, _pair_t) *const data = dict->data; \
const size_t mask = dict->mask; \
size_t p = M_CALL_HASH(key_oplist, key) & mask; \
\
/* Random access, and probably cache miss */ \
if (M_UNLIKELY (!M_CALL_EQUAL(key_oplist, data[p].key, key)) ) { \
if (oor_equal_p (data[p].key, DICTI_OA_EMPTY)) \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) \
return false; \
size_t s = 1; \
do { \
p = (p + DICTI_OA_PROBING(s)) & mask; \
if (oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) \
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) \
return false; \
assert (s <= dict->mask); \
} while (!M_CALL_EQUAL(key_oplist, data[p].key, key)); \
} \
M_CALL_CLEAR(key_oplist, data[p].key); \
M_CALL_CLEAR(value_oplist, data[p].value); \
oor_set (data[p].key, DICTI_OA_DELETED); \
M_CALL_OOR_SET(key_oplist, data[p].key, DICTI_OA_DELETED); \
assert (dict->count >= 1); \
dict->count--; \
if (M_UNLIKELY (dict->count < dict->lower_limit)) { \
@@ -1390,10 +1536,10 @@ typedef enum {
return ; \
} \
for(size_t i = 0; i <= org->mask; i++) { \
if (oor_equal_p(org->data[i].key, DICTI_OA_EMPTY)) { \
oor_set(map->data[i].key, DICTI_OA_EMPTY); \
} else if (oor_equal_p(org->data[i].key, DICTI_OA_DELETED)) { \
oor_set(map->data[i].key, DICTI_OA_DELETED); \
if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, DICTI_OA_EMPTY)) { \
M_CALL_OOR_SET(key_oplist, map->data[i].key, DICTI_OA_EMPTY); \
} else if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, DICTI_OA_DELETED)) { \
M_CALL_OOR_SET(key_oplist, map->data[i].key, DICTI_OA_DELETED); \
} else { \
M_CALL_INIT_SET(key_oplist, map->data[i].key, org->data[i].key); \
M_CALL_INIT_SET(value_oplist, map->data[i].value, org->data[i].value); \
@@ -1463,8 +1609,8 @@ typedef enum {
{ \
DICTI_OA_CONTRACT(d); \
for(size_t i = 0; i <= d->mask; i++) { \
if (!oor_equal_p(d->data[i].key, DICTI_OA_EMPTY) \
&& !oor_equal_p(d->data[i].key, DICTI_OA_DELETED)) { \
if (!M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
&& !M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) { \
M_CALL_CLEAR(key_oplist, d->data[i].key); \
M_CALL_CLEAR(value_oplist, d->data[i].value); \
} \
@@ -1477,7 +1623,7 @@ typedef enum {
d->data, DICTI_INITIAL_SIZE); \
assert(d->data != NULL); \
for(size_t i = 0; i <= d->mask; i++) { \
oor_set(d->data[i].key, DICTI_OA_EMPTY); \
M_CALL_OOR_SET(key_oplist, d->data[i].key, DICTI_OA_EMPTY); \
} \
DICTI_OA_CONTRACT(d); \
} \
@@ -1489,8 +1635,8 @@ typedef enum {
assert (it != NULL); \
it->dict = d; \
size_t i = 0; \
while ((oor_equal_p (d->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (d->data[i].key, DICTI_OA_DELETED)) \
while ((M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) \
&& i <= d->mask) { \
i++; \
} \
@@ -1514,8 +1660,8 @@ typedef enum {
assert (it != NULL); \
it->dict = d; \
size_t i = d->mask; \
while ((oor_equal_p (d->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (d->data[i].key, DICTI_OA_DELETED)) \
while ((M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) \
&& i <= d->mask) { \
i--; \
} \
@@ -1546,8 +1692,8 @@ typedef enum {
DICTI_OA_CONTRACT (it->dict); \
size_t i = it->index + 1; \
while (i <= it->dict->mask && \
(oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED))) { \
(M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED))) { \
i++; \
} \
it->index = i; \
@@ -1561,8 +1707,8 @@ typedef enum {
/* if index was 0, the operation will overflow, and stops the loop */ \
size_t i = it->index - 1; \
while (i <= it->dict->mask && \
(oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY) \
|| oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED))) { \
(M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY) \
|| M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED))) { \
i--; \
} \
it->index = i; \
@@ -1587,22 +1733,22 @@ typedef enum {
return it1->dict == it2->dict && it1->index == it2->index; \
} \
\
static inline struct M_C(name, _pair_s) * \
static inline M_C(name, _type_t) * \
M_C(name, _ref)(const dict_it_t it) \
{ \
assert (it != NULL); \
DICTI_OA_CONTRACT (it -> dict); \
const size_t i = it->index; \
assert (i <= it->dict->mask); \
assert (!oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY)); \
assert (!oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED)); \
return &it->dict->data[i]; \
assert (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY)); \
assert (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED)); \
return &it->dict->data[i] M_IF(isSet)(.key, ); \
} \
\
static inline const struct M_C(name, _pair_s) * \
static inline const M_C(name, _type_t) * \
M_C(name, _cref)(const dict_it_t it) \
{ \
return M_CONST_CAST(struct M_C(name, _pair_s), M_C(name, _ref)(it)); \
return M_CONST_CAST(M_C(name, _type_t), M_C(name, _ref)(it)); \
} \
\
static inline void \
@@ -1612,6 +1758,7 @@ typedef enum {
size_t size; \
/* Get the size which will allow to fit this capacity */ \
size = m_core_roundpow2 (capacity * (1.0 / coeff_up)); \
/* Test for overflow of the computation */ \
if (M_UNLIKELY (size < capacity)) { \
M_MEMORY_FULL((size_t)-1); \
} \
@@ -1640,20 +1787,18 @@ typedef enum {
for(M_C(name, _it)(it, dict1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)) { \
const struct M_C(name, _pair_s) *item = M_C(name, _cref)(it); \
value_type *ptr = M_C(name, _get)(dict2, item->key); \
const M_C(name, _type_t) *item = M_C(name, _cref)(it); \
value_type *ptr = M_C(name, _get)(dict2, M_IF(isSet)(*item, item->key)); \
if (ptr == NULL) \
return false; \
M_IF(isSet)(, \
if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \
return false; \
) \
return false; \
} \
return true; \
} \
, /* no value equal */ ) \
\
DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, 0, dict_t, dict_it_t)
DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t)
#endif
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - GENINT module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+32 -32
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Intrusive List module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -46,15 +46,15 @@
USAGE:
ILIST_DEF(name, type [, oplist_of_the_type]) */
#define ILIST_DEF(name, ...) \
ILISTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name, _t), M_C(name, _it_t) ), \
(name, __VA_ARGS__, M_C(name, _t), M_C(name, _it_t) )))
ILISTI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name, _t), M_C(name, _it_t) ), \
(name, __VA_ARGS__, M_C(name, _t), M_C(name, _it_t) )))
/* Define the oplist of a doubly-linked instrusive list of type.
USAGE:
ILIST_OPLIST(name [, oplist_of_the_type]) */
#define ILIST_OPLIST(...) \
ILISTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
ILISTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
@@ -68,10 +68,10 @@ typedef struct ilist_head_s {
} ilist_head_t;
/* Indirection call to allow expanding all arguments */
#define ILISTI_OPLIST(arg) ILISTI_OPLIST2 arg
#define ILISTI_OPLIST_P1(arg) ILISTI_OPLIST_P2 arg
/* Define the oplist of an ilist of type */
#define ILISTI_OPLIST2(name,oplist) \
#define ILISTI_OPLIST_P2(name,oplist) \
(INIT(M_C(name, _init)), \
CLEAR(M_C(name, _clear)), \
INIT_MOVE(M_C(name, _init_move)), \
@@ -120,7 +120,7 @@ typedef struct ilist_head_s {
} while (0)
/* Indirection call to allow expanding all arguments (TBC) */
#define ILISTI_DEF(arg) ILISTI_DEF2 arg
#define ILISTI_DEF_P1(arg) ILISTI_DEF_P2 arg
/* Definition of the type and function for an intrusive doubly-linked list.
USAGE:
@@ -128,9 +128,9 @@ typedef struct ilist_head_s {
type: type of the object
oplist: oplist of the type
list_t: type of the intrusive list (name##_t)
list_it_t: iterator of the intrusive list (name##_it_t)
it_t: iterator of the intrusive list (name##_it_t)
*/
#define ILISTI_DEF2(name, type, oplist, list_t, list_it_t) \
#define ILISTI_DEF_P2(name, type, oplist, list_t, it_t) \
\
typedef struct M_C(name, _s) { \
struct ilist_head_s name; \
@@ -145,7 +145,7 @@ typedef struct ilist_head_s {
struct ilist_head_s *previous; \
struct ilist_head_s *current; \
struct ilist_head_s *next; \
} list_it_t[1]; \
} it_t[1]; \
\
static inline void M_C(name, _init)(list_t list) \
{ \
@@ -316,7 +316,7 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _it)(list_it_t it, const list_t list) \
M_C(name, _it)(it_t it, const list_t list) \
{ \
ILISTI_CONTRACT(name, list); \
assert (it != NULL); \
@@ -328,7 +328,7 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _it_set)(list_it_t it, const list_it_t cit) \
M_C(name, _it_set)(it_t it, const it_t cit) \
{ \
assert (it != NULL && cit != NULL); \
it->head = cit->head; \
@@ -339,30 +339,30 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _it_last)(list_it_t it, list_t list) \
M_C(name, _it_last)(it_t it, list_t const list) \
{ \
ILISTI_CONTRACT(name, list); \
assert (it != NULL); \
it->head = &list->name; \
it->head = list->name.next->prev; \
it->current = list->name.prev; \
it->next = &list->name; \
it->next = it->head; \
it->previous = list->name.prev->prev; \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
static inline void \
M_C(name, _it_end)(list_it_t it, list_t list) \
M_C(name, _it_end)(it_t it, list_t const list) \
{ \
assert (it != NULL && list != NULL); \
it->head = &list->name; \
it->current = &list->name; \
it->head = list->name.next->prev; \
it->current = it->head; \
it->next = list->name.next; \
it->previous = list->name.prev; \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
static inline bool \
M_C(name, _end_p)(const list_it_t it) \
M_C(name, _end_p)(const it_t it) \
{ \
assert (it != NULL); \
ILISTI_NODE_CONTRACT(it->current); \
@@ -370,7 +370,7 @@ typedef struct ilist_head_s {
} \
\
static inline bool \
M_C(name, _last_p)(const list_it_t it) \
M_C(name, _last_p)(const it_t it) \
{ \
assert (it != NULL); \
ILISTI_NODE_CONTRACT(it->current); \
@@ -378,7 +378,7 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _next)(list_it_t it) \
M_C(name, _next)(it_t it) \
{ \
assert (it != NULL); \
/* Cannot check node for it->current: it may have been deleted! */ \
@@ -393,7 +393,7 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _previous)(list_it_t it) \
M_C(name, _previous)(it_t it) \
{ \
assert (it != NULL); \
/* Cannot check node for it->current: it may have been deleted! */ \
@@ -408,7 +408,7 @@ typedef struct ilist_head_s {
} \
\
static inline bool \
M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2 ) \
M_C(name, _it_equal_p)(const it_t it1, const it_t it2 ) \
{ \
assert (it1 != NULL && it2 != NULL); \
/* No need to check for next & previous */ \
@@ -416,7 +416,7 @@ typedef struct ilist_head_s {
} \
\
static inline type * \
M_C(name, _ref)(const list_it_t it) \
M_C(name, _ref)(const it_t it) \
{ \
assert (it != NULL && it->current != NULL); \
ILISTI_NODE_CONTRACT(it->current); \
@@ -429,14 +429,14 @@ typedef struct ilist_head_s {
} \
\
static inline type const * \
M_C(name, _cref)(const list_it_t it) \
M_C(name, _cref)(const it_t it) \
{ \
type *ptr = M_C(name, _ref)(it); \
return M_CONST_CAST(type, ptr); \
} \
\
static inline void \
M_C(name, _remove)(list_t list, list_it_t it) \
M_C(name, _remove)(list_t list, it_t it) \
{ \
ILISTI_CONTRACT(name, list); \
ILISTI_NODE_CONTRACT(it->current); \
@@ -451,7 +451,7 @@ typedef struct ilist_head_s {
\
M_IF_METHOD(NEW, oplist)( \
static inline void \
M_C(name, _insert)(list_t list, list_it_t it, type x) \
M_C(name, _insert)(list_t list, it_t it, type x) \
{ \
ILISTI_CONTRACT(name, list); \
ILISTI_NODE_CONTRACT(it->current); \
@@ -508,7 +508,7 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _splice_back)(list_t nv, list_t ov, list_it_t it) \
M_C(name, _splice_back)(list_t nv, list_t ov, it_t it) \
{ \
ILISTI_CONTRACT(name, nv); \
ILISTI_CONTRACT(name, ov); \
@@ -524,8 +524,8 @@ typedef struct ilist_head_s {
} \
\
static inline void \
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
list_t olist, list_it_t opos) \
M_C(name, _splice_at)(list_t nlist, it_t npos, \
list_t olist, it_t opos) \
{ \
ILISTI_CONTRACT(name, nlist); \
ILISTI_CONTRACT(name, olist); \
@@ -590,7 +590,7 @@ typedef struct ilist_head_s {
ILISTI_CONTRACT(name, list); \
} \
\
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
#endif
+14 -11
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - INTRUSIVE SHARED PTR Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -31,9 +31,9 @@
/* Define the oplist of a intrusive shared pointer.
USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */
#define ISHARED_PTR_OPLIST(...) \
ISHAREDI_PTR_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
ISHAREDI_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/* Interface to add to a structure to allow intrusive support.
name: name of the intrusive shared pointer.
@@ -45,16 +45,18 @@
/* Define the intrusive shared pointer type and its static inline functions.
USAGE: ISHARED_PTR_DEF(name[, oplist]) */
#define ISHARED_PTR_DEF(name, ...) \
ISHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
ISHAREDI_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
/********************************** INTERNAL ************************************/
#define ISHAREDI_PTR_OPLIST(arg) ISHAREDI_PTR_OPLIST2 arg
// Deferred evaluation
#define ISHAREDI_PTR_OPLIST_P1(arg) ISHAREDI_PTR_OPLIST_P2 arg
#define ISHAREDI_PTR_OPLIST2(name, oplist) ( \
// Define the oplist
#define ISHAREDI_PTR_OPLIST_P2(name, oplist) ( \
INIT(M_INIT_DEFAULT), \
INIT_SET(M_C(name, _init_set2) M_IPTR), \
SET(M_C(name, _set) M_IPTR), \
@@ -68,9 +70,10 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
#define ISHAREDI_PTR_DEF(arg) ISHAREDI_PTR_DEF2 arg
// Deferred evaluatioin
#define ISHAREDI_PTR_DEF_P1(arg) ISHAREDI_PTR_DEF_P2 arg
#define ISHAREDI_PTR_DEF2(name, type, oplist) \
#define ISHAREDI_PTR_DEF_P2(name, type, oplist) \
\
typedef type *M_C(name,_t); \
typedef type M_C(name, _type_t); \
+170 -61
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - LIST module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -30,30 +30,33 @@
/* Define a singly linked list of a given type.
USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */
#define LIST_DEF(name, ...) \
LISTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
LISTI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
/* Define a dual-push singly linked list of a given type.
USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */
#define LIST_DUAL_PUSH_DEF(name, ...) \
LISTI_DUAL_PUSH_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
LISTI_DUAL_PUSH_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
/* Define the oplist of a list of type.
USAGE: LIST_OPLIST(name [, oplist_of_the_type]) */
#define LIST_OPLIST(...) \
LISTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
(__VA_ARGS__ )))
LISTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
#define LISTI_OPLIST(arg) LISTI_OPLIST2 arg
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define LISTI_OPLIST_P1(arg) LISTI_OPLIST_P2 arg
#define LISTI_OPLIST2(name, oplist) \
/* OPLIST definition of a list and list_dual_push */
#define LISTI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
INIT_WITH(API_1(M_INIT_VAI)), \
@@ -90,6 +93,8 @@
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
@@ -97,7 +102,9 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
#define LISTI_DEF(arg) LISTI_DEF2 arg
/* Deferred evaluation for the list definition,
so that all arguments are evaluated before further expansion */
#define LISTI_DEF_P1(arg) LISTI_DEF_P2 arg
/* Define allocation functions. If MEMPOOL, we need to define it */
#define LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
@@ -122,7 +129,16 @@
} \
) \
#define LISTI_DEF2(name, type, oplist, list_t, list_it_t) \
/* Internal list definition
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- list_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define LISTI_DEF_P2(name, type, oplist, list_t, it_t) \
\
typedef struct M_C(name, _s) { \
struct M_C(name, _s) *next; \
@@ -136,17 +152,29 @@
typedef struct M_C(name, _it_s) { \
struct M_C(name, _s) *previous; \
struct M_C(name, _s) *current; \
} list_it_t[1]; \
} it_t[1]; \
\
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
LISTI_DEF3(name, type, oplist, list_t, list_it_t) \
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \
LISTI_DEF_P3(name, type, oplist, list_t, it_t) \
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
/* Define the internal contract of a list
(there is nothing worthy to be checked) */
#define LISTI_CONTRACT(v) do { \
assert (v != NULL); \
} while (0)
#define LISTI_DEF3(name, type, oplist, list_t, list_it_t) \
/* Internal list function definition
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- list_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define LISTI_DEF_P3(name, type, oplist, list_t, it_t) \
\
static inline void \
M_C(name, _init)(list_t v) \
@@ -280,7 +308,7 @@
} \
\
static inline void \
M_C(name, _it)(list_it_t it, const list_t v) \
M_C(name, _it)(it_t it, const list_t v) \
{ \
LISTI_CONTRACT(v); \
assert (it != NULL); \
@@ -289,7 +317,7 @@
} \
\
static inline void \
M_C(name, _it_set)(list_it_t it1, const list_it_t it2) \
M_C(name, _it_set)(it_t it1, const it_t it2) \
{ \
assert (it1 != NULL && it2 != NULL); \
it1->current = it2->current; \
@@ -297,7 +325,7 @@
} \
\
static inline void \
M_C(name, _it_end)(list_it_t it1, const list_t v) \
M_C(name, _it_end)(it_t it1, const list_t v) \
{ \
LISTI_CONTRACT(v); \
assert (it1 != NULL); \
@@ -307,21 +335,21 @@
} \
\
static inline bool \
M_C(name, _end_p)(const list_it_t it) \
M_C(name, _end_p)(const it_t it) \
{ \
assert (it != NULL); \
return it->current == NULL; \
} \
\
static inline bool \
M_C(name, _last_p)(const list_it_t it) \
M_C(name, _last_p)(const it_t it) \
{ \
assert (it != NULL); \
return it->current == NULL || it->current->next == NULL; \
} \
\
static inline void \
M_C(name, _next)(list_it_t it) \
M_C(name, _next)(it_t it) \
{ \
assert(it != NULL && it->current != NULL); \
it->previous = it->current; \
@@ -329,21 +357,21 @@
} \
\
static inline bool \
M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2) \
M_C(name, _it_equal_p)(const it_t it1, const it_t it2) \
{ \
assert(it1 != NULL && it2 != NULL); \
return it1->current == it2->current; \
} \
\
static inline type * \
M_C(name, _ref)(const list_it_t it) \
M_C(name, _ref)(const it_t it) \
{ \
assert(it != NULL && it->current != NULL); \
return &(it->current->data); \
} \
\
static inline type const * \
M_C(name, _cref)(const list_it_t it) \
M_C(name, _cref)(const it_t it) \
{ \
assert(it != NULL && it->current != NULL); \
return M_CONST_CAST(type, &(it->current->data)); \
@@ -363,7 +391,7 @@
} \
\
static inline bool \
M_C(name, _sublist_p)(const list_t list, const list_it_t itsub) \
M_C(name, _sublist_p)(const list_t list, const it_t itsub) \
{ \
LISTI_CONTRACT(list); \
assert (itsub != NULL); \
@@ -399,7 +427,7 @@
} \
\
static inline void \
M_C(name, _insert)(list_t list, list_it_t insertion_point, \
M_C(name, _insert)(list_t list, it_t insertion_point, \
type const x) \
{ \
LISTI_CONTRACT(list); \
@@ -423,7 +451,7 @@
} \
\
static inline void \
M_C(name, _remove)(list_t list, list_it_t removing_point) \
M_C(name, _remove)(list_t list, it_t removing_point) \
{ \
LISTI_CONTRACT(list); \
assert (removing_point != NULL); \
@@ -491,7 +519,7 @@
} \
\
static inline void \
M_C(name, _splice_back)(list_t nv, list_t ov, list_it_t it) \
M_C(name, _splice_back)(list_t nv, list_t ov, it_t it) \
{ \
LISTI_CONTRACT(nv); \
LISTI_CONTRACT(ov); \
@@ -515,8 +543,8 @@
} \
\
static inline void \
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
list_t olist, list_it_t opos) \
M_C(name, _splice_at)(list_t nlist, it_t npos, \
list_t olist, it_t opos) \
{ \
LISTI_CONTRACT(nlist); \
LISTI_CONTRACT(olist); \
@@ -580,7 +608,18 @@
*list = previous; \
} \
#define LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t) \
/* Internal list function definition based on iterator functions
(common for all kind of lists)
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- list_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t) \
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, const list_t list, \
@@ -676,6 +715,56 @@
} \
, /* no IN_STR & INIT */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, const list_t list) \
{ \
assert (list != NULL); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
bool first_done = false; \
ret = f->m_interface->write_array_start(local, f, 0); \
M_C(name, _it_t) it; \
for (M_C(name, _it)(it, list) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
type const *item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
first_done = true; \
} \
ret |= f->m_interface->write_array_end(local, f); \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(list_t list, m_serial_read_t f) \
{ \
assert (list != NULL); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
size_t estimated_size = 0; \
M_C(name,_clean)(list); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
type item; \
M_CALL_INIT(oplist, item); \
do { \
ret = M_CALL_IN_SERIAL(oplist, item, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push_back)(list, item); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(oplist, item); \
M_C(name, _reverse)(list); \
return ret; \
} \
, /* no IN_SERIAL & INIT */ ) \
\
M_IF_METHOD(EQUAL, oplist)( \
static inline bool \
M_C(name, _equal_p)(const list_t list1, const list_t list2) \
@@ -718,20 +807,31 @@
} \
, /* no hash */ ) \
/* Dual Push singly linked list.
Support Push Back / Push Front / Pop back in O(1).
Doesn't support Pop front.
This is done by keeping a pointer to both back & front
*/
#define LISTI_DUAL_PUSH_DEF(arg) LISTI_DUAL_PUSH_DEF2 arg
#define LISTI_DUAL_PUSH_DEF2(name, type, oplist, list_t, list_it_t) \
/* Deferred evaluation for the dual-push list definition,
so that all arguments are evaluated before further expansion */
#define LISTI_DUAL_PUSH_DEF_P1(arg) LISTI_DUAL_PUSH_DEF_P2 arg
/* Internal dual-push list definition
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- list_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define LISTI_DUAL_PUSH_DEF_P2(name, type, oplist, list_t, it_t) \
\
struct M_C(name, _s) { \
struct M_C(name, _s) *next; \
type data; \
}; \
\
/* Dual Push singly linked list. \
Support Push Back / Push Front / Pop back in O(1). \
Doesn't support Pop front. \
This is done by keeping a pointer to both back & front \
*/ \
typedef struct M_C(name, _head_s) { \
struct M_C(name, _s) *front; \
struct M_C(name, _s) *back; \
@@ -744,19 +844,28 @@
typedef struct M_C(name, _it_s) { \
struct M_C(name, _s) *previous; \
struct M_C(name, _s) *current; \
} list_it_t[1]; \
} it_t[1]; \
\
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
LISTI_DUAL_PUSH_DEF3(name, type, oplist, list_t, list_it_t) \
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \
LISTI_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
/* Define the internal contract of an dual-push list */
#define LISTI_DUAL_PUSH_CONTRACT(l) do { \
assert (l != NULL); \
assert ( (l->back == NULL && l->front == NULL) \
|| (l->back != NULL && l->front != NULL)); \
} while (0)
#define LISTI_DUAL_PUSH_DEF3(name, type, oplist, list_t, list_it_t) \
/* Internal dual-push list definition
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- list_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define LISTI_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \
\
static inline void \
M_C(name, _init)(list_t v) \
@@ -972,7 +1081,7 @@
} \
\
static inline void \
M_C(name, _it)(list_it_t it, const list_t v) \
M_C(name, _it)(it_t it, const list_t v) \
{ \
LISTI_DUAL_PUSH_CONTRACT(v); \
assert (it != NULL); \
@@ -981,7 +1090,7 @@
} \
\
static inline void \
M_C(name, _it_set)(list_it_t it1, const list_it_t it2) \
M_C(name, _it_set)(it_t it1, const it_t it2) \
{ \
assert (it1 != NULL && it2 != NULL); \
it1->current = it2->current; \
@@ -989,7 +1098,7 @@
} \
\
static inline void \
M_C(name, _it_end)(list_it_t it1, const list_t v) \
M_C(name, _it_end)(it_t it1, const list_t v) \
{ \
assert (it1 != NULL); \
LISTI_DUAL_PUSH_CONTRACT(v); \
@@ -999,21 +1108,21 @@
} \
\
static inline bool \
M_C(name, _end_p)(const list_it_t it) \
M_C(name, _end_p)(const it_t it) \
{ \
assert (it != NULL); \
return it->current == NULL; \
} \
\
static inline bool \
M_C(name, _last_p)(const list_it_t it) \
M_C(name, _last_p)(const it_t it) \
{ \
assert (it != NULL); \
return it->current == NULL || it->current->next == NULL; \
} \
\
static inline void \
M_C(name, _next)(list_it_t it) \
M_C(name, _next)(it_t it) \
{ \
assert(it != NULL && it->current != NULL); \
it->previous = it->current; \
@@ -1021,21 +1130,21 @@
} \
\
static inline bool \
M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2) \
M_C(name, _it_equal_p)(const it_t it1, const it_t it2) \
{ \
assert(it1 != NULL && it2 != NULL); \
return it1->current == it2->current; \
} \
\
static inline type * \
M_C(name, _ref)(const list_it_t it) \
M_C(name, _ref)(const it_t it) \
{ \
assert(it != NULL && it->current != NULL); \
return &(it->current->data); \
} \
\
static inline type const * \
M_C(name, _cref)(const list_it_t it) \
M_C(name, _cref)(const it_t it) \
{ \
assert(it != NULL && it->current != NULL); \
return M_CONST_CAST(type, &(it->current->data)); \
@@ -1055,7 +1164,7 @@
} \
\
static inline void \
M_C(name, _insert)(list_t list, list_it_t insertion_point, \
M_C(name, _insert)(list_t list, it_t insertion_point, \
type const x) \
{ \
LISTI_DUAL_PUSH_CONTRACT(list); \
@@ -1084,7 +1193,7 @@
} \
\
static inline void \
M_C(name, _remove)(list_t list, list_it_t removing_point) \
M_C(name, _remove)(list_t list, it_t removing_point) \
{ \
LISTI_DUAL_PUSH_CONTRACT(list); \
assert (removing_point != NULL); \
@@ -1159,7 +1268,7 @@
} \
\
static inline void \
M_C(name, _splice_back)(list_t list1, list_t list2, list_it_t it) \
M_C(name, _splice_back)(list_t list1, list_t list2, it_t it) \
{ \
LISTI_DUAL_PUSH_CONTRACT(list1); \
LISTI_DUAL_PUSH_CONTRACT(list2); \
@@ -1191,8 +1300,8 @@
} \
\
static inline void \
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
list_t olist, list_it_t opos) \
M_C(name, _splice_at)(list_t nlist, it_t npos, \
list_t olist, it_t opos) \
{ \
LISTI_DUAL_PUSH_CONTRACT(nlist); \
LISTI_DUAL_PUSH_CONTRACT(olist); \
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - MEMPOOL module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+24 -10
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin Mutex & Thread wrapper
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -42,7 +42,7 @@ typedef thrd_t m_thread_t[1];
static inline void m_mutex_init(m_mutex_t m)
{
int rc = mtx_init(m, mtx_plain);
M_ASSERT_INIT (rc == 0);
M_ASSERT_INIT (rc == 0, "mutex");
}
static inline void m_mutex_clear(m_mutex_t m)
@@ -63,7 +63,7 @@ static inline void m_mutex_unlock(m_mutex_t m)
static inline void m_cond_init(m_cond_t c)
{
int rc = cnd_init(c);
M_ASSERT_INIT (rc == 0);
M_ASSERT_INIT (rc == 0, "conditional variable");
}
static inline void m_cond_clear(m_cond_t c)
@@ -89,13 +89,15 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
{
int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (rc == thrd_success);
M_ASSERT_INIT (rc == thrd_success, "thread");
}
static inline void m_thread_join(m_thread_t t)
{
int rc = thrd_join(*t, NULL);
assert (rc == thrd_success);
// Avoid warning about variable unused.
(void) rc;
}
static inline void m_thread_yield(void)
@@ -120,6 +122,7 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
call_once(o,func);
}
#define M_THREAD_ATTR _Thread_local
// MSYS2 doesn't define _WIN32 by default.
@@ -190,8 +193,8 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, arg, 0, NULL);
M_ASSERT_INIT (*t != NULL);
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (uintptr_t) func, arg, 0, NULL);
M_ASSERT_INIT (*t != NULL, "thread");
}
static inline void m_thread_join(m_thread_t t)
@@ -212,7 +215,7 @@ static inline bool m_thread_sleep(unsigned long long usec)
LARGE_INTEGER ft;
ft.QuadPart = -(10ULL*usec);
HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
M_ASSERT_INIT (hd != NULL);
M_ASSERT_INIT (hd != NULL, "timer");
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
CloseHandle(hd);
@@ -237,6 +240,12 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
InitOnceExecuteOnce(o, m_oncei_callback, (void*)(intptr_t)func, NULL);
}
#if defined(_MSC_VER)
# define M_THREAD_ATTR __declspec( thread )
#else
# define M_THREAD_ATTR __thread
#endif
#else
/**************************** PTHREAD version ******************************/
@@ -259,7 +268,7 @@ typedef pthread_t m_thread_t[1];
static inline void m_mutex_init(m_mutex_t m)
{
int _rc = pthread_mutex_init(m, NULL);
M_ASSERT_INIT (_rc == 0);
M_ASSERT_INIT (_rc == 0, "mutex");
}
static inline void m_mutex_clear(m_mutex_t m)
@@ -286,7 +295,7 @@ static inline void m_mutexi_lazy_lock(m_mutex_t m)
static inline void m_cond_init(m_cond_t c)
{
int _rc = pthread_cond_init(c, NULL);
M_ASSERT_INIT (_rc == 0);
M_ASSERT_INIT (_rc == 0, "conditional variable");
}
static inline void m_cond_clear(m_cond_t c)
@@ -312,7 +321,7 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (_rc == 0);
M_ASSERT_INIT (_rc == 0, "thread");
}
static inline void m_thread_join(m_thread_t t)
@@ -348,8 +357,13 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
pthread_once(o,func);
}
#define M_THREAD_ATTR __thread
#endif
// TODO: m_thread_sleep doesn't yield thread during waiting, making it a poor choice for active waiting.
// TODO: Obsolete M_LOCK macro.
/* M_LOCK macro. Allow simple locking encapsulation.
USAGE:
static M_LOCK_DECL(name);
+35 -19
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic priority queue module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -33,24 +33,29 @@
/* Define a prioqueue of a given type and its associated functions.
USAGE: PRIOQUEUE_DEF(name, type [, oplist_of_the_type]) */
#define PRIOQUEUE_DEF(name, ...) \
PRIOQUEUEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
PRIOQUEUEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
/* Define the oplist of a prioqueue of type.
USAGE: PRIOQUEUE_OPLIST(name[, oplist of the type]) */
#define PRIOQUEUE_OPLIST(...) \
PRIOQUEUEI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
PRIOQUEUEI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
#define PRIOQUEUEI_OPLIST(arg) PRIOQUEUEI_OPLIST2 arg
#define PRIOQUEUEI_DEF(arg) PRIOQUEUEI_DEF2 arg
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define PRIOQUEUEI_OPLIST_P1(arg) PRIOQUEUEI_OPLIST_P2 arg
#define PRIOQUEUEI_DEF_P1(arg) PRIOQUEUEI_DEF_P2 arg
#define PRIOQUEUEI_OPLIST2(name, oplist) \
/* Define oplist of a priority queue */
#define PRIOQUEUEI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
@@ -65,9 +70,18 @@
,PUSH(M_C(name,_push)) \
,POP(M_C(name,_pop)) \
,OPLIST(oplist) \
,TEST_EMPTY(M_C(name, _empty_p)), \
,GET_SIZE(M_C(name, _size)) \
)
#define PRIOQUEUEI_DEF2(name, type, oplist, prioqueue_t, it_t) \
/* Define the priority queue:
- name: prefix to use,
- type: type of the contained objects,
- oplist: oplist of the contained objects,
- prioqueue_t: type of the container,
- it_t: iterator of the container
*/
#define PRIOQUEUEI_DEF_P2(name, type, oplist, prioqueue_t, it_t) \
ARRAY_DEF(M_C(name, _array), type, oplist) \
\
typedef type M_C(name, _type_t); \
@@ -87,13 +101,13 @@
} \
\
static inline void \
M_C(name, _init_set)(prioqueue_t p, prioqueue_t o) \
M_C(name, _init_set)(prioqueue_t p, prioqueue_t const o) \
{ \
M_C(name, _array_init_set)(p->array, o->array); \
} \
\
static inline void \
M_C(name, _set)(prioqueue_t p, prioqueue_t o) \
M_C(name, _set)(prioqueue_t p, prioqueue_t const o) \
{ \
M_C(name, _array_set)(p->array, o->array); \
} \
@@ -157,13 +171,13 @@
} \
\
static inline bool \
M_C(name, _empty_p)(const prioqueue_t p) \
M_C(name, _empty_p)(prioqueue_t const p) \
{ \
return M_C(name, _array_empty_p)(p->array); \
} \
\
static inline size_t \
M_C(name, _size)(const prioqueue_t p) \
M_C(name, _size)(prioqueue_t const p) \
{ \
return M_C(name, _array_size)(p->array); \
} \
@@ -184,7 +198,7 @@
} \
\
static inline type const * \
M_C(name, _front)(prioqueue_t p) \
M_C(name, _front)(prioqueue_t const p) \
{ \
return M_C(name, _array_cget)(p->array, 0); \
} \
@@ -212,19 +226,19 @@
} \
\
static inline void \
M_C(name, _it)(it_t it, const prioqueue_t v) \
M_C(name, _it)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_last)(it_t it, const prioqueue_t v) \
M_C(name, _it_last)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it_last)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_end)(it_t it, const prioqueue_t v) \
M_C(name, _it_end)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it_end)(it, v->array); \
} \
@@ -279,6 +293,7 @@
M_C(name, _erase)(prioqueue_t p, type x) \
{ \
/* First pass: search for an item EQUAL to x */ \
/* NOTE: An HASHMAP may be a good idea to optimize this pass. */ \
size_t size = M_C(name, _array_size)(p->array); \
size_t i = 0; \
for(i = 0; i < size; i++) { \
@@ -342,5 +357,6 @@
, /* No EQUAL */ \) \
// TODO: set all & remove all function
#endif
+126 -43
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - RED BLACK TREE module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -30,26 +30,28 @@
/* Define a Red/Black binary tree of a given type.
USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */
#define RBTREE_DEF(name, ...) \
RBTREEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
RBTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), \
M_C(name, _t), struct M_C(name, _node_s), M_C(name, _it_t)), \
M_C(name, _t), M_C(name, _node_t), M_C(name, _it_t)), \
(name, __VA_ARGS__ , \
M_C(name, _t), struct M_C(name, _node_s), M_C(name, _it_t))))
M_C(name, _t), M_C(name, _node_t), M_C(name, _it_t))))
/* Define the oplist of a rbtree of type.
USAGE: RBTREE_OPLIST(name [, oplist_of_the_type]) */
#define RBTREE_OPLIST(...) \
RBTREEI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
RBTREEI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
// deferred evaluation
#define RBTREEI_OPLIST(arg) RBTREEI_OPLIST2 arg
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define RBTREEI_OPLIST_P1(arg) RBTREEI_OPLIST_P2 arg
#define RBTREEI_OPLIST2(name, oplist) \
/* OPLIST definition of a rbtree */
#define RBTREEI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
INIT_WITH(API_1(M_INIT_VAI)), \
@@ -61,6 +63,7 @@
TYPE(M_C(name,_t)), \
SUBTYPE(M_C(name, _type_t)), \
TEST_EMPTY(M_C(name,_empty_p)), \
GET_SIZE(M_C(name, _size)), \
IT_TYPE(M_C(name, _it_t)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name,_it_set)), \
@@ -80,6 +83,8 @@
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
@@ -127,16 +132,25 @@ typedef enum {
//TODO: UPDATE shall use a separate method than push
// deferred evaluation
#define RBTREEI_DEF(arg) RBTREEI_DEF2 arg
/* Deferred evaluation for the rbtree definition,
so that all arguments are evaluated before further expansion */
#define RBTREEI_DEF_P1(arg) RBTREEI_DEF_P2 arg
#define RBTREEI_DEF2(name, type, oplist, tree_t, node_t, tree_it_t) \
/* Internal rbtree definition
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- tree_t: alias for M_C(name, _t) [ type of the container ]
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
- node_t: alias for M_C(name, _node_t) [ node ]
*/
#define RBTREEI_DEF_P2(name, type, oplist, tree_t, node_t, it_t) \
\
node_t { \
node_t *child[2]; \
typedef struct M_C(name, _node_s) { \
struct M_C(name, _node_s) *child[2]; \
type data; \
rbtreei_color_e color; \
}; \
} node_t; \
\
typedef struct M_C(name, _s) { \
size_t size; \
@@ -147,9 +161,9 @@ typedef enum {
\
typedef struct M_C(name, _it_s) { \
node_t *stack[RBTREEI_MAX_STACK]; \
char which[RBTREEI_MAX_STACK]; \
int8_t which[RBTREEI_MAX_STACK]; \
unsigned int cpt; \
} tree_it_t[1]; \
} it_t[1]; \
\
typedef type M_C(name, _type_t); \
\
@@ -172,9 +186,9 @@ typedef enum {
M_CALL_DEL(oplist, ptr); \
} ) \
\
RBTREEI_DEF3(name, type, oplist, tree_t, node_t, tree_it_t)
RBTREEI_DEF_P3(name, type, oplist, tree_t, node_t, it_t)
#define RBTREEI_DEF3(name, type, oplist, tree_t, node_t, tree_it_t) \
#define RBTREEI_DEF_P3(name, type, oplist, tree_t, node_t, it_t) \
\
static inline void \
M_C(name, _init)(tree_t tree) \
@@ -231,7 +245,7 @@ typedef enum {
{ \
RBTREEI_CONTRACT(tree); \
node_t *tab[RBTREEI_MAX_STACK]; \
char which[RBTREEI_MAX_STACK]; \
int8_t which[RBTREEI_MAX_STACK]; \
unsigned int cpt = 0; \
node_t *n = tree->node; \
/* If nothing, create new node */ \
@@ -342,7 +356,7 @@ typedef enum {
tree->node = p; \
} else { \
assert (cpt >= 3); \
tab[cpt-3]->child[(int) which[cpt-3]] = p; \
tab[cpt-3]->child[which[cpt-3]] = p; \
} \
/* Done */ \
RBTREEI_CONTRACT (tree); \
@@ -356,7 +370,7 @@ typedef enum {
} \
\
static inline void \
M_C(name, _int_it)(tree_it_t it, const tree_t tree, int child) \
M_C(name, _int_it)(it_t it, const tree_t tree, int child) \
{ \
RBTREEI_CONTRACT (tree); \
assert (it != NULL); \
@@ -377,19 +391,19 @@ typedef enum {
} \
\
static inline void \
M_C(name, _it)(tree_it_t it, const tree_t tree) \
M_C(name, _it)(it_t it, const tree_t tree) \
{ \
M_C(name, _int_it)(it, tree, 0); \
} \
\
static inline void \
M_C(name, _it_last)(tree_it_t it, const tree_t tree) \
M_C(name, _it_last)(it_t it, const tree_t tree) \
{ \
M_C(name,_int_it)(it, tree, 1); \
} \
\
static inline void \
M_C(name, _it_end)(tree_it_t it, const tree_t tree) \
M_C(name, _it_end)(it_t it, const tree_t tree) \
{ \
RBTREEI_CONTRACT (tree); \
assert (it != NULL); \
@@ -397,21 +411,21 @@ typedef enum {
} \
\
static inline void \
M_C(name, _it_set)(tree_it_t it, const tree_it_t ref) \
M_C(name, _it_set)(it_t it, const it_t ref) \
{ \
assert (it != NULL && ref != NULL); \
*it = *ref; \
} \
\
static inline bool \
M_C(name, _end_p)(const tree_it_t it) \
M_C(name, _end_p)(const it_t it) \
{ \
assert (it != NULL); \
return it->cpt == 0; \
} \
\
static inline void \
M_C(name, _int_next)(tree_it_t it, int child) \
M_C(name, _int_next)(it_t it, int child) \
{ \
assert (it != NULL); \
assert (child == 0 || child == 1); \
@@ -442,19 +456,19 @@ typedef enum {
} \
\
static inline void \
M_C(name, _next)(tree_it_t it) \
M_C(name, _next)(it_t it) \
{ \
M_C(name, _int_next)(it, 0); \
} \
\
static inline void \
M_C(name, _previous)(tree_it_t it) \
M_C(name, _previous)(it_t it) \
{ \
M_C(name, _int_next)(it, 1); \
} \
\
static inline type * \
M_C(name, _ref)(const tree_it_t it) \
M_C(name, _ref)(const it_t it) \
{ \
assert(it != NULL && it->cpt > 0); \
/* NOTE: partially unsafe if the user modify the order of the el */ \
@@ -462,14 +476,14 @@ typedef enum {
} \
\
static inline type const * \
M_C(name, _cref)(const tree_it_t it) \
M_C(name, _cref)(const it_t it) \
{ \
return M_CONST_CAST(type, M_C(name, _ref)(it)); \
} \
\
static inline bool \
M_C(name, _it_equal_p)(const tree_it_t it1, \
const tree_it_t it2) \
M_C(name, _it_equal_p)(const it_t it1, \
const it_t it2) \
{ \
return it1->cpt == it2->cpt \
&& it1->stack[it1->cpt-1] == it2->stack[it2->cpt-1]; \
@@ -477,7 +491,7 @@ typedef enum {
\
\
static inline void \
M_C(name, _it_from)(tree_it_t it, \
M_C(name, _it_from)(it_t it, \
const tree_t tree, type const data) \
{ \
RBTREEI_CONTRACT (tree); \
@@ -499,7 +513,7 @@ typedef enum {
} \
\
static inline bool \
M_C(name, _it_to_p)(tree_it_t it, type const data) \
M_C(name, _it_until_p)(it_t it, type const data) \
{ \
assert (it != NULL); \
if (it->cpt == 0) return true; \
@@ -509,6 +523,23 @@ typedef enum {
return (cmp >= 0); \
} \
\
static inline bool \
M_C(name, _it_while_p)(it_t it, type const data) \
{ \
assert (it != NULL); \
if (it->cpt == 0) return true; \
assert (it->cpt > 0 && it->cpt < RBTREEI_MAX_STACK); \
node_t *n = it->stack[it->cpt-1]; \
int cmp = M_CALL_CMP(oplist, n->data, data); \
return (cmp <= 0); \
} \
\
static inline bool M_ATTR_DEPRECATED \
M_C(name, _it_to_p)(it_t it, type const data) \
{ \
return M_C(name, _it_until_p)(it, data); \
} \
\
static inline type * \
M_C(name, _min)(const tree_t tree) \
{ \
@@ -649,7 +680,7 @@ typedef enum {
return tree->size == 0; \
} \
\
/* Take care of the case n == NULL too */ \
/* Take care of the case n == NULL too */ \
static inline bool \
M_C(name, _int_is_black)(const node_t *n) \
{ \
@@ -692,7 +723,7 @@ typedef enum {
{ \
RBTREEI_CONTRACT (tree); \
node_t *tab[RBTREEI_MAX_STACK]; \
unsigned char which[RBTREEI_MAX_STACK]; \
int8_t which[RBTREEI_MAX_STACK]; \
unsigned int cpt = 0; \
node_t *n = tree->node; \
which[0] = 0; \
@@ -849,8 +880,8 @@ typedef enum {
RBTREEI_CONTRACT(t1); \
RBTREEI_CONTRACT(t2); \
if (t1->size != t2->size) return false; \
tree_it_t it1; \
tree_it_t it2; \
it_t it1; \
it_t it2; \
/* NOTE: We can't compare two tree directly as they can be \
structuraly different but functionnaly equal (you get this by \
constructing the tree in a different way). We have to \
@@ -877,7 +908,7 @@ typedef enum {
M_HASH_DECL(hash); \
/* NOTE: We can't compute the hash directly for the same reason \
than for EQUAL operator. */ \
tree_it_t it1; \
it_t it1; \
M_C(name, _it)(it1, t1); \
while (!M_C(name, _end_p)(it1)) { \
type const *ref1 = M_C(name, _cref)(it1); \
@@ -896,7 +927,7 @@ typedef enum {
(append ? string_cat_str : string_set_str) (str, "["); \
/* NOTE: The print is really naive, and not really efficient */ \
bool commaToPrint = false; \
tree_it_t it1; \
it_t it1; \
M_C(name, _it)(it1, t1); \
while (!M_C(name, _end_p)(it1)) { \
if (commaToPrint) \
@@ -917,7 +948,7 @@ typedef enum {
RBTREEI_CONTRACT(rbtree); \
assert (file != NULL); \
fputc ('[', file); \
tree_it_t it; \
it_t it; \
bool commaToPrint = false; \
for (M_C(name, _it)(it, rbtree) ; \
!M_C(name, _end_p)(it); \
@@ -978,7 +1009,7 @@ typedef enum {
type item; \
M_CALL_INIT(oplist, item); \
do { \
bool b = M_CALL_IN_STR(oplist, item, file); \
bool b = M_CALL_IN_STR(oplist, item, file); \
do { c = fgetc(file); } while (isspace(c)); \
if (b == false || c == EOF) break; \
M_C(name, _push)(rbtree, item); \
@@ -988,6 +1019,58 @@ typedef enum {
} \
, /* no in_str */ ) \
\
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
{ \
RBTREEI_CONTRACT(t1); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
const M_C(name, _type_t) *item; \
bool first_done = false; \
it_t it; \
ret = f->m_interface->write_array_start(local, f, t1->size); \
for (M_C(name, _it)(it, t1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
item = M_C(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
first_done = true; \
} \
ret |= f->m_interface->write_array_end(local, f); \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD(IN_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(tree_t t1, m_serial_read_t f) \
{ \
RBTREEI_CONTRACT(t1); \
assert (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
type key; \
M_C(name,_clean)(t1); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(oplist, key); \
do { \
ret = M_CALL_IN_SERIAL(oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push)(t1, key); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(oplist, key); \
return ret; \
} \
, /* no in_serial */ ) \
\
\
// TODO: specialized _sort shall do nothing, but shall check the requested order. How ?
+506
View File
@@ -0,0 +1,506 @@
/*
* M*LIB - Serial BIN
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_SERIAL_BIN_H
#define MSTARLIB_SERIAL_BIN_H
#include <stdint.h>
#include "m-core.h"
#include "m-string.h"
/* Write the boolean 'data' into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_write_boolean(m_serial_write_t serial, const bool data)
{
FILE *f = (FILE *)serial->data[0].p;
size_t n = fwrite (M_ASSIGN_CAST(const void*, &data), sizeof (bool), 1, f);
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
{
int8_t i8;
int16_t i16;
int32_t i32;
int64_t i64;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
switch (size_of_type) {
case 1:
i8 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &i8), sizeof i8, 1, f);
break;
case 2:
i16 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &i16), sizeof i16, 1, f);
break;
case 4:
i32 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &i32), sizeof i32, 1, f);
break;
case 8:
i64 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &i64), sizeof i64, 1, f);
break;
default:
M_ASSERT_INIT(false, "an integer of suitable size");
break;
}
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
{
float f1;
double f2;
long double f3;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
switch (size_of_type) {
case sizeof f1:
f1 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &f1), sizeof f1, 1, f);
break;
case sizeof f2:
f2 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &f2), sizeof f2, 1, f);
break;
case sizeof f3:
f3 = data;
n = fwrite (M_ASSIGN_CAST(const void*, &f3), sizeof f3, 1, f);
break;
default:
M_ASSERT_INIT(false, "a float of suitable size");
break;
}
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Write the null-terminated string 'data'into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_write_string(m_serial_write_t serial, const char data[])
{
FILE *f = (FILE *)serial->data[0].p;
size_t l = strlen(data);
size_t n = fwrite (M_ASSIGN_CAST(const void*, data), 1, l+1, f);
return n == l+1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 unkown.
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 */
static inline m_serial_return_code_t
m_serial_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
FILE *f = (FILE *)serial->data[0].p;
size_t n = fwrite (M_ASSIGN_CAST(const void*, &number_of_elements), sizeof number_of_elements, 1, f);
local->data[0].b = (number_of_elements == 0);
return n == 1 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial)
{
FILE *f = (FILE *)serial->data[0].p;
// Need separator if we don't know the real size
if (local->data[0].b) {
size_t n = 0xABCDEF;
n = fwrite (M_ASSIGN_CAST(const void*, &n), sizeof n, 1, f);
return n == 1 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
} else {
return M_SERIAL_OK_CONTINUE;
}
}
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial)
{
FILE *f = (FILE *)serial->data[0].p;
// Need mark if we don't know the real size
if (local->data[0].b) {
size_t n = 0x12345678;
n = fwrite (M_ASSIGN_CAST(const void*, &n), sizeof n, 1, f);
return n == 1 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
} else {
return M_SERIAL_OK_CONTINUE;
}
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_CONTINUE;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_CONTINUE;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
(void) local; // argument not used
(void) serial;
(void) field_name;
(void) max;
(void) index; // Assume index are write in order from 0 to max.
return M_SERIAL_OK_CONTINUE;
}
/* End the write of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_DONE;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_bin_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
(void) field_name;
(void) max;
(void) local;
FILE *f = (FILE *)serial->data[0].p;
size_t n = fwrite (M_ASSIGN_CAST(const void*, &index), sizeof index, 1, f);
return n == 1 ? ((index < 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE) : M_SERIAL_FAIL;
}
/* End Writing a variant into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_DONE;
}
/* The exported interface. */
static const m_serial_write_interface_t m_serial_write_bin_interface = {
m_serial_bin_write_boolean,
m_serial_bin_write_integer,
m_serial_bin_write_float,
m_serial_bin_write_string,
m_serial_bin_write_array_start,
m_serial_bin_write_array_next,
m_serial_bin_write_array_end,
m_serial_bin_write_array_start,
m_serial_bin_write_map_value,
m_serial_bin_write_array_next,
m_serial_bin_write_array_end,
m_serial_bin_write_tuple_start,
m_serial_bin_write_tuple_id,
m_serial_bin_write_tuple_end,
m_serial_bin_write_variant_start,
m_serial_bin_write_variant_end
};
static inline void m_serial_bin_write_init(m_serial_write_t serial, FILE *f)
{
serial->m_interface = &m_serial_write_bin_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
static inline void m_serial_bin_write_clear(m_serial_write_t serial)
{
(void) serial; // Nothing to do
}
/* Read from the stream 'serial' a boolean.
Set '*b' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_boolean(m_serial_read_t serial, bool *b){
FILE *f = (FILE*) serial->data[0].p;
size_t n = fread (M_ASSIGN_CAST(void*, b), sizeof (bool), 1, f);
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes.
Set '*i' with the integer value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){
int8_t i8;
int16_t i16;
int32_t i32;
int64_t i64;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
switch (size_of_type) {
case 1:
n = fread (M_ASSIGN_CAST(void*, &i8), sizeof i8, 1, f);
*i = i8;
break;
case 2:
n = fread (M_ASSIGN_CAST(void*, &i16), sizeof i16, 1, f);
*i = i16;
break;
case 4:
n = fread (M_ASSIGN_CAST(void*, &i32), sizeof i32, 1, f);
*i = i32;
break;
case 8:
n = fread (M_ASSIGN_CAST(void*, &i64), sizeof i64, 1, f);
*i = i64;
break;
default:
M_ASSERT_INIT(false, "an integer of suitable size");
break;
}
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes.
Set '*r' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){
float f1;
double f2;
long double f3;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
switch (size_of_type) {
case sizeof f1:
n = fread (M_ASSIGN_CAST(void*, &f1), sizeof f1, 1, f);
*r = f1;
break;
case sizeof f2:
n = fread (M_ASSIGN_CAST(void*, &f2), sizeof f2, 1, f);
*r = f2;
break;
case sizeof f3:
n = fread (M_ASSIGN_CAST(void*, &f3), sizeof f3, 1, f);
*r = f3;
break;
default:
M_ASSERT_INIT(false, "a float of suitable size");
break;
}
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Read from the stream 'serial' a string.
Set 's' with the string if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_string(m_serial_read_t serial, struct string_s *s){
FILE *f = (FILE*) serial->data[0].p;
string_clean(s);
int c;
c = fgetc(f);
while (c != 0 && c != EOF) {
string_push_back(s, c);
c = fgetc(f);
}
return c != EOF ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Start reading from the stream 'serial' an 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 array
(local is an unique serialization object of the array).
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
FILE *f = (FILE*) serial->data[0].p;
size_t n = fread (M_ASSIGN_CAST(void*, num), sizeof *num, 1, f);
if (n != 1)
return M_SERIAL_FAIL;
local->data[0].b = (*num == 0);
local->data[1].s = *num;
if (local->data[0].b) {
// Size not know ==> use of marker in the stream.
size_t p;
n = fread (M_ASSIGN_CAST(void*, &p), sizeof p, 1, f);
if (n != 1)
return M_SERIAL_FAIL;
return p == 0xABCDEF ? M_SERIAL_OK_CONTINUE : p == 0x12345678 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
return M_SERIAL_OK_CONTINUE;
}
/* Continue reading from the stream 'serial' an array.
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_array_next(m_serial_local_t local, m_serial_read_t serial)
{
FILE *f = (FILE*) serial->data[0].p;
if (local->data[0].b) {
// Size not know ==> use of marker in the stream.
size_t p, n;
n = fread (M_ASSIGN_CAST(void*, &p), sizeof p, 1, f);
if (n != 1)
return M_SERIAL_FAIL;
return p == 0xABCDEF ? M_SERIAL_OK_CONTINUE : p == 0x12345678 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
} else {
assert(local->data[1].s > 0);
local->data[1].s --;
return local->data[1].s == 0 ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE;
}
}
/* Continue reading from the stream 'serial' the value separator
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_map_value(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_CONTINUE;
}
/* Start reading a tuple from the stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
{
(void) serial;
local->data[1].s = 0;
return M_SERIAL_OK_CONTINUE;
}
/* Continue reading a tuple 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 tuple continues,
Return M_SERIAL_OK_DONE if it succeeds and the tuple ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id)
{
(void) serial;
(void) field_name;
(void) max;
*id = local->data[1].s;
local->data[1].s ++;
return (*id == max) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE;
}
/* Start reading a variant 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 */
static inline m_serial_return_code_t
m_serial_bin_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id)
{
(void) field_name;
(void) max;
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
size_t n = fread (M_ASSIGN_CAST(void*, id), sizeof *id, 1, f);
return n == 1 ? ((*id < 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE) : M_SERIAL_FAIL;
}
/* End reading a variant from the stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds and the variant ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_bin_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_DONE;
}
static const m_serial_read_interface_t m_serial_bin_read_interface = {
m_serial_bin_read_boolean,
m_serial_bin_read_integer,
m_serial_bin_read_float,
m_serial_bin_read_string,
m_serial_bin_read_array_start,
m_serial_bin_read_array_next,
m_serial_bin_read_array_start,
m_serial_bin_read_map_value,
m_serial_bin_read_array_next,
m_serial_bin_read_tuple_start,
m_serial_bin_read_tuple_id,
m_serial_bin_read_variant_start,
m_serial_bin_read_variant_end
};
static inline void m_serial_bin_read_init(m_serial_read_t serial, FILE *f)
{
serial->m_interface = &m_serial_bin_read_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
static inline void m_serial_bin_read_clear(m_serial_read_t serial)
{
(void) serial; // Nothing to do
}
#endif
+542
View File
@@ -0,0 +1,542 @@
/*
* M*LIB - Serial JSON
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_SERIAL_JSON_H
#define MSTARLIB_SERIAL_JSON_H
#include "m-core.h"
#include "m-string.h"
/* Write the boolean 'data' into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_write_boolean(m_serial_write_t serial, const bool data)
{
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%s", data ? "true" : "false");
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
{
(void) size_of_type; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%lld", data);
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
{
(void) size_of_type; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%Lf", data);
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Write the null-terminated string 'data'into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_write_string(m_serial_write_t serial, const char data[])
{
FILE *f = (FILE *)serial->data[0].p;
/* Build dummy string to reuse string_out_str */
string_t v2;
uintptr_t ptr = (uintptr_t) data;
v2->u.heap.size = strlen(data);
v2->u.heap.alloc = v2->u.heap.size + 1;
v2->ptr = (char*)ptr;
string_out_str(f, v2);
return M_SERIAL_OK_DONE;
}
/* 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 unkown.
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 */
static inline m_serial_return_code_t
m_serial_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
(void) local; // argument not used
(void) number_of_elements; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "[");
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, ",");
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "]");
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 unkown.
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 */
static inline m_serial_return_code_t
m_serial_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
(void) local; // argument not used
(void) number_of_elements; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "{");
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, ":");
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, ",");
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* End the writing of a map into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "}");
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "{");
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
(void) local; // argument not used
(void) max; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]);
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* End the write of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "}");
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* 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 */
static inline m_serial_return_code_t
m_serial_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
(void) local; // argument not used
(void) max; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n;
if (index >= 0) {
assert (index < max);
n = fprintf(f, "{\"%s\":", field_name[index]);
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
} else {
n = fprintf(f, "{}");
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
}
/* End Writing a variant into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "}");
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* The exported interface. */
static const m_serial_write_interface_t m_serial_write_json_interface = {
m_serial_json_write_boolean,
m_serial_json_write_integer,
m_serial_json_write_float,
m_serial_json_write_string,
m_serial_json_write_array_start,
m_serial_json_write_array_next,
m_serial_json_write_array_end,
m_serial_json_write_map_start,
m_serial_json_write_map_value,
m_serial_json_write_map_next,
m_serial_json_write_map_end,
m_serial_json_write_tuple_start,
m_serial_json_write_tuple_id,
m_serial_json_write_tuple_end,
m_serial_json_write_variant_start,
m_serial_json_write_variant_end
};
static inline void m_serial_json_write_init(m_serial_write_t serial, FILE *f)
{
serial->m_interface = &m_serial_write_json_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
static inline void m_serial_json_write_clear(m_serial_write_t serial)
{
(void) serial; // Nothing to do
}
/* Helper function */
static inline int
m_serial_json_read_skip (FILE *f)
{
int c;
do {
c = fgetc(f);
} while (isspace(c));
return c;
}
/* Read from the stream 'serial' a boolean.
Set '*b' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_boolean(m_serial_read_t serial, bool *b){
FILE *f = (FILE*) serial->data[0].p;
int c = m_serial_json_read_skip(f);
if (c == 't') {
*b = true;
c = fgetc(f);
if (c != 'r') return M_SERIAL_FAIL;
c = fgetc(f);
if (c != 'u') return M_SERIAL_FAIL;
c = fgetc(f);
if (c != 'e') return M_SERIAL_FAIL;
return M_SERIAL_OK_DONE;
} else if (c == 'f') {
*b = false;
c = fgetc(f);
if (c != 'a') return M_SERIAL_FAIL;
c = fgetc(f);
if (c != 'l') return M_SERIAL_FAIL;
c = fgetc(f);
if (c != 's') return M_SERIAL_FAIL;
c = fgetc(f);
if (c != 'e') return M_SERIAL_FAIL;
return M_SERIAL_OK_DONE;
} else {
return M_SERIAL_FAIL;
}
}
/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes.
Set '*i' with the integer value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){
(void) size_of_type; // Ignored
FILE *f = (FILE*) serial->data[0].p;
return fscanf(f, " %lld", i) == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes.
Set '*r' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){
(void) size_of_type; // Ignored
FILE *f = (FILE*) serial->data[0].p;
return fscanf(f, " %Lf", r) == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Read from the stream 'serial' a string.
Set 's' with the string if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_string(m_serial_read_t serial, struct string_s *s){
FILE *f = (FILE*) serial->data[0].p;
fscanf(f, " "); // Skip any leading spaces.
return string_in_str(s, f) ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Start reading from the stream 'serial' an 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 array
(local is an unique serialization object of the array).
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int final1 = -1, final2 = -1;
fscanf(f, " [%n ]%n", &final1, &final2);
*num = 0; // don't know the size of the array.
// Test how much the parsing succeed.
return (final1 < 0) ? M_SERIAL_FAIL : (final2 < 0) ? M_SERIAL_OK_CONTINUE : M_SERIAL_OK_DONE;
}
/* Continue reading from the stream 'serial' an array.
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_array_next(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
char c = 0;
fscanf(f, " %c", &c);
return c == ',' ? M_SERIAL_OK_CONTINUE : c == ']' ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Start reading from the stream 'serial' a map.
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 array
(local is an unique serialization object of the array).
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 */
static inline m_serial_return_code_t
m_serial_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int final1 = -1, final2 = -1;
fscanf(f, " {%n }%n", &final1, &final2);
*num = 0; // don't know the size of the map.
// Test how much the parsing succeed.
return (final1 < 0) ? M_SERIAL_FAIL : (final2 < 0) ? M_SERIAL_OK_CONTINUE : M_SERIAL_OK_DONE;
}
/* Continue reading from the stream 'serial' the value separator
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_map_value(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int final = -1;
fscanf(f, " :%n", &final);
return final > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* Continue reading from the stream 'serial' a map.
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 */
static inline m_serial_return_code_t
m_serial_json_read_map_next(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
char c = 0;
fscanf(f, " %c", &c);
return c == ',' ? M_SERIAL_OK_CONTINUE : c == '}' ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
/* Start reading a tuple from the stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int final = -1;
fscanf(f, " {%n", &final);
return final > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
}
/* Continue reading a tuple 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 tuple continues,
Return M_SERIAL_OK_DONE if it succeeds and the tuple ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int c = m_serial_json_read_skip(f);
if (c == EOF) return M_SERIAL_FAIL;
if (c == '}') return M_SERIAL_OK_DONE;
if (c == ',') {
// If first call of read_tuple_id, it is a failure
if (*id == -1) return M_SERIAL_FAIL;
} else {
// c should be \" but let fscanf parse it.
ungetc(c, f);
}
/* Read the field in the JSON */
c = -1;
char field[M_MAX_IDENTIFIER_LENGTH+1]; // TODO: fix error detection below.
fscanf(f, " \"%" M_APPLY(M_AS_STR, M_MAX_IDENTIFIER_LENGTH) "[^ \t\n\"]\":%n", field, &c);
if (c <= 0)
return M_SERIAL_FAIL;
/* Search for field in field_name */
for(int n = 0; n < max; n++) {
if (strcmp(field, field_name[n]) == 0) {
*id = n;
return M_SERIAL_OK_CONTINUE;
}
}
return M_SERIAL_FAIL;
}
/* Start reading a variant 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 */
static inline m_serial_return_code_t
m_serial_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int final1 = -1, final2 = -1;
fscanf(f, " {%n }%n", &final1, &final2);
// Test how much the parsing succeed.
if (final1 <= 0) return M_SERIAL_FAIL;
if (final2 > 0) return M_SERIAL_OK_DONE;
/* Read the field in the JSON */
char field[M_MAX_IDENTIFIER_LENGTH+1];
fscanf(f, " \"%" M_APPLY(M_AS_STR, M_MAX_IDENTIFIER_LENGTH) "[^ \t\n\"]\":%n", field, &final2);
if (final2 <= 0) return M_SERIAL_FAIL;
/* Search for field in field_name */
for(int n = 0; n < max; n++) {
if (strcmp(field, field_name[n]) == 0) {
*id = n;
return M_SERIAL_OK_CONTINUE;
}
}
return M_SERIAL_FAIL;
}
/* End reading a variant from the stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds and the variant ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
m_serial_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
FILE *f = (FILE*) serial->data[0].p;
int final = -1;
fscanf(f, " }%n", &final);
return final > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
}
static const m_serial_read_interface_t m_serial_json_read_interface = {
m_serial_json_read_boolean,
m_serial_json_read_integer,
m_serial_json_read_float,
m_serial_json_read_string,
m_serial_json_read_array_start,
m_serial_json_read_array_next,
m_serial_json_read_map_start,
m_serial_json_read_map_value,
m_serial_json_read_map_next,
m_serial_json_read_tuple_start,
m_serial_json_read_tuple_id,
m_serial_json_read_variant_start,
m_serial_json_read_variant_end
};
static inline void m_serial_json_read_init(m_serial_read_t serial, FILE *f)
{
serial->m_interface = &m_serial_json_read_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
static inline void m_serial_json_read_clear(m_serial_read_t serial)
{
(void) serial; // Nothing to do
}
#endif
+20 -20
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SHARED Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -32,40 +32,40 @@
/* Define the oplist of a shared pointer.
USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */
#define SHARED_PTR_OPLIST(...) \
SHAREDI_PTR_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
_OPLIST(__VA_ARGS__ )))
SHAREDI_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
_OPLIST(__VA_ARGS__ )))
/* Define shared pointer and its function.
USAGE: SHARED_PTR_DEF(name, type, [, oplist]) */
#define SHARED_PTR_DEF(name, ...) \
SHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_ATOMIC_OPLIST ), \
(name, __VA_ARGS__ , SHAREDI_ATOMIC_OPLIST)))
SHAREDI_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_ATOMIC_OPLIST ), \
(name, __VA_ARGS__ , SHAREDI_ATOMIC_OPLIST)))
/* Define relaxed shared pointer and its function (thread unsafe).
USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
#define SHARED_PTR_RELAXED_DEF(name, ...) \
SHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_INTEGER_OPLIST ), \
(name, __VA_ARGS__ , SHAREDI_INTEGER_OPLIST)))
SHAREDI_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_INTEGER_OPLIST ), \
(name, __VA_ARGS__ , SHAREDI_INTEGER_OPLIST)))
/* Define shared resource and its function.
This is a bounded pool of resource shared by multiple owners.
USAGE: SHARED_RESOURCE_DEF(name, type, [, oplist]) */
#define SHARED_RESOURCE_DEF(name, ...) \
SHAREDI_RESOURCE_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
(name, __VA_ARGS__)))
SHAREDI_RESOURCE_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
(name, __VA_ARGS__)))
/********************************** INTERNAL ************************************/
// deferred
#define SHAREDI_PTR_OPLIST(arg) SHAREDI_PTR_OPLIST2 arg
#define SHAREDI_PTR_OPLIST_P1(arg) SHAREDI_PTR_OPLIST_P2 arg
#define SHAREDI_PTR_OPLIST2(name, oplist) ( \
#define SHAREDI_PTR_OPLIST_P2(name, oplist) ( \
INIT(M_C(name, _init)), \
CLEAR(M_C(name, _clear)), \
INIT_SET(M_C(name, _init_set)), \
@@ -80,7 +80,7 @@
)
// deferred
#define SHAREDI_PTR_DEF(arg) SHAREDI_PTR_DEF2 arg
#define SHAREDI_PTR_DEF_P1(arg) SHAREDI_PTR_DEF_P2 arg
// OPLIST to handle a counter of atomic type
#define SHAREDI_ATOMIC_OPLIST (TYPE(atomic_int), \
@@ -105,7 +105,7 @@ static inline int sharedi_integer_cref(int *p) { return *p; }
assert(*shared == NULL || M_CALL_IT_CREF(cpt_oplist, &(*shared)->cpt) >= 1); \
} while (0)
#define SHAREDI_PTR_DEF2(name, type, oplist, cpt_oplist) \
#define SHAREDI_PTR_DEF_P2(name, type, oplist, cpt_oplist) \
\
typedef struct M_C(name, _s){ \
type *data; /* Pointer to the data */ \
@@ -297,15 +297,15 @@ static inline int sharedi_integer_cref(int *p) { return *p; }
} while (0)
// deferred
#define SHAREDI_RESOURCE_DEF(arg) SHAREDI_RESOURCE_DEF2 arg
#define SHAREDI_RESOURCE_DEF_P1(arg) SHAREDI_RESOURCE_DEF_P2 arg
#define SHAREDI_RESOURCE_DEF2(name, type, oplist) \
#define SHAREDI_RESOURCE_DEF_P2(name, type, oplist) \
\
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _atype_s) { \
atomic_uint cpt; \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) +sizeof(atomic_uint) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) -sizeof(atomic_uint) : 1]; \
M_CACHELINE_ALIGN(align, type, atomic_uint); \
} M_C(name, _atype_t); \
\
typedef struct M_C(name, _s) { \
+40 -31
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SNAPSHOT Module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -32,39 +32,39 @@
/* Define a Single Producer Single Consummer snapshot and its functions
USAGE: SNAPSHOT_SPSC_DEF(name, type[, oplist]) */
#define SNAPSHOT_SPSC_DEF(name, ...) \
SNAPSHOTI_SPSC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
SNAPSHOTI_SPSC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
/* Define a Single Producer Multiple Consummer snapshot and its functions
USAGE: SNAPSHOT_SPMC_DEF(name, type[, oplist]) */
#define SNAPSHOT_SPMC_DEF(name, ...) \
SNAPSHOTI_SPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
SNAPSHOTI_SPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
/* Define a Multiple Producer Multiple Consummer snapshot and its functions
USAGE: SNAPSHOT_MPMC_DEF(name, type[, oplist]) */
#define SNAPSHOT_MPMC_DEF(name, ...) \
SNAPSHOTI_MPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
SNAPSHOTI_MPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(name, __VA_ARGS__ )))
/* Define the oplist of a snapshot (SPSC, SPMC or MPMC).
USAGE: SNAPSHOT_OPLIST(name[, oplist]) */
#define SNAPSHOT_OPLIST(...) \
SNAPSHOTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(__VA_ARGS__ )))
SNAPSHOTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
// deferred evaluation of the input
#define SNAPSHOTI_OPLIST(arg) SNAPSHOTI_OPLIST2 arg
#define SNAPSHOTI_OPLIST_P1(arg) SNAPSHOTI_OPLIST_P2 arg
/* Define the oplist of a snapshot */
#define SNAPSHOTI_OPLIST2(name, oplist) \
#define SNAPSHOTI_OPLIST_P2(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,SET(M_C(name, _set)) \
@@ -84,6 +84,7 @@
* - w: Index of the write buffer Range [0..2]
* - f: Next index of the write buffer when a shot is taken Range [0..2]
* - b: Boolean indicating that the read buffer shall be updated
* all fields packed in an unsigned char type.
*/
#define SNAPSHOTI_SPSC_FLAG(r, w, f, b) \
((unsigned char)( ( (r) << 4) | ((w) << 2) | ((f)) | ((b) << 6)))
@@ -108,19 +109,19 @@
SNAPSHOTI_SPSC_FLAGS_CONTRACT(f); \
} while (0)
// Defered evaluation (TBC if it really helps).
#define SNAPSHOTI_SPSC_DEF(arg) SNAPSHOTI_SPSC_DEF2 arg
// Defered evaluation of the arguments.
#define SNAPSHOTI_SPSC_DEF_P1(arg) SNAPSHOTI_SPSC_DEF_P2 arg
// This is basically an atomic triple buffer (Lock Free)
// between a produced thread and a consummer thread.
// A snapshot is basically an atomic triple buffer (Lock Free)
// between a single producer thread and a single consummer thread.
#define SNAPSHOTI_SPSC_MAX_BUFFER 3
#define SNAPSHOTI_SPSC_DEF2(name, type, oplist) \
#define SNAPSHOTI_SPSC_DEF_P2(name, type, oplist) \
\
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _aligned_type_s) { \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
M_CACHELINE_ALIGN(align, type); \
} M_C(name, _aligned_type_t); \
\
typedef struct M_C(name, _s) { \
@@ -269,6 +270,7 @@
} \
/******************************** INTERNAL SPMC **********************************/
#define SNAPSHOTI_SPMC_INT_FLAG(w, n) ( ((w) << 1) | (n) )
@@ -280,7 +282,13 @@
#define SNAPSHOTI_SPMC_MAX_READER (GENINT_MAX_ALLOC-SNAPSHOTI_SPMC_EXTRA_BUFFER)
// structure to handle SPMC snapshot but return a unique index in a buffer array.
/* structure to handle SPMC snapshot but return an unique index in the buffer array.
- lastNext: last published written index + next flag (format SNAPSHOTI_SPMC_INT_FLAG)
- currentWrite: the index being currently written.
- n : number of readers
- cptTab: ref counter array
- freeList: a pool of free integers.
*/
typedef struct snapshot_mrsw_int_s {
atomic_uint lastNext;
unsigned int currentWrite;
@@ -343,7 +351,7 @@ static inline unsigned int snapshot_mrsw_int_get_write_idx(snapshot_mrsw_int_t s
static inline unsigned int snapshot_mrsw_int_size(snapshot_mrsw_int_t s)
{
SNAPSHOTI_SPMC_INT_CONTRACT(s);
return s->n - SNAPSHOTI_SPMC_EXTRA_BUFFER;
return s->n;
}
static inline unsigned int snapshot_mrsw_int_write_idx(snapshot_mrsw_int_t s, unsigned int idx)
@@ -479,15 +487,15 @@ static inline void snapshot_mrsw_int_read_end(snapshot_mrsw_int_t s, unsigned in
} while (0)
// Defered evaluation (TBC if it really helps).
#define SNAPSHOTI_SPMC_DEF(arg) SNAPSHOTI_SPMC_DEF2 arg
// Defered evaluation
#define SNAPSHOTI_SPMC_DEF_P1(arg) SNAPSHOTI_SPMC_DEF_P2 arg
#define SNAPSHOTI_SPMC_DEF2(name, type, oplist) \
#define SNAPSHOTI_SPMC_DEF_P2(name, type, oplist) \
\
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _aligned_type_s) { \
type x; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
M_CACHELINE_ALIGN(align, type); \
} M_C(name, _aligned_type_t); \
\
typedef struct M_C(name, _s) { \
@@ -563,16 +571,17 @@ static inline void snapshot_mrsw_int_read_end(snapshot_mrsw_int_t s, unsigned in
//FIXME: Evaluate the needs for the methods _set_, _init_set.
/******************************** INTERNAL MPMC **********************************/
// MPMC is built upon SPMC
// Defered evaluation (TBC if it really helps).
#define SNAPSHOTI_MPMC_DEF(arg) SNAPSHOTI_MPMC_DEF2 arg
// Defered evaluation
#define SNAPSHOTI_MPMC_DEF_P1(arg) SNAPSHOTI_MPMC_DEF_P2 arg
#define SNAPSHOTI_MPMC_DEF2(name, type, oplist) \
#define SNAPSHOTI_MPMC_DEF_P2(name, type, oplist) \
\
SNAPSHOTI_SPMC_DEF((M_C(name, _mrsw), type, oplist)) \
SNAPSHOTI_SPMC_DEF_P1((M_C(name, _mrsw), type, oplist)) \
\
typedef struct M_C(name, _s) { \
M_C(name, _mrsw_t) core; \
+567 -293
View File
File diff suppressed because it is too large. Load diff
+77 -8
View File
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* M*LIB - TUPLE module
*
* Copyright (c) 2017-2019, 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:
@@ -31,14 +33,14 @@
USAGE:
TUPLE_DEF2(name, [(field1, type1[, oplist1]), (field2, type2[, oplist2]), ...] ) */
#define TUPLE_DEF2(name, ...) \
TUPLEI_DEF2_A( (name, TUPLEI_INJECT_GLOBAL(__VA_ARGS__)) )
TUPLEI_DEF2_P1( (name, TUPLEI_INJECT_GLOBAL(__VA_ARGS__)) )
/* Define the oplist of a tuple.
USAGE: TUPLE_OPLIST(name[, oplist of the first type, ...]) */
#define TUPLE_OPLIST(...) \
M_IF_NARGS_EQ1(__VA_ARGS__) \
(TUPLEI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \
TUPLEI_OPLIST(__VA_ARGS__ ))
(TUPLEI_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST )), \
TUPLEI_OPLIST_P1((__VA_ARGS__ )))
/* Return an array suitable for the _cmp_order function.
As compound literals are not supported in C++,
@@ -78,10 +80,10 @@ namespace m_tuple {
#define TUPLEI_INJECT_OPLIST_B( f, ... ) \
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
#define TUPLEI_DEF2_A(...) \
TUPLEI_DEF2_B __VA_ARGS__
// Deferred evaluation
#define TUPLEI_DEF2_P1(...) TUPLEI_DEF2_P2 __VA_ARGS__
#define TUPLEI_DEF2_B(name, ...) \
#define TUPLEI_DEF2_P2(name, ...) \
TUPLE_DEFINE_TYPE(name, __VA_ARGS__) \
TUPLE_DEFINE_ENUM(name, __VA_ARGS__) \
TUPLEI_IF_ALL(INIT, __VA_ARGS__) \
@@ -110,6 +112,10 @@ namespace m_tuple {
(TUPLE_DEFINE_IN_STR(name, __VA_ARGS__),) \
TUPLEI_IF_ALL(PARSE_STR, __VA_ARGS__) \
(TUPLE_DEFINE_PARSE_STR(name, __VA_ARGS__),) \
TUPLEI_IF_ALL(OUT_SERIAL, __VA_ARGS__) \
(TUPLE_DEFINE_OUT_SERIAL(name, __VA_ARGS__),) \
TUPLEI_IF_ALL(IN_SERIAL, __VA_ARGS__) \
(TUPLE_DEFINE_IN_SERIAL(name, __VA_ARGS__),) \
TUPLEI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
(TUPLE_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
TUPLEI_IF_ALL(MOVE, __VA_ARGS__) \
@@ -140,6 +146,8 @@ namespace m_tuple {
#define TUPLE_GET_STR(f,t,o) M_GET_GET_STR o
#define TUPLE_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
#define TUPLE_GET_IN_STR(f,t,o) M_GET_IN_STR o
#define TUPLE_GET_OUT_SERIAL(f,t,o) M_GET_OUT_SERIAL o
#define TUPLE_GET_IN_SERIAL(f,t,o) M_GET_IN_SERIAL o
#define TUPLE_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
#define TUPLE_GET_SWAP(f,t,o) M_GET_SWAP o
#define TUPLE_GET_CLEAN(f,t,o) M_GET_CLEAN o
@@ -157,6 +165,8 @@ namespace m_tuple {
#define TUPLE_CALL_OUT_STR(t, ...) M_APPLY_API(TUPLE_GET_OUT_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
#define TUPLE_CALL_IN_STR(t, ...) M_APPLY_API(TUPLE_GET_IN_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
#define TUPLE_CALL_PARSE_STR(t, ...) M_APPLY_API(TUPLE_GET_PARSE_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
#define TUPLE_CALL_OUT_SERIAL(t, ...) M_APPLY_API(TUPLE_GET_OUT_SERIAL t, TUPLE_GET_OPLIST t, __VA_ARGS__)
#define TUPLE_CALL_IN_SERIAL(t, ...) M_APPLY_API(TUPLE_GET_IN_SERIAL t, TUPLE_GET_OPLIST t, __VA_ARGS__)
#define TUPLE_CALL_SWAP(t, ...) M_APPLY_API(TUPLE_GET_SWAP t, TUPLE_GET_OPLIST t, __VA_ARGS__)
#define TUPLE_CALL_CLEAN(t, ...) M_APPLY_API(TUPLE_GET_CLEAN t, TUPLE_GET_OPLIST t, __VA_ARGS__)
@@ -399,6 +409,60 @@ namespace m_tuple {
if (TUPLE_CALL_PARSE_STR(a, el -> TUPLE_GET_FIELD a, str, &str) == false) \
goto exit ; \
#define TUPLE_STRINGIFY_NAME(a) \
M_APPLY(M_AS_STR, TUPLE_GET_FIELD a)
#define TUPLE_DEFINE_OUT_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, \
M_C(name,_t) const el) { \
assert (f != NULL && f->m_interface != NULL && el != NULL); \
const int field_max = M_NARGS(__VA_ARGS__); \
static const char *const field_name[] = \
{ M_REDUCE(TUPLE_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
int index = 0; \
m_serial_local_t local; \
m_serial_return_code_t ret; \
ret = f->m_interface->write_tuple_start(local, f); \
M_MAP(TUPLE_DEFINE_OUT_SERIAL_FUNC , __VA_ARGS__) \
assert( index == field_max); \
ret |= f->m_interface->write_tuple_end(local, f); \
return ret & M_SERIAL_FAIL; \
}
#define TUPLE_DEFINE_OUT_SERIAL_FUNC(a) \
f->m_interface->write_tuple_id(local, f, field_name, field_max, index); \
TUPLE_CALL_OUT_SERIAL(a, f, el -> TUPLE_GET_FIELD a); \
index++; \
#define TUPLE_DEFINE_IN_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(M_C(name,_t) el, m_serial_read_t f) { \
assert (f != NULL && el != NULL); \
int index = -1; \
const int field_max = M_NARGS(__VA_ARGS__); \
static const char *const field_name[] = \
{ M_REDUCE(TUPLE_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
m_serial_local_t local; \
m_serial_return_code_t ret; \
ret = f->m_interface->read_tuple_start(local, f); \
while (ret == M_SERIAL_OK_CONTINUE) { \
ret = f->m_interface->read_tuple_id(local, f, field_name, field_max, &index); \
if (ret == M_SERIAL_OK_CONTINUE) { \
assert (index >= 0 && index < field_max); \
switch (1+index) { \
M_MAP2(TUPLE_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \
default: assert(0); \
} \
ret = (ret == M_SERIAL_OK_DONE) ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL; \
} \
} \
return ret; \
}
#define TUPLE_DEFINE_IN_SERIAL_FUNC(name, a) \
case M_C4(name, _, TUPLE_GET_FIELD a, _value): \
ret = TUPLE_CALL_IN_SERIAL(a, el -> TUPLE_GET_FIELD a, f); \
break; \
#define TUPLE_DEFINE_INIT_MOVE(name, ...) \
static inline void M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
@@ -441,7 +505,10 @@ namespace m_tuple {
#define TUPLEI_IF_ALL(method, ...) \
M_IF(M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, method, __VA_ARGS__))
#define TUPLEI_OPLIST(name, ...) \
// deferred
#define TUPLEI_OPLIST_P1(arg) TUPLEI_OPLIST_P2 arg
#define TUPLEI_OPLIST_P2(name, ...) \
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_C(name,_init)),), \
INIT_SET(M_C(name, _init_set)), \
INIT_WITH(M_C(name, _init_set2)), \
@@ -455,6 +522,8 @@ namespace m_tuple {
M_IF_METHOD_ALL(PARSE_STR, __VA_ARGS__)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD_ALL(IN_SERIAL, __VA_ARGS__)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_C(name, _init_move)),), \
M_IF_METHOD_ALL(MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_C(name, _swap)),), \
+157 -71
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - VARIANT module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -34,14 +34,14 @@
USAGE:
VARIANT_DEF2(name, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */
#define VARIANT_DEF2(name, ...) \
VARIANTI_DEF2_A( (name, VARIANTI_INJECT_GLOBAL(__VA_ARGS__)) )
VARIANTI_DEF2_P1( (name, VARIANTI_INJECT_GLOBAL(__VA_ARGS__)) )
/* Define the oplist of a variant.
USAGE: VARIANT_OPLIST(name[, oplist of the first type, ...]) */
#define VARIANT_OPLIST(...) \
M_IF_NARGS_EQ1(__VA_ARGS__) \
(VARIANTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST), \
VARIANTI_OPLIST(__VA_ARGS__ ))
(VARIANTI_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST)), \
VARIANTI_OPLIST_P1((__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
@@ -55,10 +55,10 @@
#define VARIANTI_INJECT_OPLIST_B( f, ... ) \
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
#define VARIANTI_DEF2_A(...) \
VARIANTI_DEF2_B __VA_ARGS__
// Deferred evaluation
#define VARIANTI_DEF2_P1(...) VARIANTI_DEF2_P2 __VA_ARGS__
#define VARIANTI_DEF2_B(name, ...) \
#define VARIANTI_DEF2_P2(name, ...) \
VARIANTI_DEFINE_TYPE(name, __VA_ARGS__) \
VARIANTI_DEFINE_INIT(name, __VA_ARGS__) \
VARIANTI_DEFINE_CLEAR(name, __VA_ARGS__) \
@@ -83,6 +83,10 @@
(VARIANTI_DEFINE_OUT_STR(name, __VA_ARGS__),) \
VARIANTI_IF_ALL2(IN_STR, INIT, __VA_ARGS__) \
(VARIANTI_DEFINE_IN_STR(name, __VA_ARGS__),) \
VARIANTI_IF_ALL(OUT_SERIAL, __VA_ARGS__) \
(VARIANTI_DEFINE_OUT_SERIAL(name, __VA_ARGS__),) \
VARIANTI_IF_ALL2(IN_SERIAL, INIT, __VA_ARGS__) \
(VARIANTI_DEFINE_IN_SERIAL(name, __VA_ARGS__),) \
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
(VARIANTI_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
@@ -107,11 +111,14 @@
#define VARIANTI_GET_HASH(f,t,o) M_GET_HASH o
#define VARIANTI_GET_EQUAL(f,t,o) M_GET_EQUAL o
#define VARIANTI_GET_STR(f,t,o) M_GET_GET_STR o
#define VARIANTI_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
#define VARIANTI_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
#define VARIANTI_GET_IN_STR(f,t,o) M_GET_IN_STR o
#define VARIANTI_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
#define VARIANTI_GET_OUT_SERIAL(f,t,o) M_GET_OUT_SERIAL o
#define VARIANTI_GET_IN_SERIAL(f,t,o) M_GET_IN_SERIAL o
#define VARIANTI_GET_SWAP(f,t,o) M_GET_SWAP o
/* Call the methods through API */
#define VARIANTI_CALL_INIT(t, ...) M_APPLY_API(VARIANTI_GET_INIT t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_INIT_SET(t, ...) M_APPLY_API(VARIANTI_GET_INIT_SET t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_INIT_MOVE(t, ...) M_APPLY_API(VARIANTI_GET_INIT_MOVE t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
@@ -122,11 +129,14 @@
#define VARIANTI_CALL_HASH(t, ...) M_APPLY_API(VARIANTI_GET_HASH t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_EQUAL(t, ...) M_APPLY_API(VARIANTI_GET_EQUAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_GET_STR(t, ...) M_APPLY_API(VARIANTI_GET_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_PARSE_STR(t, ...) M_APPLY_API(VARIANTI_GET_PARSE_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_OUT_STR(t, ...) M_APPLY_API(VARIANTI_GET_OUT_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_IN_STR(t, ...) M_APPLY_API(VARIANTI_GET_IN_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_PARSE_STR(t, ...) M_APPLY_API(VARIANTI_GET_PARSE_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_OUT_SERIAL(t, ...) M_APPLY_API(VARIANTI_GET_OUT_SERIAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_IN_SERIAL(t, ...) M_APPLY_API(VARIANTI_GET_IN_SERIAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
#define VARIANTI_CALL_SWAP(t, ...) M_APPLY_API(VARIANTI_GET_SWAP t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
/* Define the type */
#define VARIANTI_DEFINE_TYPE(name, ...) \
enum M_C(name, _enum) { M_C(name, _EMPTY) \
M_MAP2(VARIANTI_DEFINE_UNION_ELE, name, __VA_ARGS__) \
@@ -139,20 +149,21 @@
} M_C(name,_t)[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr);
#define VARIANTI_DEFINE_UNION_ELE(name, a) \
, M_C4(name, _, VARIANTI_GET_FIELD a, _value)
#define VARIANTI_DEFINE_TYPE_ELE(a) \
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ;
#define VARIANTI_DEFINE_INIT(name, ...) \
static inline void M_C(name, _init)(M_C(name,_t) my) { \
my->type = M_C(name, _EMPTY); \
}
#define VARIANTI_DEFINE_INIT_SET(name, ...) \
static inline void M_C(name, _init_set)(M_C(name,_t) my , M_C(name,_t) const org) { \
#define VARIANTI_DEFINE_INIT_SET(name, ...) \
static inline void M_C(name, _init_set)(M_C(name,_t) my , \
M_C(name,_t) const org) { \
my->type = org->type; \
switch (org->type) { \
case M_C(name, _EMPTY): break; \
@@ -168,13 +179,14 @@
#define VARIANTI_DEFINE_SET(name, ...) \
static inline void M_C(name, _set)(M_C(name,_t) my , M_C(name,_t) const org) { \
static inline void M_C(name, _set)(M_C(name,_t) my , \
M_C(name,_t) const org) { \
if (my->type != org->type) { \
/* Different types: clear previous, create new */ \
/* Different types: clear previous one and create new */ \
M_C(name, _clear)(my); \
M_C(name, _init_set)(my, org); \
} else { \
/* Same type: optimize the settings */ \
/* Same type: optimize the set */ \
switch (org->type) { \
case M_C(name, _EMPTY): break; \
M_MAP2(VARIANTI_DEFINE_SET_FUNC, name, __VA_ARGS__) \
@@ -205,7 +217,7 @@
#define VARIANTI_DEFINE_TEST_P(name, ...) \
static inline bool M_C(name, _empty_p)(M_C(name,_t) my) { \
static inline bool M_C(name, _empty_p)(M_C(name,_t) const my) { \
return my->type == M_C(name, _EMPTY); \
} \
static inline enum M_C(name, _enum) \
@@ -214,7 +226,8 @@
} \
M_MAP2(VARIANTI_DEFINE_TEST_FUNC, name, __VA_ARGS__)
#define VARIANTI_DEFINE_TEST_FUNC(name, a) \
static inline bool M_C4(name, _, VARIANTI_GET_FIELD a, _p)(M_C(name,_t) my) { \
static inline bool \
M_C4(name, _, VARIANTI_GET_FIELD a, _p)(M_C(name,_t) const my) { \
return my->type == M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
}
@@ -222,7 +235,8 @@
#define VARIANTI_DEFINE_INIT_FIELD(name, ...) \
M_MAP2(VARIANTI_DEFINE_INIT_FIELD_FUNC, name, __VA_ARGS__)
#define VARIANTI_DEFINE_INIT_FIELD_FUNC(name, a) \
static inline void M_C3(name, _init_, VARIANTI_GET_FIELD a)(M_C(name,_t) my) { \
static inline void \
M_C3(name, _init_, VARIANTI_GET_FIELD a)(M_C(name,_t) my) { \
/* Reinit variable with the given value */ \
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
VARIANTI_CALL_INIT(a, my -> value. VARIANTI_GET_FIELD a); \
@@ -232,8 +246,9 @@
#define VARIANTI_DEFINE_INIT_SETTER_FIELD(name, ...) \
M_MAP2(VARIANTI_DEFINE_INIT_SETTER_FIELD_FUNC, name, __VA_ARGS__)
#define VARIANTI_DEFINE_INIT_SETTER_FIELD_FUNC(name, a) \
static inline void M_C3(name, _init_set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
static inline void \
M_C3(name, _init_set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
VARIANTI_GET_TYPE a const VARIANTI_GET_FIELD a ) { \
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
VARIANTI_CALL_INIT_SET(a, my -> value. VARIANTI_GET_FIELD a, \
VARIANTI_GET_FIELD a); \
@@ -243,8 +258,9 @@
#define VARIANTI_DEFINE_SETTER_FIELD(name, ...) \
M_MAP2(VARIANTI_DEFINE_SETTER_FIELD_FUNC, name, __VA_ARGS__)
#define VARIANTI_DEFINE_SETTER_FIELD_FUNC(name, a) \
static inline void M_C3(name, _set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
static inline void \
M_C3(name, _set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
VARIANTI_GET_TYPE a const VARIANTI_GET_FIELD a ) { \
if (my->type == M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
VARIANTI_CALL_SET(a, my -> value. VARIANTI_GET_FIELD a, \
VARIANTI_GET_FIELD a); \
@@ -257,6 +273,7 @@
} \
}
#define VARIANTI_DEFINE_GETTER_FIELD(name, ...) \
M_MAP2(VARIANTI_DEFINE_GETTER_FIELD_FUNC, name, __VA_ARGS__)
#define VARIANTI_DEFINE_GETTER_FIELD_FUNC(name, a) \
@@ -267,6 +284,7 @@
} \
return &my -> value . VARIANTI_GET_FIELD a; \
} \
\
static inline VARIANTI_GET_TYPE a const * \
M_C3(name, _cget_, VARIANTI_GET_FIELD a)(M_C(name,_t) const my) { \
if (my->type != M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
@@ -314,7 +332,8 @@
#define VARIANTI_DEFINE_INIT_MOVE(name, ...) \
static inline void M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
static inline void \
M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
el -> type = org -> type; \
switch (el->type) { \
case M_C(name, _EMPTY): break; \
@@ -331,7 +350,8 @@
#define VARIANTI_DEFINE_MOVE(name, ...) \
static inline void M_C(name, _move)(M_C(name,_t) el, M_C(name,_t) org) { \
static inline void \
M_C(name, _move)(M_C(name,_t) el, M_C(name,_t) org) { \
M_C(name, _clear)(el); \
M_C(name, _init_move)(el , org); \
}
@@ -340,8 +360,9 @@
#define VARIANTI_DEFINE_MOVER(name, ...) \
M_MAP2(VARIANTI_DEFINE_MOVER_FUNC, name, __VA_ARGS__)
#define VARIANTI_DEFINE_MOVER_FUNC(name, a) \
static inline void M_C3(name, _move_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
static inline void \
M_C3(name, _move_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
M_C(name, _clear)(my); \
/* Reinit variable with the given value */ \
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
@@ -351,7 +372,8 @@
#define VARIANTI_DEFINE_SWAP(name, ...) \
static inline void M_C(name, _swap)(M_C(name,_t) el1, M_C(name,_t) el2) { \
static inline void \
M_C(name, _swap)(M_C(name,_t) el1, M_C(name,_t) el2) { \
if (el1->type == el2->type) { \
switch (el1->type) { \
case M_C(name, _EMPTY): break; \
@@ -402,6 +424,46 @@
break;
#define VARIANTI_DEFINE_PARSE_STR(name, ...) \
static inline bool M_C(name, _parse_str)(M_C(name,_t) el, \
const char str[], \
const char **endp) { \
assert (str != NULL && el != NULL); \
bool success = false; \
char variantTypeBuf[M_MAX_IDENTIFIER_LENGTH+1]; \
int c = *str++; \
unsigned int i = 0; \
M_C(name, _clean)(el); \
if (c != '@') goto exit; \
/* First read the name of the type */ \
c = *str++; \
while (c != '@' && c != 0 && i < sizeof(variantTypeBuf) - 1) { \
variantTypeBuf[i++] = c; \
c = *str++; \
} \
if (c != '@') goto exit; \
variantTypeBuf[i++] = 0; \
assert(i < sizeof(variantTypeBuf)); \
/* In function of the type */ \
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
el->type = M_C(name, _EMPTY); \
} \
M_MAP2(VARIANTI_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \
else goto exit; \
success = (*str++ == '@'); \
exit: \
if (endp) *endp = str; \
return success; \
}
#define VARIANTI_DEFINE_PARSE_STR_FUNC(name, a) \
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
bool b = VARIANTI_CALL_PARSE_STR(a, el -> value . VARIANTI_GET_FIELD a, str, &str); \
if (!b) goto exit; \
}
#define VARIANTI_DEFINE_OUT_STR(name, ...) \
static inline void M_C(name, _out_str)(FILE *f, \
M_C(name,_t) const el) { \
@@ -424,10 +486,11 @@
static inline bool M_C(name, _in_str)(M_C(name,_t) el, \
FILE *f) { \
assert (f != NULL && el != NULL); \
/* A buffer of 400 bytes should be more than enough for all variant names... */ \
char variantTypeBuf[400]; \
char variantTypeBuf[M_MAX_IDENTIFIER_LENGTH+1]; \
M_C(name, _clean)(el); \
if (fgetc(f) != '@') return false; \
/* First read the name of the type */ \
bool b = true; \
int c = fgetc(f); \
unsigned int i = 0; \
while (c != '@' && c != EOF && i < sizeof(variantTypeBuf) - 1) { \
@@ -437,63 +500,81 @@
if (c != '@') return false; \
variantTypeBuf[i++] = 0; \
assert(i < sizeof(variantTypeBuf)); \
M_C(name, _clear)(el); \
/* In function of the type */ \
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
el->type = M_C(name, _EMPTY); \
} \
M_MAP2(VARIANTI_DEFINE_IN_STR_FUNC , name, __VA_ARGS__) \
else return false; \
return fgetc(f) == '@'; \
else { b = false; } \
return b && (fgetc(f) == '@'); \
}
#define VARIANTI_DEFINE_IN_STR_FUNC(name, a) \
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
bool b = VARIANTI_CALL_IN_STR(a, el -> value . VARIANTI_GET_FIELD a, f); \
if (!b) return false; \
b = VARIANTI_CALL_IN_STR(a, el -> value . VARIANTI_GET_FIELD a, f); \
}
#define VARIANTI_DEFINE_PARSE_STR(name, ...) \
static inline bool M_C(name, _parse_str)(M_C(name,_t) el, \
const char str[], \
const char **endp) { \
assert (str != NULL && el != NULL); \
bool success = false; \
/* A buffer of 400 bytes should be more than enough for all variant names... */ \
char variantTypeBuf[400]; \
int c = *str++; \
unsigned int i = 0; \
if (c != '@') goto exit; \
/* First read the name of the type */ \
c = *str++; \
while (c != '@' && c != 0 && i < sizeof(variantTypeBuf) - 1) { \
variantTypeBuf[i++] = c; \
c = *str++; \
#define VARIANTI_STRINGIFY_NAME(a) \
M_APPLY(M_AS_STR, VARIANTI_GET_FIELD a)
#define VARIANTI_DEFINE_OUT_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, \
M_C(name,_t) const el) { \
const int field_max = M_NARGS(__VA_ARGS__); \
static const char *const field_name[] = \
{ M_REDUCE(VARIANTI_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
assert (f != NULL && f->m_interface != NULL && el != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
switch (el->type) { \
case M_C(name, _EMPTY): \
return f->m_interface->write_variant_start(local, f, field_name, field_max, -1); \
break; \
M_MAP2(VARIANTI_DEFINE_OUT_SERIAL_FUNC , name, __VA_ARGS__) \
default: assert(false); break; \
} \
if (c != '@') goto exit; \
variantTypeBuf[i++] = 0; \
assert(i < sizeof(variantTypeBuf)); \
M_C(name, _clear)(el); \
/* In function of the type */ \
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
el->type = M_C(name, _EMPTY); \
} \
M_MAP2(VARIANTI_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \
else goto exit; \
success = (*str++ == '@'); \
exit: \
if (endp) *endp = str; \
return success; \
ret |= f->m_interface->write_variant_end(local, f); \
return ret & M_SERIAL_FAIL; \
}
#define VARIANTI_DEFINE_PARSE_STR_FUNC(name, a) \
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
#define VARIANTI_DEFINE_OUT_SERIAL_FUNC(name, a) \
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
ret = f->m_interface->write_variant_start(local, f, field_name, field_max, \
M_C4(name, _, VARIANTI_GET_FIELD a, _value) -1); \
VARIANTI_CALL_OUT_SERIAL(a, f, el -> value . VARIANTI_GET_FIELD a); \
break;
#define VARIANTI_DEFINE_IN_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(M_C(name,_t) el, \
m_serial_read_t f) { \
const int field_max = M_NARGS(__VA_ARGS__); \
static const char *const field_name[] = \
{ M_REDUCE(VARIANTI_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
assert (f != NULL && f->m_interface != NULL && el != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
int id = -1; \
M_C(name, _clean)(el); \
ret = f->m_interface->read_variant_start(local, f, field_name, field_max, &id); \
if (ret != M_SERIAL_OK_CONTINUE) return ret; \
assert (id >= 0 && id < field_max); \
el->type = (enum M_C(name, _enum))(id+1); \
switch (id+1) { \
M_MAP2(VARIANTI_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \
default: assert(false); break; \
} \
if (ret == M_SERIAL_OK_DONE) \
ret = f->m_interface->read_variant_end(local, f); \
return ret; \
}
#define VARIANTI_DEFINE_IN_SERIAL_FUNC(name, a) \
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
bool b = VARIANTI_CALL_PARSE_STR(a, el -> value . VARIANTI_GET_FIELD a, str, &str); \
if (!b) goto exit; \
}
ret = VARIANTI_CALL_IN_SERIAL(a, el -> value . VARIANTI_GET_FIELD a, f); \
break; \
#define VARIANTI_DEFINE_CLEAN_FUNC(name, ...) \
@@ -504,7 +585,10 @@
} \
#define VARIANTI_OPLIST(name, ...) \
// deferred
#define VARIANTI_OPLIST_P1(arg) VARIANTI_OPLIST_P2 arg
#define VARIANTI_OPLIST_P2(name, ...) \
(INIT(M_C(name,_init)), \
INIT_SET(M_C(name, _init_set)), \
SET(M_C(name,_set)), \
@@ -518,6 +602,8 @@
M_IF_METHOD2_ALL(PARSE_STR, INIT, __VA_ARGS__)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD2_ALL(IN_STR, INIT, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD2_ALL(IN_SERIAL, INIT, __VA_ARGS__)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_C(name, _init_move)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_C(name, _swap)),), \
+59 -33
View File
@@ -1,7 +1,7 @@
/*
* M*LIB / WORKER - Extra worker interface
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -25,13 +25,22 @@
#ifndef MSTARLIB_WORKER_H
#define MSTARLIB_WORKER_H
#ifndef WORKER_DISABLE
/* The User Code can define M_USE_WORKER to 0 to disable the use of workers.
The macros / functions are then defined to only use one core.
By default, the behavior is to use workers.
*/
#ifndef M_USE_WORKER
# define M_USE_WORKER 1
#endif
#if M_USE_WORKER
#include "m-atomic.h"
#include "m-buffer.h"
#include "m-mutex.h"
/* Needed header for detection of how many core on the system */
/* Include needed system header for detection of how many core on the system */
#if defined(_WIN32)
# include <windows.h>
#elif (defined(__APPLE__) && defined(__MACH__)) \
@@ -39,7 +48,7 @@
|| defined(__NetBSD__) || defined(__OpenBSD__)
# include <sys/param.h>
# include <sys/sysctl.h>
# define WORKER_USE_SYSCTL
# define M_USE_WORKER_SYSCTL 1
#else
# include <unistd.h>
#endif
@@ -53,29 +62,35 @@
if C++, it will use Lambda function (and std::function) instead
(It doesn't support pre-C++11 compiler).
Otherwise go with nested function for the MACRO version.
Otherwise go with nested function (GCC) for the MACRO version.
This behavior can be overriden by User Code by defining to 1 or 0 the
following macros:
* M_USE_WORKER_CPP_FUNCTION
* M_USE_WORKER_CLANG_BLOCK
*/
#if defined(__cplusplus) && !defined(WORKER_USE_CPP_FUNCTION)
# define WORKER_USE_CPP_FUNCTION 1
#if defined(__cplusplus) && !defined(M_USE_WORKER_CPP_FUNCTION)
# define M_USE_WORKER_CPP_FUNCTION 1
# include <functional>
#elif defined(__has_extension) && !defined(WORKER_USE_CLANG_BLOCK)
#elif defined(__has_extension) && !defined(M_USE_WORKER_CLANG_BLOCK)
# if __has_extension(blocks)
# define WORKER_USE_CLANG_BLOCK 1
# define M_USE_WORKER_CLANG_BLOCK 1
# endif
#endif
#ifndef WORKER_USE_CLANG_BLOCK
# define WORKER_USE_CLANG_BLOCK 0
#ifndef M_USE_WORKER_CLANG_BLOCK
# define M_USE_WORKER_CLANG_BLOCK 0
#endif
#ifndef WORKER_USE_CPP_FUNCTION
# define WORKER_USE_CPP_FUNCTION 0
#ifndef M_USE_WORKER_CPP_FUNCTION
# define M_USE_WORKER_CPP_FUNCTION 0
#endif
/* Control that not both options are selected at the same time.
Note: there are not really incompatible, but if we use C++ we shall go to
lambda directly (there is no need to support blocks)! */
#if WORKER_USE_CLANG_BLOCK && WORKER_USE_CPP_FUNCTION
# error WORKER_USE_CPP_FUNCTION and WORKER_USE_CLANG_BLOCK are both defined (not supported).
lambda directly (there is no need to support blocks). */
#if M_USE_WORKER_CLANG_BLOCK && M_USE_WORKER_CPP_FUNCTION
# error M_USE_WORKER_CPP_FUNCTION and M_USE_WORKER_CLANG_BLOCK are both defined. This is not supported.
#endif
/* This type defines a work order */
@@ -83,10 +98,10 @@ typedef struct work_order_s {
struct worker_sync_s *block;
void * data;
void (*func) (void *data);
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
void (^blockFunc)(void *data);
#endif
#if WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CPP_FUNCTION
std::function<void(void*)> function;
#endif
} worker_order_t;
@@ -95,7 +110,7 @@ typedef struct work_order_s {
* * MACRO to be used to send an empty order to stop the thread
* * MACRO to complete the not-used fields
*/
#if WORKER_USE_CLANG_BLOCK || WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CLANG_BLOCK || M_USE_WORKER_CPP_FUNCTION
# define WORKER_EMPTY_ORDER { NULL, NULL, NULL, NULL }
# define WORKER_EXTRA_ORDER , NULL
#else
@@ -103,13 +118,27 @@ typedef struct work_order_s {
# define WORKER_EXTRA_ORDER
#endif
/* As in C++, it uses std::function, M_POD_OPLIST
is not sufficient for initilization of the structure.
So let's use C++ constructor, destructor and copy constructor */
#if M_USE_WORKER_CPP_FUNCTION
# define WORKER_CPP_INIT(x) (new (&(x)) worker_order_t())
# define WORKER_CPP_INIT_SET(x, y) (new (&(x)) worker_order_t(y))
# define WORKER_CPP_SET(x, y) ((x) = (y))
# define WORKER_CPP_CLEAR(x) ((&(x))->~worker_order_t())
# define WORKER_CPP_INIT_MOVE(x,y) (new (&(x)) worker_order_t(y), ((&(y))->~worker_order_t()))
# define WORKER_OPLIST (INIT(WORKER_CPP_INIT), INIT_SET(WORKER_CPP_INIT_SET), SET(WORKER_CPP_SET), CLEAR(WORKER_CPP_CLEAR),INIT_MOVE(WORKER_CPP_INIT_MOVE) )
#else
# define WORKER_OPLIST M_POD_OPLIST
#endif
/* This type defines a worker represented as a thread */
typedef struct worker_thread_s {
m_thread_t id;
} worker_thread_t;
/* Let's define the queue which will manage all work orders */
BUFFER_DEF(worker_queue, worker_order_t, 0, BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, M_POD_OPLIST)
BUFFER_DEF(worker_queue, worker_order_t, 0, BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, WORKER_OPLIST)
/* This type defines the state of the workers */
typedef struct worker_s {
@@ -146,7 +175,7 @@ workeri_get_cpu_count(void)
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#elif defined(WORKER_USE_SYSCTL)
#elif defined(M_USE_WORKER_SYSCTL)
int nm[2];
int count = 0;
size_t len = sizeof (count);
@@ -167,13 +196,13 @@ workeri_exec(worker_order_t *w)
{
assert (w!= NULL && w->block != NULL);
//printf ("Starting thread with data %p\n", w->data);
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
//printf ("Running %s f=%p b=%p\n", (w->func == NULL) ? "Blocks" : "Function", w->func, w->blockFunc);
if (w->func == NULL)
w->blockFunc(w->data);
else
#endif
#if WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CPP_FUNCTION
//printf ("Running %s f=%p b=%p\n", (w->function == NULL) ? "Lambda" : "Function", w->func, w->blockFunc);
if (w->function)
w->function(w->data);
@@ -291,7 +320,7 @@ worker_spawn(worker_sync_t block, void (*func)(void *data), void *data)
(*func) (data);
}
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
/* Spawn or not the given work order to workers,
or do it ourself if no worker is available */
static inline void
@@ -310,7 +339,7 @@ worker_spawn_block(worker_sync_t block, void (^func)(void *data), void *data)
}
#endif
#if WORKER_USE_CPP_FUNCTION
#if M_USE_WORKER_CPP_FUNCTION
/* Spawn or not the given work order to workers,
or do it ourself if no worker is available */
static inline void
@@ -377,12 +406,12 @@ worker_count(worker_t g)
'input' is the list of input variables of the 'core' block within "( )"
'output' is the list of output variables of the 'core' block within "( )"
Output variables are only available after a synchronisation block. */
#if WORKER_USE_CLANG_BLOCK
#if M_USE_WORKER_CLANG_BLOCK
#define WORKER_SPAWN(_block, _input, _core, _output) \
WORKER_DEF_DATA(_input, _output) \
WORKER_DEF_SUBBLOCK(_input, _output, _core) \
worker_spawn_block ((_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
#elif WORKER_USE_CPP_FUNCTION
#elif M_USE_WORKER_CPP_FUNCTION
// TODO: Explicit pass all arguments by reference.
#define WORKER_SPAWN(_block, _input, _core, _output) \
worker_spawn_function ((_block), [&](void *param) {(void)param ; _core } , NULL)
@@ -452,12 +481,9 @@ worker_count(worker_t g)
M_MAP(WORKER_PROPAGATE_SINGLE_OUTPUT, __VA_ARGS__)
#else /* WORKER_DISABLE */
#else /* M_USE_WORKER */
/* User has defined WORKER_DISABLE to disable the use of workers.
Define empty types and empty functions
*/
/* Define empty types and empty functions to not use any worker */
typedef struct worker_block_s {
int x;
@@ -475,6 +501,6 @@ typedef struct worker_s {
#define worker_count(w) 1
#define WORKER_SPAWN(b, i, c, o) do { c } while (0)
#endif /* WORKER_DISABLE */
#endif /* M_USE_WORKER */
#endif
+30 -17
View File
@@ -1,15 +1,24 @@
CC=cc -std=c99
WCFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
WCFLAGS_GCC=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
WCFLAGS_CLANG=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
WCFLAGS_CPP=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wno-unused-function -ftrapv
XCFLAGS=
CFLAGS=$(WCFLAGS) $(XCFLAGS)
CFLAGS=$(WCFLAGS_GCC) $(XCFLAGS)
CPPFLAGS=-I..
LDFLAGS=-pthread
RM=rm -f
RMDIR=rm -rf
LOG_COMPILER=
SED=sed -e
all:
@echo "Nothing to be done."
#####################################################################################################
# Rules
#####################################################################################################
.POSIX:
.PHONY: valgrind scan-build sanitize check clean *.log coverage synthesis
.SUFFIXES:
@@ -20,7 +29,7 @@ all:
$(CC) $(CFLAGS) $(CPPFLAGS) -g $(LDFLAGS) $< -o $@
.c.depend:
$(CC) $(CFLAGS) $(CPPFLAGS) -MM -MT `echo $< |sed -e 's/\.c/\.exe/g'` $< > $@
$(CC) $(CFLAGS) $(CPPFLAGS) -MM -MT `echo $< |$(SED) 's/\.c/\.exe/g'` $< > $@
.exe.log:
$(LOG_COMPILER) ./$<
@@ -42,28 +51,30 @@ all:
-include depend
#####################################################################################################
# Test suite
#####################################################################################################
depend:
$(RM) depend
echo "" > depend
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.depend/g'`
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.depend/g'`
cat *.depend > depend
$(RM) *.depend
#####################################################################################################
# Clean
#####################################################################################################
check: depend
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.log/g'`
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.log/g'`
clean:
$(RM) *.exe *.s *~ *.o *.log *.valgrind ./a*.dat clang_output_* *.c.c *.end.c *.first.c *.middle.c
$(RM) -rf coverage *.gcda *.gcno *.gcov all.info test.tar coverage-dir mlib-report *.synt
$(RM) *.exe *.s *~ *.o *.log a*.dat
$(RM) *.valgrind *.gcda *.gcno *.gcov all.info test.tar *.synt clang_output_* *.c.c *.end.c *.first.c *.middle.c
$(RMDIR) coverage coverage-dir mlib-report
distclean: clean
$(RM) *.depend depend
#####################################################################################################
# Code generation analysis
#####################################################################################################
@@ -89,12 +100,13 @@ gene-queue:
gene-string:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mstring.c && less tgen-mstring.s
#####################################################################################################
# Coverage
#####################################################################################################
coverage:
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.gcov/g'`
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.gcov/g'`
$(MAKE) synthesis
SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
@@ -102,6 +114,7 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-BITSET ../m-bitset.h test-mbitset.synt \
M-BBPTREE test-mbptree.c test-mbptree.synt \
M-BUFFER test-mbuffer.c.c test-mbuffer.synt \
M-CONCURRENT test-mconcurrent.c.c test-mconcurrent.synt \
M-CORE test-mcore.c.c test-mcore.synt \
M-DEQUE test-mdeque.c.c test-mdeque.synt \
M-DICT test-mdict.c.c test-mdict.synt \
@@ -112,6 +125,8 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-MUTEX ../m-mutex.h test-mmutex.synt \
M-PRIOQUEUE test-mprioqueue.c.c test-mprioqueue.synt \
M-RBTREE test-mrbtree.c.c test-mrbtree.synt \
M-SERIAL-BIN ../m-serial-bin.h test-mserial-bin.synt \
M-SERIAL-JSON ../m-serial-json.h test-mserial-json.synt \
M-SHARED test-mshared.c.c test-mshared.synt \
M-SNAPSHOT test-msnapshot.c.c test-msnapshot.synt \
M-STRING ../m-string.h test-mstring.synt \
@@ -130,12 +145,13 @@ synthesis:
done
@printf "+-------------------------------+-------------------------------+\n"
#####################################################################################################
# Code analyzer tools
#####################################################################################################
valgrind:
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.valgrind/g'`
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.valgrind/g'`
scan-build: clean
$(RM) -rf ./mlib-report
@@ -150,9 +166,6 @@ sanitize:
# Test All
#####################################################################################################
WCFLAGS_CLANG=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
WCFLAGS_CPP=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wno-unused-function -ftrapv
checkall:
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c99"
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c99 -m32"
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+32 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -25,9 +25,11 @@
#include "m-array.h"
#include "m-string.h"
#include "m-deque.h"
#include "test-obj.h"
#include "m-dict.h"
#include "m-algo.h"
#include "test-obj.h"
typedef struct over_s {
unsigned long data;
ILIST_INTERFACE(ilist_over, over_s);
@@ -42,6 +44,8 @@ ILIST_DEF(ilist_over, over_t, M_POD_OPLIST)
LIST_DEF(list_string, string_t, STRING_OPLIST)
DEQUE_DEF(deque_obj, testobj_t, TESTOBJ_CMP_OPLIST)
#define M_OPL_deque_obj_t() DEQUE_OPLIST(deque_obj, TESTOBJ_CMP_OPLIST)
DICT_DEF2(dict_obj, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
LIST_DUAL_PUSH_DEF(dlist_int, int)
#include "coverage.h"
START_COVERAGE
@@ -50,9 +54,13 @@ ALGO_DEF(algo_list, LIST_OPLIST(list_int))
ALGO_DEF(algo_over, ILIST_OPLIST(ilist_over, M_POD_OPLIST))
ALGO_DEF(algo_string, LIST_OPLIST(list_string, STRING_OPLIST))
ALGO_DEF(algo_deque, deque_obj_t)
ALGO_DEF(algo_dict, DICT_OPLIST(dict_obj, STRING_OPLIST, TESTOBJ_OPLIST))
END_COVERAGE
ALGO_DEF(algo_dlist, LIST_OPLIST(list_int))
/* Helper functions */
int g_min, g_max, g_count;
static void g_f(int n)
{
assert (g_min <= n && n <= g_max);
@@ -91,6 +99,8 @@ static bool func_test_both_even_or_odd(int a, int b)
return (a&1) == (b&1);
}
/* Tests starts */
static void test_list(void)
{
list_int_t l;
@@ -156,11 +166,29 @@ static void test_list(void)
assert (!algo_list_sort_p(l));
algo_list_sort(l);
assert (algo_list_sort_p(l));
list_int_clear(l);
ALGO_INIT_VA(l, LIST_OPLIST(list_int), 1, 2, 3, 4, 5);
assert (list_int_size(l) == 5);
algo_list_remove_val(l, 3);
assert (list_int_size(l) == 4);
algo_list_find (it1, l, 3);
assert (list_int_end_p (it1));
algo_list_remove_val(l, 3);
assert (list_int_size(l) == 4);
algo_list_remove_if(l, func_test_42);
assert (list_int_size(l) == 4);
list_int_push_back(l, 42);
list_int_push_back(l, 43);
list_int_push_back(l, 42);
algo_list_remove_if(l, func_test_42);
assert (list_int_size(l) == 5);
algo_list_find (it1, l, 42);
assert (list_int_end_p (it1));
list_int_clear(l);
}
@@ -397,6 +425,7 @@ static void test_insert(void)
ALGO_LET_INIT_VA(b, aint_t, -1, -2, -3) {
lint_it_t i;
lint_it(i, a);
// Insert it after first element
ALGO_INSERT_AT(a, lint_t, i, b, aint_t);
// TBC: order ok for the list (the reverse is confusing...)?
ALGO_LET_INIT_VA(c, lint_t, 1, 2, 3, -3, -2, -1, 4) {
+10 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -128,7 +128,15 @@ static void test_uint(void)
assert(*array_uint_cget(v2, 500) == 0);
assert(array_uint_size(v2) == 1005);
assert(*array_uint_cget(v2, 1004) == 0);
array_uint_set_at(v2, 1004, 465487);
array_uint_insert_v (v2, 500, 0);
assert(array_uint_size(v2) == 1005);
assert(*array_uint_cget(v2, 1004) == 465487);
array_uint_insert_v (v2, 1004, 10000);
assert(array_uint_size(v2) == 11005);
assert(*array_uint_cget(v2, 11004) == 465487);
array_uint_resize(v2, 1005);
array_uint_init(v);
array_uint_swap(v,v2);
assert(array_uint_size(v) == 1005);
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+190 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -30,12 +30,17 @@
START_COVERAGE
BPTREE_DEF2(btree, 3, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
END_COVERAGE
BPTREE_DEF2(btree_my, 3, testobj_t, TESTOBJ_CMP_OPLIST, testobj_t, TESTOBJ_CMP_OPLIST)
BPTREE_DEF2(btree_string, 5, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
BPTREE_DEF2(btree_int, 17, int, int)
BPTREE_DEF(btree_intset, 13, int)
BPTREE_DEF(btree_myset, 15, testobj_t, TESTOBJ_CMP_OPLIST)
BPTREE_MULTI_DEF2(multimap, 3, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
BPTREE_MULTI_DEF(multiset, 6, int, M_DEFAULT_OPLIST)
static void test1(void)
{
btree_t b;
@@ -68,6 +73,7 @@ static void test1(void)
btree_set_at(b, 5, 39);
assert (btree_size(b) == 6);
assert (*btree_get(b, -25) == 38);
assert (*btree_get(b, 5) == 39);
btree_set_at(b, 5, 40);
assert (btree_size(b) == 6);
assert (*btree_get(b, 5) == 40);
@@ -194,9 +200,88 @@ static void test3(void)
}
assert(btree_size(b) == 0);
const int prime[5] = { 3, 5, 11, 13, 17};
const int prime_size = 5;
for(int k = 0; k < prime_size; k++) {
int p = prime[k];
for(int i = 0; i < 1024; i++) {
btree_set_at(b, (i*p) % 1024, i);
assert(*btree_cget(b, (i*p) % 1024) == i);
}
assert(btree_size(b) == 1024);
for(int i = 0; i < 1024; i++) {
assert(*btree_cget(b, (i*p) % 1024) == i);
}
for(int i = 0; i < 1024; i++) {
bool r = btree_erase(b, (i*p) % 1024);
assert(r);
assert(btree_cget(b, (i*p) % 1024) == NULL);
}
assert(btree_size(b) == 0);
}
for(int k = 0; k < prime_size; k++) {
int p = prime[k];
for(int i = 0; i < 512; i++) {
btree_set_at(b, (i*p) % 1024, i);
assert(*btree_cget(b, (i*p) % 1024) == i);
}
assert(btree_size(b) == 512);
for(int i = 256; i < 512; i++) {
bool r = btree_erase(b, (i*p) % 1024);
assert(r);
assert(btree_cget(b, (i*p) % 1024) == NULL);
}
for(int i = 256; i < 1024; i++) {
btree_set_at(b, (i*p) % 1024, i);
assert(*btree_cget(b, (i*p) % 1024) == i);
}
assert(btree_size(b) == 1024);
for(int i = 0; i < 1024; i++) {
bool r = btree_erase(b, (i*p) % 1024);
assert(r);
assert(btree_cget(b, (i*p) % 1024) == NULL);
}
assert(btree_size(b) == 0);
}
btree_clear(b);
}
static void test3_string(void)
{
string_t key, value;
string_init(key);
string_init(value);
btree_string_t b;
btree_string_init(b);
const int prime[6] = { 1, 3, 5, 11, 13, 17};
const int prime_size = 6;
for(int k = 0; k < prime_size; k++) {
int p = prime[k];
for(int i = 0; i < 1000; i++) {
string_printf(key, "%d", (i*p) % 1024);
string_printf(value, "%d", 1000*i);
btree_string_set_at(b, key, value);
assert(string_equal_p(value, *btree_string_cget(b, key)));
}
assert(btree_string_size(b) == 1000);
for(int i = 0; i < 1000; i++) {
string_printf(key, "%d", (i*p) % 1024);
string_printf(value, "%d", 1000*i);
assert(string_equal_p(value, *btree_string_cget(b, key)));
bool r = btree_string_erase(b, key);
assert (r == true);
}
assert(btree_string_size(b) == 0);
}
btree_string_clear(b);
string_clear(key);
string_clear(value);
}
static void test4(void)
{
btree_t b1, b2;
@@ -282,7 +367,7 @@ static void test5(void)
assert (btree_it_equal_p(it, it2));
i = 500;
for(btree_it_from(it, b, 500); !btree_it_to_p(it, 600); btree_next(it)) {
for(btree_it_from(it, b, 500); !btree_it_until_p(it, 600); btree_next(it)) {
const btree_type_t *item = btree_cref(it);
assert (*item->key_ptr == i);
assert (*item->value_ptr == 1000*i);
@@ -292,7 +377,7 @@ static void test5(void)
btree_erase (b, 500);
i = 501;
for(btree_it_from(it, b, 500); !btree_it_to_p(it, 600); btree_next(it)) {
for(btree_it_from(it, b, 500); !btree_it_until_p(it, 600); btree_next(it)) {
const btree_type_t *item = btree_cref(it);
assert (*item->key_ptr == i);
assert (*item->value_ptr == 1000*i);
@@ -302,7 +387,7 @@ static void test5(void)
btree_it_from(it, b, 1000);
assert (btree_end_p(it));
assert (btree_it_to_p(it, 1001));
assert (btree_it_until_p(it, 1001));
btree_clear(b);
}
@@ -414,14 +499,114 @@ static void test_io_set(void)
}
}
#if 0
// Dump code for debug
static void multimap_dump_node(multimap_node_t n)
{
while (n) {
int num = n->num < 0 ? -n->num : n->num;
printf ("[");
for(int i = 0; i < num; i++)
printf ("%d, ", n->key[i]);
printf ("] ");
n = n->next;
}
printf("\n");
}
static void
multimap_dump(multimap_t b)
{
multimap_node_t n = b->root;
while (n->num > 0) {
multimap_dump_node(n);
n = n->kind.node[0];
}
multimap_dump_node(n);
}
#endif
static void
test_multimap(void)
{
multimap_t b;
multimap_init(b);
for (int size = 20; size < 1000; size += 10) {
for(int i = 0; i < size; i++) {
multimap_set_at(b, i/4, i);
}
assert(multimap_size(b) == (unsigned) size);
for (int k = 0 ; k < size/4; k++) {
int j = (4*k+3);
multimap_it_t it;
for(multimap_it_from(it, b, k);
multimap_it_while_p(it, k);
multimap_next(it)) {
const multimap_type_t *ref = multimap_cref(it);
assert(*ref->key_ptr == k);
assert(*ref->value_ptr == j);
j--;
}
assert(j == 4*(k-1)+3);
}
for(int i = 0; i < size; i++) {
bool r = multimap_erase(b, i/4);
assert (r == true);
}
assert(multimap_size(b) == 0);
}
multimap_clear(b);
}
static void
test_multiset(void)
{
multiset_t b;
multiset_init(b);
for (int size = 20; size < 1000; size += 10) {
for(int i = 0; i < size; i++) {
multiset_push(b, i/4);
}
assert(multiset_size(b) == (unsigned) size);
for (int k = 0 ; k < size/4; k++) {
int j = 0;
multiset_it_t it;
for(multiset_it_from(it, b, k);
multiset_it_while_p(it, k);
multiset_next(it)) {
const multiset_type_t *ref = multiset_cref(it);
assert(*ref == k);
j++;
}
assert(j == 4);
}
for(int i = 0; i < size; i++) {
bool r = multiset_erase(b, i/4);
assert (r == true);
}
assert(multiset_size(b) == 0);
}
multiset_clear(b);
}
int main(void)
{
test1();
test2();
test3();
test3_string();
test4();
test5();
test_io();
test_io_set();
test_multimap();
test_multiset();
exit(0);
}
+82 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -407,6 +407,86 @@ static void test_queue(size_t n, int cpu_count, unsigned long long ref)
queue_uint_clear(g_buff2);
}
/********************************************************************************************/
static void test_spsc(void)
{
squeue_uint_t q;
bool b;
unsigned j;
squeue_uint_init(q, 256);
assert(squeue_uint_empty_p(q));
for(unsigned i = 0; i < 256; i++) {
b = squeue_uint_push(q, i);
assert(b);
}
assert(!squeue_uint_empty_p(q));
assert(squeue_uint_full_p(q));
assert(squeue_uint_size(q) == 256);
assert(squeue_uint_capacity(q) == 256);
b = squeue_uint_push(q, 256);
assert (!b);
for(unsigned i = 0; i < 256; i++) {
b = squeue_uint_pop(&j, q);
assert(b);
assert(j == i);
}
assert(squeue_uint_empty_p(q));
assert(!squeue_uint_full_p(q));
assert(squeue_uint_size(q) == 0);
assert(squeue_uint_capacity(q) == 256);
for(unsigned i = 0; i < 256; i++) {
b = squeue_uint_push(q, i);
assert(b);
}
for(unsigned i = 256; i < 256+128; i++) {
assert(squeue_uint_full_p(q));
squeue_uint_push_force(q, i);
}
for(unsigned i = 128; i < 256+128; i++) {
b = squeue_uint_pop(&j, q);
assert(b);
assert(j == i);
}
unsigned tab[16];
for(unsigned i = 0; i < 16; i++)
tab[i] = i * i;
for(unsigned i = 0; i < 15; i++) {
j = squeue_uint_push_bulk(q, 16, tab);
assert(j == 16);
}
b = squeue_uint_push(q, 1024);
assert(b);
j = squeue_uint_push_bulk(q, 16, tab);
assert(j == 15);
for(unsigned i = 0; i < 16; i++)
tab[i] = 0;
j = squeue_uint_pop_bulk(16, tab, q);
assert(j == 16);
for(unsigned i = 0; i < 16; i++)
assert( tab[i] == i * i);
do {
b = squeue_uint_pop(&j, q);
} while (b);
assert(squeue_uint_empty_p(q));
b = squeue_uint_push_move(q, 65536);
assert(b);
b = squeue_uint_pop(&j, q);
assert(b);
assert(j == 65536);
squeue_uint_clear(q);
}
/********************************************************************************************/
int main(void)
@@ -438,5 +518,6 @@ int main(void)
test_no_thread();
test_global_ishared();
test_queue(1000000, 2, 2148371710223136ULL);
test_spsc();
exit(0);
}
+208
View File
@@ -0,0 +1,208 @@
/*
* M*LIB - Test for Concurrent memory pool allocator
*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "m-c-mempool.h"
#include "m-mutex.h"
C_MEMPOOL_DEF(lf_mempool, int)
m_gc_t gc;
/**********************************************************************/
#define MAX_VALUE 100
#define MAX_THREAD 4
lf_mempool_t g;
typedef struct {
int step;
int *ptr[MAX_VALUE];
int value[MAX_VALUE];
} p_t;
p_t g_tab[MAX_THREAD];
static void do_stuff(m_gc_tid_t id)
{
// Mix of local and more global allocation
int *d[MAX_VALUE];
for(int i = 0; i < MAX_VALUE; i++) {
d[i] = lf_mempool_new(g, id);
*d[i] = i*i;
}
switch (g_tab[id].step)
{
case 0:
for(int i = 0; i < 50; i++) {
g_tab[id].value[i] += i;
g_tab[id].ptr[i] = lf_mempool_new(g, id);
*g_tab[id].ptr[i] = g_tab[id].value[i];
}
g_tab[id].step = 1;
break;
case 1:
for(int i = 50; i < MAX_VALUE; i++) {
g_tab[id].value[i] += i;
g_tab[id].ptr[i] = lf_mempool_new(g, id);
*g_tab[id].ptr[i] = g_tab[id].value[i];
}
g_tab[id].step = 2;
break;
case 2:
for(int i = 0; i < 50; i++) {
assert(g_tab[id].ptr[i] != NULL);
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
lf_mempool_del(g, g_tab[id].ptr[i], id);
g_tab[id].ptr[i] = NULL;
}
g_tab[id].step = 3;
break;
case 3:
for(int i = 50; i < MAX_VALUE; i++) {
assert(g_tab[id].ptr[i] != NULL);
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
lf_mempool_del(g, g_tab[id].ptr[i], id);
g_tab[id].ptr[i] = NULL;
}
g_tab[id].step = 0;
break;
default:
break;
}
for(int i = 0; i < MAX_VALUE; i++) {
assert (*d[i] == i*i);
lf_mempool_del(g, d[i], id);
}
}
static void thread1(void *arg)
{
(void)arg;
/* Attach this thread to the GC */
m_gc_tid_t id = m_gc_attach_thread(gc);
for(int n = 0; n < 100000; n ++) {
/* Awake the thread: now we can alloc nodes */
m_gc_awake(gc, id);
/* Do some stuff */
do_stuff(id);
/* Sleep the thread. A Garbage Collect may occur if possible */
m_gc_sleep(gc, id);
}
/* Detach the thread from the GC */
m_gc_detach_thread(gc, id);
}
static void thread2(void *arg)
{
(void)arg;
m_gc_tid_t id = m_gc_attach_thread(gc);
for(int n = 0; n < 10000; n ++) {
m_gc_awake(gc, id);
do_stuff(id);
m_thread_sleep(1);
m_gc_sleep(gc, id);
}
m_gc_detach_thread(gc, id);
}
static void test(void)
{
/* First create the Garbage Collector */
m_gc_init (gc, MAX_THREAD);
/* Create a mempool and attach it to the Garbage Collector */
lf_mempool_init(g, gc, 256, MAX_THREAD);
m_thread_t id[MAX_THREAD];
for(int i = 0; i < MAX_THREAD; i+=2) {
m_thread_create(id[i], thread1, NULL);
m_thread_create(id[i+1], thread2, NULL);
}
for(int i = 0; i < MAX_THREAD; i++) {
m_thread_join(id[i]);
}
/* Destroy the mempool */
lf_mempool_clear(g);
/* Destroy the Garbage Collector */
m_gc_clear (gc);
}
static void test2(void)
{
/* First create the Garbage Collector */
m_gc_init (gc, MAX_THREAD);
/* Create a mempool and attach it to the Garbage Collector */
lf_mempool_init(g, gc, 256, MAX_THREAD);
/* Create another mempool and attach it to the Garbage Collector */
lf_mempool_t g2;
lf_mempool_init(g2, gc, 256, MAX_THREAD);
/* Register VLA */
m_vlapool_t vla;
m_vlapool_init(vla, gc);
/* Attach the thread */
m_gc_tid_t id = m_gc_attach_thread(gc);
/* Awake the thread: now we can alloc nodes */
m_gc_awake(gc, id);
/* Test some allocation */
void *ptr = m_vlapool_new(vla, id, sizeof (int));
*(int*)ptr = 42;
m_vlapool_del(vla, ptr, id);
int *pi = lf_mempool_new(g, id);
*pi = 44;
lf_mempool_del(g, pi, id);
int *pi2 = lf_mempool_new(g2, id);
*pi2 = 46;
lf_mempool_del(g2, pi2, id);
/* Test that the data remain accessible
as it has only be logically deleted */
assert (*(int*)ptr == 42);
assert (*pi == 44);
assert (*pi2 == 46);
/* Sleep the thread */
m_gc_sleep(gc, id);
/* Detach the thread */
m_gc_detach_thread(gc, id);
/* Clear everything */
m_vlapool_clear(vla);
lf_mempool_clear(g);
lf_mempool_clear(g2);
m_gc_clear(gc);
}
int main(void)
{
test();
test2();
exit(0);
}
+88 -7
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -24,18 +24,27 @@
#include "test-obj.h"
#include "coverage.h"
#include "m-tuple.h"
#include "m-array.h"
#include "m-bptree.h"
#include "m-deque.h"
#include "m-dict.h"
#include "m-list.h"
#include "m-deque.h"
#include "m-bptree.h"
#include "m-prioqueue.h"
#include "m-rbtree.h"
#include "m-tuple.h"
#include "m-variant.h"
#include "m-concurrent.h"
TUPLE_DEF2(point, (x, int), (y, int))
#define M_OPL_point_t() TUPLE_OPLIST(point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
START_COVERAGE
CONCURRENT_DEF(ppoint, point_t)
END_COVERAGE
VARIANT_DEF2(dimension, (x, int), (y, float))
#define M_OPL_dimension_t() VARIANT_OPLIST(dimension, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
CONCURRENT_DEF(pdimension, dimension_t)
ARRAY_DEF(array1, int)
CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1))
@@ -55,6 +64,9 @@ CONCURRENT_DEF(plist2, list2_t, LIST_OPLIST(list2))
DEQUE_DEF(deque1, int)
CONCURRENT_DEF(pdeque1, deque1_t, DEQUE_OPLIST(deque1))
PRIOQUEUE_DEF(prio1, int)
CONCURRENT_DEF(pprio1, prio1_t, PRIOQUEUE_OPLIST(prio1))
BPTREE_DEF2(bptree1, 10, int, int)
CONCURRENT_DEF(pbtree1, bptree1_t, BPTREE_OPLIST2(bptree1, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST))
@@ -68,7 +80,6 @@ DICT_DEF2(string_pool, string_t, string_t)
#define STRING_POOL_OPLIST \
DICT_OPLIST(string_pool, STRING_OPLIST, STRING_OPLIST)
#define M_OPL_string_pool_t() STRING_POOL_OPLIST
CONCURRENT_DEF(string_pool_ts, string_pool_t, STRING_POOL_OPLIST)
#define STRING_POOL_TS_OPLIST \
CONCURRENT_OPLIST(string_pool_ts, STRING_POOL_OPLIST)
@@ -78,6 +89,28 @@ TUPLE_DEF2(my_tuple, (pool, string_pool_ts_t))
#define MY_TUPLE_OPLIST TUPLE_OPLIST(my_tuple, STRING_POOL_TS_OPLIST)
#define M_OPL_my_tuple_t() MY_TUPLE_OPLIST
ARRAY_DEF(key_list, string_t)
#define M_OPL_key_list_t() ARRAY_OPLIST(key_list, STRING_OPLIST)
CONCURRENT_DEF(key_list_ts, key_list_t)
CONCURRENT_RP_DEF(rparray1, array1_t, ARRAY_OPLIST(array1))
CONCURRENT_RP_DEF(rpdict1, dict1_t, DICT_OPLIST(dict1))
/* OA dict needs more operators than the default ones to work */
static inline bool int_oor_equal_p(int s, unsigned char n)
{
return s == -n;
}
static inline void int_oor_set(int *s, unsigned char n)
{
*s = -n;
}
#define INT_OA_OPLIST \
M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(int_oor_equal_p), OOR_SET(API_2(int_oor_set)))
DICT_OA_DEF2(dict3, int, INT_OA_OPLIST, int, M_DEFAULT_OPLIST)
CONCURRENT_DEF(pdict3, dict3_t, DICT_OPLIST(dict3))
/********************************/
parray1_t arr;
@@ -92,7 +125,7 @@ static void conso(void *p)
}
}
static void test(void)
static void test_thread(void)
{
m_thread_t idx;
parray1_init(arr);
@@ -118,6 +151,7 @@ static void test_basic(void)
bool b = pdict1_get_blocking(&z, dict, 2, false);
assert (b);
assert (z == 3);
assert (pdict1_size(dict) == 1);
pdict1_clear(dict);
M_LET(str, string_t)
@@ -132,8 +166,55 @@ static void test_basic(void)
}
}
rparray1_t rarr;
static void conso_rp(void *p)
{
assert (p == NULL);
for(int i = 0; i < 1000; i++) {
int j;
bool b = rparray1_pop_blocking(&j, rarr, true);
assert (b);
assert (j == i);
}
}
static void test_rp_thread(void)
{
m_thread_t idx;
rparray1_init(rarr);
m_thread_create (idx, conso_rp, NULL);
m_thread_sleep(1000);
for(int i = 0; i < 1000; i++) {
rparray1_push (rarr, i);
while (!rparray1_empty_p(rarr));
}
m_thread_join(idx);
rparray1_clear(rarr);
}
static void test_rp_basic(void)
{
rpdict1_t dict;
rpdict1_init(dict);
int z;
rpdict1_set_at(dict, 2, 3);
rpdict1_get_copy(&z, dict, 2);
assert (z == 3);
z = 1;
bool b = rpdict1_get_blocking(&z, dict, 2, false);
assert (b);
assert (z == 3);
assert (rpdict1_size(dict) == 1);
rpdict1_clear(dict);
}
int main(void)
{
test_basic();
test();
test_rp_basic();
test_thread();
test_rp_thread();
exit(0);
}
+82 -6
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -273,15 +273,15 @@ static void test_oplist(void)
assert (!M_OPLIST_P(func(INIT(init)) a));
assert (!M_OPLIST_P(func(INIT(init),) a));
#define a (INIT(0), CLEAR(clear))
assert (M_TEST_DISABLED_METHOD_P(INIT, a));
assert (!M_TEST_DISABLED_METHOD_P(INIT2, a));
assert (!M_TEST_DISABLED_METHOD_P(CLEAR, a));
#define aop (INIT(0), CLEAR(clear))
assert (M_TEST_DISABLED_METHOD_P(INIT, aop));
assert (!M_TEST_DISABLED_METHOD_P(INIT2, aop));
assert (!M_TEST_DISABLED_METHOD_P(CLEAR, aop));
assert (!M_TEST_DISABLED_METHOD_P(INIT, ()));
#define M_OPL_op() (INIT(1), CLEAR(0))
assert(M_GET_INIT M_GLOBAL_OPLIST(op));
assert(!M_GET_INIT M_GLOBAL_OPLIST(a));
assert(!M_GET_INIT M_GLOBAL_OPLIST(aop));
#define MAKE_OPLIST(op) (OPLIST(M_GLOBAL_OPLIST_OR_DEF(op)()))
#define M_OPL_uint() (INIT(0), CLEAR(1))
@@ -371,6 +371,81 @@ static void test_parse_standard_c_type(void)
#endif
}
typedef int ti[1];
typedef struct { int b,c; } ts;
static void f_ti(ti x)
{
ti y;
M_MOVE_A1_DEFAULT(y,x);
assert(y[0] == 9);
assert(x[0] == 0);
}
static void f_ts(ts x)
{
ts y;
M_MOVE_DEFAULT(y,x);
assert(y.b == 2);
assert(y.c == 3);
assert(x.b == 0);
assert(x.c == 0);
}
static void f_ts2(ts *x)
{
ts y;
M_MOVE_DEFAULT(y,*x);
assert(y.b == 2);
assert(y.c == 3);
assert(x->b == 0);
assert(x->c == 0);
}
static void test_move_default(void)
{
int o, p;
o = 9;
M_MOVE_DEFAULT(p, o);
assert(p == 9);
assert(o == 0);
double u, k;
u = 9.5;
M_MOVE_DEFAULT(k, u);
assert(k == 9.5);
assert(u == 0.0); // NOTE: memset(0) sets an IEEE-754 number to 0.0
int *ptr = &o, *m;
M_MOVE_DEFAULT(m, ptr);
assert( ptr == NULL);
assert( m == &o);
ti x, y;
x[0] = 9;
M_MOVE_A1_DEFAULT(y, x);
assert(y[0] == 9);
assert(x[0] == 0);
x[0] = 9;
f_ti(x);
assert(x[0] == 0);
ts y2, x2 = { .b = 2, .c = 3};
M_MOVE_DEFAULT(y2, x2);
assert(y2.b == 2 && y2.c == 3);
assert(x2.b == 0 && x2.c == 0);
x2.b = 2; x2.c = 3;
f_ts(x2);
assert(x2.b == 2);
assert(x2.c == 3);
f_ts2(&x2);
assert(x2.b == 0);
assert(x2.c == 0);
}
int main(void)
{
test_cat();
@@ -389,5 +464,6 @@ int main(void)
test_cast();
test_reduce();
test_parse_standard_c_type();
test_move_default();
exit(0);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+50 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -41,6 +41,12 @@ DICT_SET_DEF(dict_setstr, string_t, STRING_OPLIST)
DICT_DEF2(dict_int, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
DICT_DEF2(dict_mpz, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
BOUNDED_STRING_DEF(symbol, 15)
DICT_OA_DEF2(dict_oa_str, symbol_t, BOUNDED_STRING_OPLIST(symbol), int, M_DEFAULT_OPLIST)
DICT_OA_DEF2(dict_oa_bstr, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST)
DICT_OASET_DEF(dict_oa_setstr, string_t, STRING_OPLIST)
/* Helper structure */
ARRAY_DEF(array_string, string_t, STRING_OPLIST)
array_string_t v_str;
@@ -463,6 +469,47 @@ static void test_it_oa(void)
}
}
static void
test_oa_str1(void)
{
symbol_t s;
int *p;
dict_oa_str_t dict;
symbol_init(s);
dict_oa_str_init(dict);
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "x"), 1);
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "y"), 2);
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "z"), 3);
p = dict_oa_str_get_at(dict, BOUNDED_STRING_CTE(symbol, "x"));
assert(p != NULL);
assert(*p == 1);
dict_oa_str_clear(dict);
symbol_clear(s);
}
static void
test_oa_str2(void)
{
int *p;
dict_oa_bstr_t dict;
dict_oa_bstr_init(dict);
dict_oa_bstr_set_at(dict, STRING_CTE("x"), 1);
dict_oa_bstr_set_at(dict, STRING_CTE("y"), 2);
dict_oa_bstr_set_at(dict, STRING_CTE("z"), 3);
p = dict_oa_bstr_get_at(dict, STRING_CTE("x"));
assert(p != NULL);
assert(*p == 1);
dict_oa_bstr_clear(dict);
}
int main(void)
{
test1();
@@ -472,5 +519,7 @@ int main(void)
test_oa();
test_init_oa();
test_it_oa();
test_oa_str1();
test_oa_str2();
exit(0);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+155
View File
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "m-serial-bin.h"
#include "m-tuple.h"
#include "m-array.h"
#include "m-variant.h"
#include "m-list.h"
#include "m-dict.h"
// Serial bin is not supported for standard types if not C11
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
ARRAY_DEF(a2, int)
#define M_OPL_a2_t() ARRAY_OPLIST(a2, M_DEFAULT_OPLIST)
ARRAY_DEF(l2, int)
#define M_OPL_l2_t() ARRAY_OPLIST(l2, M_DEFAULT_OPLIST)
DICT_DEF2(d2, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST )
#define M_OPL_d2_t() DICT_OPLIST(d2, STRING_OPLIST, M_DEFAULT_OPLIST)
VARIANT_DEF2(v2,
(is_int, int),
(is_bool, bool) )
#define M_OPL_v2_t() VARIANT_OPLIST(v2, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
TUPLE_DEF2(my,
(vala, int),
(valb, float),
(valc, bool),
(vald, string_t),
(vale, a2_t),
(valf, v2_t),
(valg, l2_t),
(valh, d2_t)
)
#define M_OPL_my_t() TUPLE_OPLIST(my, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, STRING_OPLIST, M_OPL_a2_t(), M_OPL_v2_t(), M_OPL_l2_t(), M_OPL_d2_t() )
TUPLE_DEF2(my2,
(activated, bool),
(data, my_t, M_OPL_my_t() ) )
static void test_out_empty(void)
{
m_serial_read_t in;
m_serial_write_t out;
m_serial_return_code_t ret;
my2_t el1, el2;
my2_init(el1);
my2_init(el2);
FILE *f = fopen ("a-mbin.dat", "wt");
if (!f) abort();
m_serial_bin_write_init(out, f);
ret = my2_out_serial(out, el1);
assert (ret == M_SERIAL_OK_DONE);
m_serial_bin_write_clear(out);
fclose(f);
f = fopen ("a-mbin.dat", "rt");
if (!f) abort();
m_serial_bin_read_init(in, f);
ret = my2_in_serial(el2, in);
assert (ret == M_SERIAL_OK_DONE);
m_serial_bin_read_clear(in);
fclose(f);
assert (my2_equal_p (el1, el2));
my2_clear(el1);
my2_clear(el2);
}
my2_t el1, el2;
static void test_out_fill(void)
{
m_serial_read_t in;
m_serial_write_t out;
m_serial_return_code_t ret;
my2_init(el1);
my2_init(el2);
FILE *f;
el2->activated = true;
el2->data->vala = 145788;
el2->data->valb = -0.1;
el2->data->valc = false;
string_set_str(el2->data->vald, "This is a string test.");
a2_push_back(el2->data->vale, 1);
a2_push_back(el2->data->vale, 4);
a2_push_back(el2->data->vale, -5);
a2_push_back(el2->data->vale, 1458);
v2_set_is_int(el2->data->valf, 12356789);
l2_push_back(el2->data->valg, 1345);
l2_push_back(el2->data->valg, 46543);
l2_push_back(el2->data->valg, -5678);
d2_set_at(el2->data->valh, STRING_CTE("Paul"), 1);
d2_set_at(el2->data->valh, STRING_CTE("Smith"), 2);
f = fopen ("a-mbin.dat", "wt");
if (!f) abort();
m_serial_bin_write_init(out, f);
ret = my2_out_serial(out, el2);
assert (ret == M_SERIAL_OK_DONE);
m_serial_bin_write_clear(out);
fclose(f);
f = fopen ("a-mbin.dat", "rt");
if (!f) abort();
m_serial_bin_read_init(in, f);
ret = my2_in_serial(el1, in);
assert (ret == M_SERIAL_OK_DONE);
m_serial_bin_read_clear(in);
fclose(f);
assert (my2_equal_p (el1, el2));
my2_clear(el1);
my2_clear(el2);
}
int main(void)
{
//test_out_empty();
test_out_fill();
exit(0);
}
#else
int main(void)
{
exit(0);
}
#endif
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2017-2019, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "m-serial-json.h"
#include "m-tuple.h"
#include "m-array.h"
#include "m-variant.h"
#include "m-list.h"
#include "m-dict.h"
// Serial json is not supported for standard types if not C11
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
ARRAY_DEF(a2, int)
#define M_OPL_a2_t() ARRAY_OPLIST(a2, M_DEFAULT_OPLIST)
ARRAY_DEF(l2, int)
#define M_OPL_l2_t() ARRAY_OPLIST(l2, M_DEFAULT_OPLIST)
DICT_DEF2(d2, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST )
#define M_OPL_d2_t() DICT_OPLIST(d2, STRING_OPLIST, M_DEFAULT_OPLIST)
VARIANT_DEF2(v2,
(is_int, int),
(is_bool, bool) )
#define M_OPL_v2_t() VARIANT_OPLIST(v2, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
TUPLE_DEF2(my,
(vala, int),
(valb, float),
(valc, bool),
(vald, string_t),
(vale, a2_t),
(valf, v2_t),
(valg, l2_t),
(valh, d2_t)
)
#define M_OPL_my_t() TUPLE_OPLIST(my, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, STRING_OPLIST, M_OPL_a2_t(), M_OPL_v2_t(), M_OPL_l2_t(), M_OPL_d2_t() )
TUPLE_DEF2(my2,
(activated, bool),
(data, my_t, M_OPL_my_t() ) )
static void test_out_empty(void)
{
m_serial_read_t in;
m_serial_write_t out;
m_serial_return_code_t ret;
my2_t el1, el2;
my2_init(el1);
my2_init(el2);
FILE *f = fopen ("a-mjson.dat", "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);
f = fopen ("a-mjson.dat", "rt");
if (!f) abort();
static const char expected[] = "{ \"activated\":false,\"data\":{ \"vala\":0,\"valb\":0.000000,\"valc\":false,\"vald\":\"\",\"vale\":[],\"valf\":{},\"valg\":[],\"valh\":{}}";
char get[sizeof expected];
fgets (get, sizeof expected , f);
assert (strcmp(get, expected) == 0);
fclose(f);
f = fopen ("a-mjson.dat", "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);
assert (my2_equal_p (el1, el2));
my2_clear(el1);
my2_clear(el2);
}
static void test_out_fill(void)
{
m_serial_read_t in;
m_serial_write_t out;
m_serial_return_code_t ret;
my2_t el1, el2;
my2_init(el1);
my2_init(el2);
FILE *f = fopen("a-mjson.dat", "wt");
if (!f) abort();
fprintf(f,
"{\n"
" \"activated\":false,\n"
"\"data\": {\n"
" \"valb\": -2.300000 , \n"
"\"vale\": [1,2,3],\n"
"\"valg\": [1,2,3,4,5,6],\n"
"\"valh\": { \"jane\": 3, \"steeve\": -4 },\n"
"\"valf\": { \"is_bool\": true },\n"
" \"vala\":1742,\n"
" \"vald\": \"This is a test\",\n"
" \"valc\": true } }\n");
fclose(f);
f = fopen ("a-mjson.dat", "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);
f = fopen ("a-mjson.dat", "wt");
if (!f) abort();
m_serial_json_write_init(out, f);
ret = my2_out_serial(out, el2);
assert (ret == M_SERIAL_OK_DONE);
m_serial_json_write_clear(out);
fclose(f);
f = fopen ("a-mjson.dat", "rt");
if (!f) abort();
static const char expected[] = "{ \"activated\":false,\"data\":{ \"vala\":1742,\"valb\":-2.300000,\"valc\":true,\"vald\":\"This is a test\",\"vale\":[1,2,3],\"valf\":{\"is_bool\":true},\"valg\":[1,2,3,4,5,6],\"valh\":{\"steeve\":-4,\"jane\":3}}}";
static const char expected2[] = "{ \"activated\":false,\"data\":{ \"vala\":1742,\"valb\":-2.300000,\"valc\":true,\"vald\":\"This is a test\",\"vale\":[1,2,3],\"valf\":{\"is_bool\":true},\"valg\":[1,2,3,4,5,6],\"valh\":{\"jane\":3,\"steeve\":-4}}}";
char get[sizeof expected];
fgets (get, sizeof expected , f);
assert (strcmp(get, expected) == 0 || strcmp(get, expected2) == 0);
fclose(f);
f = fopen ("a-mjson.dat", "rt");
if (!f) abort();
m_serial_json_read_init(in, f);
ret = my2_in_serial(el1, in);
assert (ret == M_SERIAL_OK_DONE);
m_serial_json_read_clear(in);
fclose(f);
assert (my2_equal_p (el1, el2));
my2_clear(el1);
my2_clear(el2);
}
int main(void)
{
test_out_empty();
test_out_fill();
exit(0);
}
#else
int main(void)
{
exit(0);
}
#endif
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+6 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -34,16 +34,18 @@ typedef struct {
unsigned int c;
unsigned long long spare2;
unsigned int p;
void *v;
} data_t;
static void data_crc(data_t *p) { p->p = -p->n; p->c = ~p->n; }
static void data_init(data_t *p) { p->n = 0; p->spare1 = 0; p->spare2 = 0; data_crc(p); }
static void data_init(data_t *p) { p->n = 0; p->spare1 = 0; p->spare2 = 0; data_crc(p); p->v = calloc(1, 1); assert(p->v); }
static void data_valid_p(const data_t *p) { assert (p->n == -p->p && p->n == ~p->c); }
static void data_clear(data_t *p) { (void) p;}
static void data_clear(data_t *p) { free(p->v); }
static void data_set(data_t *p, data_t o) { p->n = o.n; data_crc(p); }
static void data_init_set(data_t *p, data_t o) { data_init(p); data_set(p, o); }
#define DATA_OPLIST \
(INIT(data_init M_IPTR), INIT_SET(data_set M_IPTR), SET(data_set M_IPTR), CLEAR(data_clear M_IPTR))
(INIT(data_init M_IPTR), INIT_SET(data_init_set M_IPTR), SET(data_set M_IPTR), CLEAR(data_clear M_IPTR))
START_COVERAGE
SNAPSHOT_SPSC_DEF(snapshot_uint, unsigned int)
+59 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -91,12 +91,17 @@ static void test_utf8_basic(void)
static void test_utf8_it(void)
{
string_t s;
string_it_t it;
string_init(s);
for(string_it(it, s) ; !string_end_p(it); string_next(it)) {
assert(0); // Shall not be called
}
const unsigned int tab[] = { 45, 1458, 25623, 129, 24 };
for(int i = 0; i < 5; i++)
string_push_u (s, tab[i]);
assert (string_length_u(s) == 5);
string_it_t it;
int i = 0;
for(string_it(it, s) ; !string_end_p(it); string_next(it), i++) {
assert (i < 5);
@@ -269,8 +274,13 @@ static void test0(void)
string_set_str(s1, "Hello, world!");
assert(string_start_with_str_p(s1, "Hello") == true);
assert(string_start_with_str_p(s1, "Help") == false);
assert(string_end_with_str_p(s1, "world!") == true);
assert(string_end_with_str_p(s1, "worldX") == false);
assert(string_end_with_str_p(s1, "Hello, world!!") == false);
string_set_str(s2, "H");
assert(string_start_with_string_p(s1, s2) == true);
string_set_str(s2, "!");
assert(string_end_with_string_p(s1, s2) == true);
string_cat_printf(s1, " %d little %s.", 10, "suns");
string_set_str(s2, "Hello, world! 10 little suns.");
@@ -345,6 +355,13 @@ static void test0(void)
char *s = string_clear_get_str (s1);
assert(strcmp(s, "QWERTYQWERTY") == 0);
free(s);
string_t s3;
string_init(s3);
string_cat_str(s3, "ABC");
s = string_clear_get_str(s3);
assert(strcmp(s, "ABC") == 0);
free(s);
string_init_set_str(s1, "RESTART");
assert (string_equal_str_p(s1, "RESTART"));
@@ -384,6 +401,9 @@ static void test0(void)
string_init (s1);
string_init (s2);
size_t h1 = string_hash(s1);
assert (h1 != 0);
string_printf (s1, "Hello %d worlds. How do you do? I'm fine. Thank you! The weather is bad today. I should had brought my umbrella. Oh! You can lend me one! Thank you very much! No really thank you. I wouldn't be able to get in time for my job.", 2);
assert (string_equal_str_p(s1, "Hello 2 worlds. How do you do? I'm fine. Thank you! The weather is bad today. I should had brought my umbrella. Oh! You can lend me one! Thank you very much! No really thank you. I wouldn't be able to get in time for my job."));
@@ -447,7 +467,7 @@ static void test0(void)
string_set_str (s1, "");
assert (string_capacity(s1) > 0);
string_reserve (s1, 0);
assert (string_capacity(s1) == 0);
assert (string_capacity(s1) == sizeof (string_heap_t) - 1);
string_set_str(s1, "HELLO XXX!");
string_replace_at(s1, 6, 3, "World");
@@ -518,6 +538,41 @@ static void test_bounded1(void)
string16_clear(s);
}
static void test_bounded_io(void)
{
string16_t s, d;
bool b;
FILE *f;
string16_init(s);
string16_init(d);
string16_set_str(s, "Hello \"world\"");
f = fopen ("a-mstring.dat", "wt");
assert (f != NULL);
string16_out_str (f, s);
fclose (f);
f = fopen("a-mstring.dat", "rt");
assert (f != NULL);
b = string16_in_str(d, f);
assert(b);
fclose(f);
assert (string16_equal_p(s, d));
const char *end;
string_t str;
string_init(str);
string16_get_str(str, s, false);
assert(string_equal_str_p(str, "\"Hello \\\"world\\\"\""));
b = string16_parse_str(d, string_get_cstr(str), &end);
assert (b);
assert (string16_equal_p(s, d));
string_clear(str);
string16_clear(s);
string16_clear(d);
}
int main(void)
{
test0();
@@ -525,5 +580,6 @@ int main(void)
test_utf8_basic();
test_utf8_it();
test_bounded1();
test_bounded_io();
exit(0);
}
+5 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -60,6 +60,10 @@ TUPLE_DEF2(pair2_str,
(str, string_t),
(value, unsigned long))
TUPLE_DEF2(int_float,
(vala, int),
(valb, float))
// Recursive tuple definition.
TUPLE_DEF2(rtuple,
(ituple, pair_str_t),
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Patrick Pelissier
* Copyright (c) 2017-2019, 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:
@@ -76,7 +76,7 @@ static void test1bis(void)
assert (atomic_load(&resetFunc_called) == true || workeri_get_cpu_count() == 1);
}
#if defined(__GNUC__) && (!defined(__clang__) || WORKER_USE_CLANG_BLOCK || WORKER_USE_CPP_FUNCTION)
#if defined(__GNUC__) && (!defined(__clang__) || (defined(WORKER_USE_CLANG_BLOCK) && WORKER_USE_CLANG_BLOCK) || (defined(WORKER_USE_CPP_FUNCTION) && WORKER_USE_CPP_FUNCTION))
/* The macro version will generate warnings about shadow variables.
There is no way to avoid this. */
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more