1300 Commits
Author SHA1 Message Date
Patrick Pelissier e694d83cc4 V0.4.0 2020-05-27 21:50:03 +02:00
Patrick Pelissier 19fc150763 Discard warning with clang++ 2020-05-27 21:41:31 +02:00
Patrick Pelissier 3784f653d9 Fix warning with CLANG 2020-05-27 21:41:11 +02:00
Patrick Pelissier eb40cff6a1 Improve checkall target to stop on warning 2020-05-27 21:09:07 +02:00
Patrick Pelissier b7f84236d3 * Update comments
* Micro optimization
2020-05-25 21:41:16 +02:00
Patrick Pelissier 65e3f1049b * Update comments.
* Fix position of const for some prototypes.
2020-05-25 00:23:12 +02:00
Patrick Pelissier cc45b9c1fe Fix issue in test suite. 2020-05-24 20:39:20 +02:00
Patrick Pelissier 9340c2e55c Fix memory leak for _parse_str in case of error. 2020-05-24 19:50:32 +02:00
Patrick Pelissier 9710e014f2 Reindent function 2020-05-24 19:19:19 +02:00
Patrick Pelissier 7761fb6bf1 Add contract for tuple & variant. 2020-05-24 19:08:18 +02:00
Patrick Pelissier 2f4df5f67c Fix potential read before init in test. 2020-05-24 19:07:29 +02:00
Patrick Pelissier b841f81416 Fix package name for MS Windows
The filesystem doesn't support '*'
2020-05-24 18:14:25 +02:00
Patrick Pelissier 72fd53ab44 Update comments 2020-05-24 12:01:56 +02:00
Patrick Pelissier 8335bc525b Add comments 2020-05-23 16:09:18 +02:00
Patrick Pelissier 9e5d14ce36 Factorization & add tests 2020-05-21 13:10:50 +02:00
Patrick Pelissier 3bafede8b7 * Make the test more pertinent 2020-05-21 12:32:33 +02:00
Patrick Pelissier 3f2904ab0d * Add comment 2020-05-21 12:32:20 +02:00
Patrick Pelissier 933b495ed1 * Optimization for M_LET for a string_t in case of constant non formatted string
* Support for string_t in M_LET for string_t in C11 mode.
* Adding associate test
2020-05-21 12:19:17 +02:00
Patrick Pelissier 77813adc00 Add example of formatted C string for string in M_LET 2020-05-21 11:55:35 +02:00
Patrick Pelissier ce2adea6c0 Update documentation 2020-05-21 09:54:08 +02:00
Patrick Pelissier 3ecdfff8f0 Remove more cleanly generated configuration file. 2020-05-21 00:54:24 +02:00
Patrick Pelissier a103ceb850 Add new example of array of string. 2020-05-21 00:36:35 +02:00
Patrick Pelissier 9f24b89487 Add debug flag for example 2020-05-21 00:36:13 +02:00
Patrick Pelissier 8be06cadb3 Add comments on description. 2020-05-20 23:21:27 +02:00
Patrick Pelissier c488d979cf Ignore Visual Code directory if any. 2020-05-20 23:05:28 +02:00
Patrick Pelissier f4c24f07af Update comment description of macros and functions 2020-05-20 22:36:36 +02:00
Patrick Pelissier 3d2ce63d63 Fix performance issue in benchmarks 2020-05-19 23:21:00 +02:00
Patrick Pelissier 0ac2c62ffe Port to new API 2020-05-19 22:57:08 +02:00
Patrick Pelissier 6001781e68 Cleanup namespace, add comments, and remove dead code 2020-05-17 21:39:24 +02:00
Patrick Pelissier d90cb7caf3 Update doc 2020-05-17 12:11:10 +02:00
Patrick Pelissier 88a4835025 Fix issue when defining algorithm with same prefix than container 2020-05-17 12:11:04 +02:00
Patrick Pelissier 75ae7a06e5 Add missing tests 2020-05-17 11:53:43 +02:00
Patrick Pelissier 3885cb29a2 Add control of type and oplists 2020-05-17 11:53:27 +02:00
Patrick Pelissier bf3ffd1766 Add test cases for new control 2020-05-17 11:10:06 +02:00
Patrick Pelissier d884d257bb Export TYPE operator in oplist 2020-05-17 10:53:17 +02:00
Patrick Pelissier a87cac84dd Move up control so that it is the first error reported by the compiler. 2020-05-17 10:52:54 +02:00
Patrick Pelissier d1ae26f0fd * Add control than the type and the oplist are compatible 2020-05-17 10:27:30 +02:00
Patrick Pelissier 9c58e73c37 * Add M_CHECK_COMPATIBLE
* Fix issue in oplist
2020-05-17 10:26:40 +02:00
Patrick Pelissier 024b62a7a7 Add instance for M_CHECK_COMPATIBLE to be able to check multiple types within one container. 2020-05-17 10:26:08 +02:00
Patrick Pelissier c59731775e * Provide alternative for M_STATIC_ASSERT in GNUC + C11 with improved error messages
* Add internal macro M_CHECK_COMPATIBLE_OPLIST to check if a type and an oplist could match.
2020-05-17 09:36:50 +02:00
Patrick Pelissier 4668476c6a Fix issue with old version of ICC 2020-05-16 14:47:30 +02:00
Patrick Pelissier 743959ebde Fix micro performance issue 2020-05-16 14:09:49 +02:00
Patrick Pelissier dd27fa7e72 Add coverage for Function Object 2020-05-16 10:24:29 +02:00
Patrick Pelissier f2025f83d3 Fix help usage 2020-05-16 09:41:05 +02:00
Patrick Pelissier 8c06b43b2b Update tests for new controls 2020-05-15 21:46:18 +02:00
Patrick Pelissier 3cde7a1731 Add protection for use of oplist within a container oplist 2020-05-15 21:46:03 +02:00
Patrick Pelissier e9db1d28b8 Try to work around to print the oplist 2020-05-15 21:45:21 +02:00
Patrick Pelissier adffaf0e87 Fix illegal usage of oplist for function object 2020-05-15 21:45:02 +02:00
Patrick Pelissier 49a2e600c4 Add control of sub-oplist when creating a new oplist of a container
Add test case of this new control
Fix some corner bugs in some containers evolving their own oplists.
2020-05-15 21:19:39 +02:00
Patrick Pelissier 0bedcba32b Rewrite M_STATIC_ASSERT to include an error code and message
Add more control on the default expanded macros to check if arguments are compatible
2020-05-15 14:51:20 +02:00
Patrick Pelissier c531f805b0 Define _reduce only if SET method is properly set 2020-05-15 14:31:47 +02:00
Patrick Pelissier da0ab5ea98 Update failure tests as per latest checks 2020-05-11 20:55:16 +02:00
Patrick Pelissier 6774afd751 Add checks of missing oplist in the last _DEF macros 2020-05-11 20:54:50 +02:00
Patrick Pelissier 6d489136e7 Update help 2020-05-10 22:18:19 +02:00
Patrick Pelissier 4ea6dbc7fa Update makefile as per latest version of library 2020-05-10 20:21:03 +02:00
Patrick Pelissier defc58f6a3 Update bench code to latest version of the library. 2020-05-10 20:20:37 +02:00
Patrick Pelissier 006d0d16a5 Fix error 2020-05-10 20:20:03 +02:00
Patrick Pelissier b8af8bb344 Redisable MSVC testing with new version of compiler 2020-05-10 12:40:37 +02:00
Patrick Pelissier e249be1142 Reenable MSVC testing with new version of compiler 2020-05-10 12:38:36 +02:00
Patrick Pelissier 05fde6c3cf Display MSVC compiler full version 2020-05-10 12:06:31 +02:00
Patrick Pelissier aaa1d7dc4b Update description of test 2020-05-10 11:20:16 +02:00
Patrick Pelissier 8c479b2e5a Update tests 2020-05-10 11:17:25 +02:00
Patrick Pelissier 45d2a529b5 Add more validation of oplists before expanding data 2020-05-10 11:16:46 +02:00
Patrick Pelissier 7d17223332 Update comments 2020-05-10 11:15:56 +02:00
Patrick Pelissier 12bc65b6a8 Fix spelling 2020-05-10 11:15:28 +02:00
Patrick Pelissier a849d5c031 Make check-failure tests parallel 2020-05-09 21:39:01 +02:00
Patrick Pelissier f195b09657 Add more tests 2020-05-09 21:38:48 +02:00
Patrick Pelissier 439177ec71 Add protection against wrong oplist in more headers 2020-05-09 21:38:32 +02:00
Patrick Pelissier aadd4f11b1 Update tests as per new checks 2020-05-09 17:16:11 +02:00
Patrick Pelissier 113fdbf388 Add more controls on proper presence of oplist for _DEF macros 2020-05-09 17:16:00 +02:00
Patrick Pelissier 648406a99a Update tests as per new checks 2020-05-09 16:11:11 +02:00
Patrick Pelissier 055e74d350 Add more check for valid oplists before expanding further 2020-05-09 16:10:56 +02:00
Patrick Pelissier ac3d7568f2 Update test 2020-05-09 11:20:05 +02:00
Patrick Pelissier 6c222b9ad7 Raise proper failure if M_let is used without an oplist
Raise proper failure if a TYPE operator is used in an oplist that doesn't export it.
2020-05-09 11:15:44 +02:00
Patrick Pelissier 4426a60904 Add test for testing expected failures 2020-05-09 10:56:31 +02:00
Patrick Pelissier 34f70a031f Use M_STATIC_FAILURE to stop expanding methods in case of detected failure 2020-05-09 10:56:05 +02:00
Patrick Pelissier 79f2f396b9 Add macro M_STATIC_FAILURE to abort expansion in case of error 2020-05-09 10:55:25 +02:00
Patrick Pelissier 88143f7d52 Update ignore file 2020-05-09 10:54:30 +02:00
Patrick Pelissier c1261dd0f2 Fix doc 2020-05-08 21:47:19 +02:00
Patrick Pelissier 8f7a60ea73 Add documentation for function object 2020-05-08 21:43:35 +02:00
Patrick Pelissier 4758bbd6cc Add more tests 2020-05-08 21:23:48 +02:00
Patrick Pelissier 3f66325c19 Add new example for function object, json & algorithms 2020-05-08 17:50:07 +02:00
Patrick Pelissier 6f10d7deb9 Add synonym of serialisater with specialized oplists to be used in RAII 2020-05-08 17:49:44 +02:00
Patrick Pelissier 919900c90a Update documentation 2020-05-08 17:48:56 +02:00
Patrick Pelissier e6daaf429c Add some tests for function objects 2020-05-08 11:37:38 +02:00
Patrick Pelissier 1200f30695 * Add more Function Object support in algorithm
* Fix bug of selection sort
2020-05-08 11:35:22 +02:00
Patrick Pelissier fca1120990 No longer export IT_REF for map containers (See API changes) 2020-05-08 11:33:28 +02:00
Patrick Pelissier 3735704c95 Update new API change 2020-05-08 11:30:57 +02:00
Patrick Pelissier 8c0c1b3102 Update documentation of reduce if the container is empty 2020-05-08 11:30:38 +02:00
Patrick Pelissier db192993f1 Reduce constraint as the dictionnary iteration is not modifiable 2020-05-08 00:27:15 +02:00
Patrick Pelissier 4f44b3f60b Add CMP callback 2020-05-07 22:13:36 +02:00
Patrick Pelissier b196fcc3c0 Rename _cmp_cb_t into _eq_cb_t 2020-05-07 22:10:53 +02:00
Patrick Pelissier 395f1af19e Implement more function object algorithm 2020-05-07 22:09:07 +02:00
Patrick Pelissier d8f761e889 Update Makefile for dist 2020-05-05 23:44:27 +02:00
Patrick Pelissier 563ffb4275 Add first support for function object for certain algos (WIP) 2020-05-05 23:34:34 +02:00
Patrick Pelissier 9ba2de4b70 Add macro M_IF_FUNCOBJ to handle conditional expression if the function object is defined 2020-05-05 23:34:12 +02:00
Patrick Pelissier 0ac6a53fe9 Define callback types 2020-05-05 22:57:19 +02:00
Patrick Pelissier 4431e9f9ae Factorization / Split of the definition of the algorithm 2020-05-05 22:40:03 +02:00
Patrick Pelissier 48526ff5c7 Simplify logic due to fix of M_PARENTHESIS_P 2020-05-04 00:33:00 +02:00
Patrick Pelissier 4ee453025c Add comments 2020-05-04 00:32:40 +02:00
Patrick Pelissier 1e7b543c0f Fix issue with oplists with 3 arguments and fix warnings 2020-05-04 00:30:14 +02:00
Patrick Pelissier 8c2b4f8cf9 Add test cases for function object 2020-05-04 00:29:42 +02:00
Patrick Pelissier 13cadfa470 Update description of the list of headers 2020-05-03 18:00:46 +02:00
Patrick Pelissier deb595372b Add first version of Function Object 2020-05-03 17:49:02 +02:00
Patrick Pelissier 01fd5d929e * Fix M_RET_ARG so that it accepts also a list of the exact number of arguments (instead of one more) * Add more keywords for pattern recognation 2020-05-03 13:13:45 +02:00
Patrick Pelissier 2d60bd2c1b Fix a bug in M_PARENTHESIS_P 2020-05-02 10:19:31 +02:00
Patrick Pelissier 9655b13203 Add more corner cases for M_PARENTHESIS_P 2020-05-02 10:18:52 +02:00
Patrick Pelissier 6b97a7436d Add basic tests for dispathing init_with based on their number of args 2020-05-02 10:06:30 +02:00
Patrick Pelissier 1c792dd0a4 Add option for init_with to dispatch to different methods in function of the number of args 2020-05-02 10:06:08 +02:00
Patrick Pelissier dcbd9f496f Document NAME operator 2020-05-02 09:56:23 +02:00
Patrick Pelissier 9804120cde * Add simple test for registration of int global operator 2020-05-02 09:56:11 +02:00
Patrick Pelissier a9b06718f0 * Add simple test for NAME operator 2020-05-02 09:55:28 +02:00
Patrick Pelissier 963d819e22 * Add NAME operator. * Register default oplist for char,short,int,long,float,double 2020-05-02 09:55:10 +02:00
Patrick Pelissier 28847af43f Update constraints on MOVE & INIT_MOVE operators 2020-04-27 23:12:06 +02:00
Patrick Pelissier 130fa36970 Update iterator constraint 2020-04-27 22:55:53 +02:00
Patrick Pelissier ab99035291 Add another example 2020-04-20 23:50:05 +02:00
Patrick Pelissier 6de43d8a45 Add doc for ALGO_REDUCE with different type 2020-04-20 23:49:48 +02:00
Patrick Pelissier 7b46891622 Add test for ALGO_REDUCE with different type 2020-04-20 23:49:35 +02:00
Patrick Pelissier a0a32d8c6c ALGO_REDUCE accept dest with different type than the container 2020-04-20 23:49:16 +02:00
Patrick Pelissier 1ffd07dc6e Update format 2020-04-11 00:25:40 +02:00
Patrick Pelissier cb49cd7100 * Fix integer overflow in test of mul reduction 2020-04-10 08:35:44 +02:00
Patrick Pelissier 923b340a80 * Update documentation 2020-04-10 08:29:55 +02:00
Patrick Pelissier 5b8750ee2a * Add support for product in ALGO_REDUCE 2020-04-10 08:28:16 +02:00
Patrick Pelissier d438d089fb Add note on the provided oplists 2020-04-10 08:19:59 +02:00
Patrick Pelissier 843ddac3dd Proper prefix internal variables of macros by '_' 2020-04-01 23:40:41 +02:00
Patrick Pelissier c5238e0a4c Add new example for algorithm 2020-04-01 23:40:16 +02:00
Patrick Pelissier f99ee8aa40 Update documentation 2020-04-01 23:28:07 +02:00
Patrick Pelissier 18c7d0e11a Add test for SET_KEY only containers for ALGO_EXTRACT 2020-04-01 23:27:38 +02:00
Patrick Pelissier 4e67823593 Add support for SET_KEY only containers for ALGO_EXTRACT 2020-04-01 23:27:20 +02:00
Patrick Pelissier e7fcefb391 * Dict supports INIT_WITH method too. 2020-03-29 00:49:36 +01:00
Patrick Pelissier be06426783 Add documentation for ALGO_TRANSFORM 2020-03-28 17:41:29 +01:00
Patrick Pelissier e558e83be3 Add test case for ALGO_TRANSFORM 2020-03-28 17:41:16 +01:00
Patrick Pelissier d77a235b2a Add macro ALGO_TRANSFORM 2020-03-28 17:41:04 +01:00
Patrick Pelissier 3add6bb5bf Improve test suite 2020-03-28 17:17:43 +01:00
Patrick Pelissier 4367daaa00 Add documentation for _transform method. 2020-03-28 13:34:17 +01:00
Patrick Pelissier 4d46b3d126 Add basic test cases for _transform method. 2020-03-28 13:34:02 +01:00
Patrick Pelissier 548cd84a7f Add _transform method for algorithm. 2020-03-28 13:33:43 +01:00
Patrick Pelissier d87ffaa572 Improve documentation. 2020-03-28 13:07:35 +01:00
Patrick Pelissier 370cdb4e45 Add another example. 2020-03-28 13:05:15 +01:00
Patrick Pelissier 068d023917 Factorize in m-core.h the pattern recognication. 2020-03-28 11:36:51 +01:00
Patrick Pelissier 86b6ad54d5 Rename filln, filla, fillan into fill_n, fill_a, fill_an (same name as C++) 2020-03-28 11:10:02 +01:00
Patrick Pelissier bdf999a9fc Disable fully VC++ build as the build is fully broken due to regression in version 16.5 vs 16.4 2020-03-26 22:34:51 +01:00
Patrick Pelissier 39f8b6828b Try workaround against regression with new VisualC++ 2020-03-26 22:03:11 +01:00
Patrick Pelissier 11f84ab34b Document new algorithms 2020-03-26 21:49:04 +01:00
Patrick Pelissier c3a5306266 Add function _fill, _filln, _filla, _fillan as algorithms 2020-03-26 21:48:46 +01:00
Patrick Pelissier 1fe9e4c14c Export ADD / MUL / DIV / SUB for basic C types. 2020-03-26 21:44:19 +01:00
Patrick Pelissier e69203dc50 Remove exported PUSH / POP methods as they don't follow stricly the prototypes of such functions. 2020-03-26 21:43:44 +01:00
Patrick Pelissier 342954f272 Fix introduced build error for Mingw 2020-03-25 11:56:38 +01:00
Patrick Pelissier 741d6b8828 Test string + algorithm 2020-03-25 11:44:35 +01:00
Patrick Pelissier f1f41ef320 * Add missing iterator functions * Export iterators in oplist so that it is usable in algorithm 2020-03-25 11:43:55 +01:00
Patrick Pelissier 785792d1f4 Allow build of examples in C11 mode 2020-03-25 11:17:06 +01:00
Patrick Pelissier 9affe3106c Add example of ALGO_FOR_EACH and ALGO_REDUCE 2020-03-25 11:16:19 +01:00
Patrick Pelissier eb9fd14e2c Fix documentation. 2020-03-25 10:19:43 +01:00
Patrick Pelissier c281a2c339 Update CI script to include VC++ 2020-03-02 18:09:53 +01:00
Patrick Pelissier cb109eb479 Add proper progression for special script 2020-03-02 14:35:49 +01:00
Patrick Pelissier 3c6d194974 Add script to launch the test suite with VC++ 2020-03-02 14:25:42 +01:00
Patrick Pelissier 653a0d9734 Fix build issue for systems where double==long double 2020-03-02 14:23:59 +01:00
Patrick Pelissier a5fbd8405c Avoid C99 construct incompatible with strict C++ compiler 2020-02-24 22:27:43 +01:00
Patrick Pelissier 70994d4f60 Update doc 2020-02-16 11:06:43 +01:00
Patrick Pelissier f15203afb2 Comment a little bit more the examples 2020-02-10 06:47:24 +01:00
Patrick Pelissier 0a0d707afd Document bitset 2020-02-08 23:39:30 +01:00
Patrick Pelissier e206c3fe48 Document SPSC buffer 2020-01-23 23:18:33 +01:00
Patrick Pelissier 7f5704d321 Document MPMC buffer 2020-01-09 22:52:02 +01:00
Patrick Pelissier 4f7b0b9455 Happy new year 2020 2020-01-04 15:41:06 +01:00
Patrick Pelissier f5f16015df In quiet mode, if the result of the bench is not assumed to be reliable, display -1 instead of the result. 2020-01-04 15:12:59 +01:00
Patrick Pelissier 935325e0cb Add bench of emilib 2020-01-04 15:06:24 +01:00
Patrick Pelissier 7b9530ac58 Fix name of bench 2020-01-04 15:05:12 +01:00
Patrick Pelissier a9fc259420 ALGO_EXTRACT func argument is now optional 2019-12-30 14:31:21 +01:00
Patrick Pelissier 6f603b52df Update comments 2019-12-30 14:11:41 +01:00
Patrick Pelissier aeb2a47952 Add comments to example 2019-12-30 13:44:03 +01:00
Patrick Pelissier ca7cea3fe3 Use of linear probing and the same hash function as other libraries 2019-12-30 00:12:05 +01:00
Patrick Pelissier f14aca8808 Port to new UDB2 protocol 2019-12-29 23:51:30 +01:00
Patrick Pelissier f1e5e9d9db Simplify code to use get_at method with its get & create semantic 2019-12-29 23:45:17 +01:00
Patrick Pelissier cecba9777a Update documentation on buffer/queues 2019-12-27 16:42:54 +01:00
Patrick Pelissier 4352267034 Update deprecated API 2019-12-27 16:42:00 +01:00
Patrick Pelissier 0c3730d9a3 Migrate version to 0.3.1 2019-12-27 16:41:42 +01:00
Patrick Pelissier 5111b0c726 Deprecated some buffer options since the _blocking functions are much more usefull 2019-12-27 16:39:42 +01:00
Patrick Pelissier 8ecd3f1592 Add best_within and variance 2019-12-23 23:05:48 +01:00
Patrick Pelissier 0a6f8851c9 Update list of bench to launch automatically 2019-12-20 21:37:51 +01:00
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
Patrick Pelissier 5d5c2380ed Update release notes due to valgrind being unable to launch the test suite and scan-build producing too many false positives. 2018-11-16 22:54:04 +01:00
Patrick Pelissier a8b5c3ff4e Improve assertions 2018-11-10 17:54:13 +01:00
Patrick Pelissier 16587aeb93 Update doc 2018-11-08 20:55:28 +01:00
Patrick Pelissier 629a3a3dc1 Update depend 2018-11-08 20:54:42 +01:00
Patrick Pelissier ea46379d24 Update long term issue. 2018-11-07 19:54:07 +01:00
Patrick Pelissier ee0c77edc8 Replace SET+CLEAR by MOVE. 2018-11-04 20:28:56 +01:00
Patrick Pelissier c0610b2d60 Update doc 2018-11-04 20:15:15 +01:00
Patrick Pelissier 13ab545246 Update example for concurrent 2018-11-03 19:05:57 +01:00
Patrick Pelissier d85d6bcb13 Update doc 2018-11-01 17:21:11 +01:00
Patrick Pelissier 138662d6a0 Update checkall 2018-11-01 16:29:55 +01:00
Patrick Pelissier e45437e19f Add missing const 2018-11-01 16:11:42 +01:00
Patrick Pelissier 725846e155 Add missing type 2018-11-01 16:11:27 +01:00
Patrick Pelissier 74aa514903 Add missing const 2018-11-01 16:11:12 +01:00
Patrick Pelissier 7f7230be90 Fix doc 2018-10-31 17:38:04 +01:00
Patrick Pelissier d4a6e8c47a Add doc for m-concurrent 2018-10-31 17:37:17 +01:00
Patrick Pelissier 9c19ed4471 Add more encapsulation tests. 2018-10-31 11:33:58 +01:00
Patrick Pelissier 357c37c69d Add missing const qualifier 2018-10-31 11:33:41 +01:00
Patrick Pelissier 7c2d9c8643 Add missing const 2018-10-31 11:31:48 +01:00
Patrick Pelissier 69d699b80e Add contract 2018-10-31 11:26:00 +01:00
Patrick Pelissier 40832dd727 Add assertions 2018-10-31 08:24:06 +01:00
Patrick Pelissier 31c3baa42b Update comment 2018-10-30 20:10:15 +01:00
Patrick Pelissier 0ed03224c2 Update test 2018-10-30 20:04:01 +01:00
Patrick Pelissier f8d82ae74a Fix oplist definition. 2018-10-30 20:00:50 +01:00
Patrick Pelissier 16234bde82 Add const 2018-10-30 17:23:59 +01:00
Patrick Pelissier 31a5d7ee3b Add oplist 2018-10-30 10:56:02 +01:00
Patrick Pelissier f908c7e04d Update 2018-10-30 10:27:29 +01:00
Patrick Pelissier eb20f66bd9 Update 2018-10-30 10:25:22 +01:00
Patrick Pelissier 3b3e853cf8 Add first test cases 2018-10-29 23:07:34 +01:00
Patrick Pelissier 998ae05a5b Update dict_set to be like a B+TREE (API breakage). _ref returns pointer to the data 2018-10-29 22:59:31 +01:00
Patrick Pelissier 155d01b8c3 Fix blocking=false for _pop & _get 2018-10-29 18:10:07 +01:00
Patrick Pelissier 1a4cce7efd Add concurrent benchmark 2018-10-29 17:17:14 +01:00
Patrick Pelissier 3a0dd2ffe7 Fix errors 2018-10-29 17:15:20 +01:00
Patrick Pelissier 68b705b16a Proper export of additional methods for DICT_SET 2018-10-29 17:02:58 +01:00
Patrick Pelissier fe245e76cf Fix DICT_SET_DEF macro 2018-10-29 16:51:33 +01:00
Patrick Pelissier 618e37b07c Remove pop operator as there is no method for it. 2018-10-29 16:51:17 +01:00
Patrick Pelissier 3dde8d45c1 Add _empty_p & _pop_move_blocking 2018-10-29 13:26:16 +01:00
Patrick Pelissier 4691f95b73 Add blocking methods for concurrent container. 2018-10-29 11:18:20 +01:00
Patrick Pelissier 02d4bf0e09 Export method _empty_p for operator TEST_EMPTY for all containers. 2018-10-29 11:06:54 +01:00
Patrick Pelissier 5ecf4f37c0 Change deprecated functions. 2018-10-29 11:06:29 +01:00
Patrick Pelissier 15f6484dd2 Fix comment 2018-10-29 11:02:55 +01:00
Patrick Pelissier 2ead3905e5 Rename m-protect to m-concurrent 2018-10-28 22:17:33 +01:00
Patrick Pelissier 99d883253c Add TEST_EMPTY operator 2018-10-28 22:13:48 +01:00
Patrick Pelissier ed4d4f9f23 Fix formatting 2018-10-28 17:53:41 +01:00
Patrick Pelissier d76f9565b2 Add new primitives. 2018-10-28 17:53:16 +01:00
Patrick Pelissier 1c637f1182 Proper export of KEY_OPLIST & VALUE_OPLIST 2018-10-28 11:17:05 +01:00
Patrick Pelissier 4dbe5de79c Fix in progress 2018-10-28 11:16:46 +01:00
Patrick Pelissier afcb88d04c Add PROTECT module that automaticaly add a lock on container access. 2018-10-28 10:41:10 +01:00
Patrick Pelissier 1ea1deef94 Update doc with last changes. 2018-10-28 10:40:18 +01:00
Patrick Pelissier 08d2af22db Add KEY_OPLIST & VALUE_OPLIST operators 2018-10-28 10:38:41 +01:00
Patrick Pelissier 70179bb1d7 Add VALUE_TYPE operator 2018-10-28 10:33:25 +01:00
Patrick Pelissier 83226c5ce8 Add KEY_TYPE operator 2018-10-28 10:03:47 +01:00
Patrick Pelissier 3d6b737c46 Export *KEY operators in OPLIST for array, dict, B+tree 2018-10-27 23:37:06 +02:00
Patrick Pelissier fff511101c Add _erase for array 2018-10-27 23:30:38 +02:00
Patrick Pelissier 6672becdd5 Add _erase for array 2018-10-27 23:30:21 +02:00
Patrick Pelissier bd315c7e43 Add method ERASE_KEY 2018-10-27 23:04:56 +02:00
Patrick Pelissier fbfe28c087 Port the test suite to use _erase instead of _remove. 2018-10-27 22:59:33 +02:00
Patrick Pelissier adbafc0b02 Fix usage of pop_at after later change. 2018-10-27 22:59:19 +02:00
Patrick Pelissier 4e6dad939a API Compatibility change: rename _remove for DICT & B+TREE into _erase for API uniformization. 2018-10-27 22:56:00 +02:00
Patrick Pelissier 12bf9c0f59 ERASE method returns bool (like dictionnary) 2018-10-27 22:43:06 +02:00
Patrick Pelissier f7f7d7bc38 Add asertion 2018-10-27 22:34:53 +02:00
Patrick Pelissier c169c05bba Clarify name of old methods SET_KEY, GET_KEY, GET_SET_KEY 2018-10-27 22:34:34 +02:00
Patrick Pelissier 19d48740cd Modify prototype of buffer _init_set & _set to allow const argument for src. Not proud of the solution. 2018-10-27 16:13:02 +02:00
Patrick Pelissier 7a067a4b90 Add M_PTR_OPLIST 2018-10-27 11:13:09 +02:00
Patrick Pelissier 13f3dab23a Update with more examples. 2018-10-27 11:12:55 +02:00
Patrick Pelissier fd9d308b28 Allow tuple initilization in M_LET 2018-10-27 09:28:17 +02:00
Patrick Pelissier 1047d842d0 Add OPLIST for buffer & an example. Still a WIP for the constness of the BUFFER source. 2018-10-26 23:30:04 +02:00
Patrick Pelissier 77385e53f7 Add _init1 for buffer (internal use) 2018-10-26 22:23:44 +02:00
Patrick Pelissier f7b3de2750 Add test cases for _init_set & _set for buffers 2018-10-26 22:05:14 +02:00
Patrick Pelissier a65e6de66b Add more cases. 2018-10-26 21:44:25 +02:00
Patrick Pelissier 41cf523dfc Adding _set & _init_set for buffer (to test) 2018-10-26 21:21:29 +02:00
Patrick Pelissier 3774f8fd11 Fix M_POD_OPLIST in C11 mode 2018-10-26 20:18:32 +02:00
Patrick Pelissier b9e26b1c2c Add test with musl-gcc to test C11 library thread.h 2018-10-26 20:09:32 +02:00
Patrick Pelissier 609175713b Fix C11 backend. 2018-10-26 19:40:51 +02:00
Patrick Pelissier d56b794177 Fix bug in _get method of deque container. 2018-10-21 22:41:29 +02:00
Patrick Pelissier 608a929fcb Added another test case for _get method for DEQUE container. 2018-10-21 22:40:56 +02:00
Patrick Pelissier 4bf045e3d6 Make INIT conditional for DEQUE containers. 2018-10-21 22:31:22 +02:00
Patrick Pelissier 6b5434d64f Fix regression:
even if there is no INIT method for the base type,
the container still defines a INIT method.
2018-10-21 22:27:52 +02:00
Patrick Pelissier abb5480890 Port DEQUE to CALL infrastructure. 2018-10-20 19:02:48 +02:00
Patrick Pelissier 95069b0d4c Update doc 2018-10-20 16:59:56 +02:00
Patrick Pelissier 8f15b50b5f Update documentation of arrays. 2018-10-20 16:57:55 +02:00
Patrick Pelissier c7a1505f9b Update doc 2018-10-10 21:54:34 +02:00
Patrick Pelissier ff098c91f4 Add _capacity for deque 2018-10-07 09:05:09 +02:00
Patrick Pelissier fb40a4f904 Update state 2018-10-03 22:03:08 +02:00
Patrick Pelissier c87696f5c1 Add _hash, _equal_p, _out_str, _in_str, _get_str, _parse_str
for intrusive list.
2018-10-03 22:01:40 +02:00
Patrick Pelissier 4f3dad4bea Add _reverse for I-LIST 2018-10-01 21:22:19 +02:00
Patrick Pelissier 46c2e33096 Remove WIP 2018-09-29 10:23:31 +02:00
Patrick Pelissier 66ee266b90 Move depend.png in doc folder. 2018-09-29 10:22:03 +02:00
Patrick Pelissier 14883b1e26 Update image 2018-09-29 10:10:30 +02:00
Patrick Pelissier 33f74988b1 Update doc 2018-09-29 10:09:18 +02:00
Patrick Pelissier 78b157ff7f Update documentation. 2018-09-29 10:05:40 +02:00
Patrick Pelissier 546b756932 Port TUPLE & VARIANT to API transformation infrastructure. 2018-09-25 21:38:31 +02:00
Patrick Pelissier 5b74d54c64 Remove unused macro. 2018-09-25 20:10:44 +02:00
Patrick Pelissier fc608b21ee Update comment 2018-09-25 20:02:58 +02:00
Patrick Pelissier 2b6a266b93 Optimize _pop_back for dual push list. 2018-09-22 11:58:22 +02:00
Patrick Pelissier 7cb5092010 Update document on dual_push list 2018-09-22 11:58:05 +02:00
Patrick Pelissier a1c3681002 Optimization of _init_set for list. 2018-09-22 11:37:23 +02:00
Patrick Pelissier 91f35aa3bb Update documentation of LIST. 2018-09-22 11:36:38 +02:00
Patrick Pelissier 49a6715dfe Merge branch 'master' of https://github.com/P-p-H-d/mlib 2018-09-19 22:22:57 +02:00
Patrick Pelissier b799725bef Update comment. 2018-09-19 22:22:48 +02:00
Patrick Pelissier 1ada22462f Update comment. 2018-09-19 22:20:11 +02:00
Patrick Pelissier b0ec9680a1 Merge branch 'master' of https://github.com/P-p-H-d/mlib 2018-09-19 07:58:38 +02:00
Patrick Pelissier 7a8a6b1bb1 Rewrite in order to be easier to read. 2018-09-19 07:57:59 +02:00
Patrick Pelissier 1380a91bf1 Update comments. 2018-09-17 22:20:39 +02:00
Patrick Pelissier 83208f74ec Add one more test. 2018-09-17 22:20:26 +02:00
Patrick Pelissier 5f571418d0 Clarify documentation 2018-09-16 00:01:41 +02:00
Patrick Pelissier a7d8ff208f Fix. 2018-09-15 19:42:54 +02:00
Patrick Pelissier 1046f3e220 Add more structure in example. 2018-09-15 19:38:18 +02:00
Patrick Pelissier 6406f97f56 Clarify documentation about oplist usage. 2018-09-15 18:28:43 +02:00
Patrick Pelissier e0c456490a Fix definition of oplist for containers:
if no argument is given, it shall behave as if it was defined with
a standard C type.
2018-09-15 17:59:50 +02:00
Patrick Pelissier 6a1b2a3964 Add new example. 2018-09-15 15:46:22 +02:00
Patrick Pelissier 3452b72add Fix regression of M*LIB:
oplist of tuple/variant wasn't able to detect the default INIT creation
as no valid OPLIST was passed in the absence of OPLIST.
Give the explicit default OPLIST in this case.
2018-09-15 15:30:03 +02:00
Patrick Pelissier 2e3b312fab Fix issue of recursive usage of M_GLOBAL_OPLIST_OR_DEF
Update the way to use the macro to avoid being limited by the
preprocessor limitation.
2018-09-14 20:57:55 +02:00
Patrick Pelissier 8f67ca339b Rename internal macros. 2018-09-13 22:59:38 +02:00
Patrick Pelissier 1679b7b5cf Mark ALGO_INIT_VA & ALGO_LET_INIT_VA as obsolete:
the M_LET macro supports their feature in a more generic way.
2018-09-10 22:57:52 +02:00
Patrick Pelissier 757049543d Port to M_CALL infrastructure the M-ALGO 2018-09-10 22:50:26 +02:00
Patrick Pelissier 38b098b547 Port to M_CALL for M-STRING 2018-09-10 21:59:29 +02:00
Patrick Pelissier 60478e3f61 Port to M_CALL for M-SNAPSHOT 2018-09-10 21:55:18 +02:00
Patrick Pelissier 6325f01122 Port to M_CALL M-SHARED 2018-09-10 21:39:38 +02:00
Patrick Pelissier 74c12e6ea2 Port to M_CALL for R/B TREE. 2018-09-10 21:34:18 +02:00
Patrick Pelissier fd3453bb2a Port to M_CALL infrastructure M-I-SHARED 2018-09-10 07:55:47 +02:00
Patrick Pelissier 2ff7631c9c Port to M_CALL infrastructure. 2018-09-10 07:53:07 +02:00
Patrick Pelissier 7a6b0ab641 Port dict to new M_CALL infrastructure to support API transformation. 2018-09-09 14:04:49 +02:00
Patrick Pelissier 8fd0003548 Update 2018-09-08 22:51:46 +02:00
Patrick Pelissier bb8de993b6 Start porting to M_CALL infrastructure, enabling support for
API transformation.
2018-09-08 22:49:12 +02:00
Patrick Pelissier 7c83d733cd Fix grammar 2018-09-08 20:34:12 +02:00
Patrick Pelissier 6f49a1905c Minor fix 2018-09-08 20:32:49 +02:00
Patrick Pelissier d0378aadeb Update doc 2018-09-08 20:25:38 +02:00
Patrick Pelissier 4834318742 Update doc about missing parse_str methods. 2018-09-08 09:56:25 +02:00
Patrick Pelissier 1838a490ba Update doc 2018-09-08 09:47:47 +02:00
Patrick Pelissier de3cbe45b4 Make INIT method optional. 2018-09-08 09:47:36 +02:00
Patrick Pelissier a34cb6c89d Simplification 2018-09-08 09:34:01 +02:00
Patrick Pelissier 9c0724ddb9 Simplification. 2018-09-08 09:10:37 +02:00
Patrick Pelissier ce948bd9ce Make INIT optional for TUPLE. 2018-09-08 09:02:54 +02:00
Patrick Pelissier 5eb5d2420e Make INIT method optional for oplists. 2018-09-08 08:57:02 +02:00
Patrick Pelissier 7510e36f6b Update doc 2018-09-08 08:56:53 +02:00
Patrick Pelissier 8bfdf2bd5f Add M_IF_METHOD2_BOTH 2018-09-08 08:48:09 +02:00
Patrick Pelissier 2174c28aa2 Make INIT operator optionnal for ARRAY & LIST. 2018-09-08 08:21:13 +02:00
Patrick Pelissier 7a55667a5d Remove lib (link broken) 2018-09-04 22:15:44 +02:00
Patrick Pelissier 79d0452427 Instead of fixed string, enable using printf like formatting for initialization of a string. 2018-09-02 11:05:27 +02:00
Patrick Pelissier ad5c141a7c Enable support to initialize a string_t with a const char[] within M_LET. 2018-09-02 11:00:14 +02:00
Patrick Pelissier d4cd327ea3 Add API transform call for all operators. 2018-09-02 10:40:09 +02:00
Patrick Pelissier 27b29d5ffe Clean up & fix DICT_SET_OPLIST 2018-09-01 21:40:17 +02:00
Patrick Pelissier b52222cad9 Add operator INIT_WITH for oplist with PUSH. 2018-09-01 21:22:18 +02:00
Patrick Pelissier 822c27399c Add M_INIT_VAI to initialize a container with a VA.
(pretty much like it was done for ALGO).
Use if for array & list so that you can init them in M_LET
by giving it a VA.
2018-09-01 21:12:02 +02:00
Patrick Pelissier 0e717389c1 Add API transform support for the method in an oplist. 2018-09-01 20:42:51 +02:00
Patrick Pelissier 2197c4ce81 Add more cases to test. 2018-09-01 09:50:28 +02:00
Patrick Pelissier b894d64063 Add support for INIT_WITH in M_LET in C99 mode. 2018-09-01 09:47:32 +02:00
Patrick Pelissier d95a11365a * New method INIT_WITH
* Prepare variable number of arguments for M_LET with INIT_WITH
2018-09-01 09:39:09 +02:00
Patrick Pelissier dbcade3087 Add support for given an argument for the initialization of a variable in M_LET
if the variable is within parenthesis:
 M_LET((a,b), string_t)
 ==> It will init a & set it to b.

For the moment, we can only init with value of the same type (through init_set)
2018-08-31 00:02:03 +02:00
Patrick Pelissier 7ac54c0eb8 Update doc 2018-08-29 22:17:52 +02:00
Patrick Pelissier 91c74b863d Update comments 2018-08-26 09:26:44 +02:00
Patrick Pelissier 8c0b6f1e9d Fix spellings. 2018-08-25 21:35:37 +02:00
Patrick Pelissier 539f388e66 Update doc (fix spelling). 2018-08-25 09:00:19 +02:00
Patrick Pelissier b03c312b4e Update doc 2018-08-25 08:32:51 +02:00
Patrick Pelissier 71db5f9a79 Support for break to break the execution of M_LET 2018-08-25 00:11:20 +02:00
Patrick Pelissier e33e77595f Simplify the logic for parsing bool. 2018-08-24 21:03:25 +02:00
Patrick Pelissier d4016b310d Add support for conditionnal support for stdio.h
(needed for special environnement where no stdio is supported).
2018-08-24 20:59:54 +02:00
Patrick Pelissier a41177a110 Add support for bool in default type support.
Needs to rewritefully how to dispatch the default type for IN_STR method
Add also proper non-support for const char* & char * for IN_STR & PARSE_STR
(We cannot do it reliabelty, so the function says it always fail).
2018-08-24 20:45:26 +02:00
Patrick Pelissier f8600296f0 Add missing const for dict's equal_p 2018-08-24 19:18:29 +02:00
Patrick Pelissier e1147fb33d Add support for IN_STR for default C types. 2018-08-23 19:58:02 +02:00
Patrick Pelissier ec0d020669 Fix documentation about right prototype of in_str. 2018-08-23 19:38:19 +02:00
Patrick Pelissier e8eab86546 Fix issue in warning about discarding const attribute. 2018-08-22 00:21:23 +02:00
Patrick Pelissier c9ebedb01f Rename _get_ methods for tuple & variants into _get_at_ methods.
Add _cget_at_ methods too.
2018-08-21 23:58:03 +02:00
Patrick Pelissier af5742ce2d Change the value used to generate the enumerate of a variant:
the previous values didn't prevent to have a name collision
with other methods defined for the variant.
2018-08-21 22:53:07 +02:00
Patrick Pelissier 8f85c71654 Rename some internal macros to a better name. 2018-08-21 22:15:11 +02:00
Patrick Pelissier 70753c1082 Add method parse_str for the oplist of default C type. 2018-08-21 20:04:11 +02:00
Patrick Pelissier 2ab2b9efd6 Add primitive to parse standard C types (except const char*) 2018-08-21 19:53:55 +02:00
Patrick Pelissier dbb34b75fd Add method for the operator OUT_STR for the default type. 2018-08-21 19:26:18 +02:00
Patrick Pelissier 7447492c76 Fix issue with deque in case of successive _clean:
the CLEAR method was called more than once for an object.
2018-08-20 23:13:55 +02:00
Patrick Pelissier 492e4c33db Adding more assertions to check the good call of the test object. 2018-08-20 23:11:13 +02:00
Patrick Pelissier cbb08bdcfc Amend previous commit. 2018-08-19 20:28:46 +02:00
Patrick Pelissier 0de829741a Add synonimous for pointers. 2018-08-19 20:27:45 +02:00
Patrick Pelissier 0fa7aed1c4 Add missing synonim 2018-08-19 20:07:58 +02:00
Patrick Pelissier 3b0e940284 Add missing _ptr & _srcptr alias. 2018-08-19 17:58:23 +02:00
Patrick Pelissier 48a970ceb9 Avoid using '__' within words.
It seems to be a reserved naming of C++.
2018-08-19 11:31:09 +02:00
Patrick Pelissier c7b533f21e Cleanup terminology 2018-08-19 10:54:48 +02:00
Patrick Pelissier 839d4da191 Update doc 2018-08-19 10:36:30 +02:00
Patrick Pelissier b76ddfe93c Update doc 2018-08-19 10:07:04 +02:00
Patrick Pelissier 4ba0156ad8 Backport solution from tuple to variant. 2018-08-19 08:45:10 +02:00
Patrick Pelissier 6ef9936e24 Fix issue of recusrive definition of TUPLE using GLOBAL oplist:
There was a recursive call of M_REDUCE2 that fails to expand properly on its second invocation
Rewrite fully how global oplists are implemented:
it performs a first pass to expand the oplists, and the a separate pass to define the functions.
This enables to separate the pass, and use M_REDUCE2 in both.
2018-08-19 08:28:17 +02:00
Patrick Pelissier e835dbc7c6 Update Makefile for also testing clang++ 2018-08-19 08:27:51 +02:00
Patrick Pelissier 5c8429a7bb Reformating 2018-08-16 07:29:22 +02:00
Patrick Pelissier 824011657a Improve once again mapping of the buffer structure to avoid
false sharing.
2018-08-15 10:33:20 +02:00
Patrick Pelissier 8e2031bf5a Add comments. 2018-08-15 10:30:17 +02:00
Patrick Pelissier 8bb5f5475e Avoid putting the push mutex & the pop mutex inside the same cache line.
Avoid false sharing for multi-threading scenario and should increase performance.
2018-08-15 10:21:23 +02:00
Patrick Pelissier eb4f249a54 Add missing CLEAN method in export OPLIST. 2018-08-14 23:09:12 +02:00
Patrick Pelissier 3aa3b87709 Add missing CLEAN method in export OPLIST. 2018-08-14 23:08:33 +02:00
Patrick Pelissier 0046157862 Fix issue for SWAP method not exported correctly for tuple. 2018-08-14 08:00:16 +02:00
Patrick Pelissier acbf1efad6 Fix issue: CLEAN method was not properly exported by tuple. 2018-08-14 07:58:37 +02:00
Patrick Pelissier 5d1eb45d5c Export CLEAN method. 2018-08-14 07:49:31 +02:00
Patrick Pelissier c52c41c885 Export CLEAN method for tuple if it is defined. 2018-08-13 20:14:37 +02:00
Patrick Pelissier 822f9aacf6 Add CLEAN method for tuple. 2018-08-13 20:05:32 +02:00
Patrick Pelissier 7d5608ca0e Export CLEAN method for string. 2018-08-13 20:03:06 +02:00
Patrick Pelissier 937bbef818 Add method _erase & _update_further for priority queue. 2018-08-12 21:35:09 +02:00
Patrick Pelissier c506443ed0 Add comment about ALGO_EXTRACT 2018-08-08 22:54:56 +02:00
Patrick Pelissier 5f42f26587 Fix warning about shadowed variables 2018-08-08 22:49:49 +02:00
Patrick Pelissier f84e96d790 Reexport IT_INSERT for array.
Even it is slow, it may be useful.
2018-08-08 22:49:25 +02:00
Patrick Pelissier ede57a20be Add ALGO ALGO_INSERT that allows inserting a container into another one. 2018-08-08 22:44:17 +02:00
Patrick Pelissier df8306c721 Export IT_INSERT method 2018-08-08 22:43:44 +02:00
Patrick Pelissier 1bb144e256 VARIANT: Oplist is now optional for their definition. 2018-08-07 23:18:37 +02:00
Patrick Pelissier 7d83603646 TUPLE: Oplist is now optional for tuple definition. 2018-08-06 23:20:02 +02:00
Patrick Pelissier 7e4d51bab0 Add support for global oplist for deque 2018-08-05 22:33:13 +02:00
Patrick Pelissier 4ef1b079cb Can omit optlist in B+TREE definition 2018-08-05 11:08:18 +02:00
Patrick Pelissier e95d13adb1 Add support for multiple M_LET on the same line
without warning about shadow variables.
2018-08-04 08:34:35 +02:00
Patrick Pelissier 92d7009804 Fis issue if given type is a T * = TYPE:
token pasting created const T *, which is not the same as
const TYPE = T * const
2018-08-03 20:35:37 +02:00
Patrick Pelissier 354327e40b Fix issue for string_strim if string was empty and not allocated. 2018-08-03 20:23:24 +02:00
Patrick Pelissier 34d56c54c8 Test strim with an empty string.
(Shall do nothing).
2018-08-03 20:22:32 +02:00
Patrick Pelissier 143a5fdf4f Fix issue if given type is a T * = TYPE:
token pasting created const T *, which is not the same as
const TYPE = T * const
2018-08-03 20:20:57 +02:00
Patrick Pelissier 030510613c Add more assertions. 2018-08-03 19:57:38 +02:00
Patrick Pelissier 984b8e7da0 Fix const cast issue for 'void *' pointer. 2018-08-03 18:55:01 +02:00
Patrick Pelissier aba1ac1646 Fix issue in 64 bits with overwriting enabled for buffer. 2018-08-02 23:31:48 +02:00
Patrick Pelissier 6b93d7644a Proper constant definition for unicode error. 2018-08-02 23:31:17 +02:00
Patrick Pelissier 68c737dcef Clean up interface if (internal) genint:
it returns at most an 2048, so an unsigned int is more enought
(and internally it was already used!).
So transform the interface to take as input unsigned int.

Fix maximum size supported by the genint package.

Add proper constant used for error, and use it in the code.
2018-08-02 23:19:05 +02:00
Patrick Pelissier b8f8ffe0a2 Perform same optimization than previous commit for
M_GLOBAL_OPLIST_OR_DEF
2018-08-02 22:48:28 +02:00
Patrick Pelissier 02e8681f76 M_GLOBAL_OPLIST:
More robust implementation that delays the evaluation of the concatenation
2018-08-02 20:02:15 +02:00
Patrick Pelissier 603fb9db62 Replace some size displayed with '%lu' as unsigned long
with proper '%zu' as size_t.
2018-08-02 19:34:10 +02:00
Patrick Pelissier b01459d1ef Add out_str for CSTR_OPLIST 2018-08-01 23:36:04 +02:00
Patrick Pelissier 667f1f5951 Update documentation. 2018-08-01 23:28:01 +02:00
Patrick Pelissier 660c5a2db4 Add M_GET_STRING to transform a standard numerical C type into a string_t
(with a method compatible with the operator GET_STR).
2018-08-01 23:26:58 +02:00
Patrick Pelissier 8a4d013354 Fix issues (normal contract where used for I/O whereas they could be used
with OA Dict)
2018-08-01 22:55:54 +02:00
Patrick Pelissier faedca81d1 Add M_CORE_VERSION_* macros. 2018-08-01 22:28:50 +02:00
Patrick Pelissier 166d408f5e Add support for out_str for the default C types if C11 is enabled. 2018-08-01 20:12:24 +02:00
Patrick Pelissier 017f25d9d5 M_PRINT_ARG & M_FPRINT_ARG don't put a final semicolon at the end
of their expansion anymore (cleaner and no longer needed with the
new available tools).
2018-08-01 19:59:57 +02:00
Patrick Pelissier 6a70eba83f Document some missing methods for priority queues. 2018-08-01 19:35:54 +02:00
Patrick Pelissier ef13ab6938 Add rapidstring 2018-08-01 00:31:58 +02:00
Patrick Pelissier 75cb61fd36 Fix issue of deque's _init_move. 2018-07-31 23:11:43 +02:00
Patrick Pelissier 9c31f8aa92 Complete test to show failure of _init_move for deque. 2018-07-31 23:11:26 +02:00
Patrick Pelissier 27a08da430 Add node contract check. 2018-07-31 08:19:45 +02:00
Patrick Pelissier fdada9661f Update comment. 2018-07-30 22:45:24 +02:00
Patrick Pelissier f1a52439bb Add more tests. 2018-07-30 22:42:33 +02:00
Patrick Pelissier d13849c173 Update formatting. 2018-07-30 22:42:09 +02:00
Patrick Pelissier d8a0e73899 Update comment. 2018-07-30 22:24:03 +02:00
Patrick Pelissier b6a3387f8a User are able to customize the number of elements of a mempool globaly. 2018-07-30 22:02:46 +02:00
Patrick Pelissier 04dd708941 Delayed the inclusion of the common headers, so that the backend may properly
set up additional parameters for the headers.
2018-07-29 19:40:37 +02:00
Patrick Pelissier 39ffe1c95a Fix issue for 32 bits build due to previous modification of limb size to 64 bits. 2018-07-29 19:40:08 +02:00
Patrick Pelissier 7b5ae3c15b Update API so that the worker_t parameter is given on creating the sync bloc.
(API breakage), more RAII like.
2018-07-28 19:09:07 +02:00
Patrick Pelissier 7678625071 Review:
* add comments
* change error code from assertion to proper memory failure
* add new assertions
2018-07-28 13:48:30 +02:00
Patrick Pelissier dbe8817d95 Add bitset_clz 2018-07-27 06:19:54 +02:00
Patrick Pelissier 09e117cd57 Update comments.
Update _new_node: it shall never return NULL.
2018-07-24 00:34:35 +02:00
Patrick Pelissier 4b8779d8d3 Update formula for increasing the size. 2018-07-24 00:24:50 +02:00
Patrick Pelissier 79158dabbc Rename _map into _for_each. 2018-07-22 21:32:46 +02:00
Patrick Pelissier f962bc8da5 Avoid overflow in case of huge computations.
Add more comments.
2018-07-22 21:32:19 +02:00
Patrick Pelissier 28e8dde06d Make target uninstall PHONY 2018-07-21 12:12:50 +02:00
Patrick Pelissier 6c3d41a124 Add target uninstall 2018-07-21 12:12:19 +02:00
Patrick Pelissier 9c2967f8df Cleanup install target with proper use of COMMAND VARIABLES. 2018-07-19 22:55:56 +02:00
Patrick Pelissier 99311364e7 Regenerate the dependency graph. 2018-07-18 22:35:25 +02:00
Patrick Pelissier 16e86b8291 Don't regenerate depend.png everytime but only if the headers have been updated.
Optimize the generation of the PNG.
2018-07-18 22:34:52 +02:00
Patrick Pelissier 5741af8fd3 Add 'dist' target in the Makefile. 2018-07-18 22:31:53 +02:00
Patrick Pelissier 6bdd47a557 Don't remove depend with a make clean
otherwise you'll get a regeneration of the dependency file for each clean command
which is useless.
make distclean shall be used to remove the dependency file.
2018-07-18 22:28:19 +02:00
Patrick Pelissier 750ba93c14 Add note. 2018-07-18 21:43:27 +02:00
Patrick Pelissier 18be930507 Fix bug in case of error returned by vsnprintf:
the buffer may have been corrupted in case of error.
Clean it before returning.
2018-07-18 21:41:52 +02:00
Patrick Pelissier be26176828 Add one more test case. 2018-07-18 21:41:14 +02:00
Patrick Pelissier 540e01f1fe Fix prototype of the callback to InitOnceExecuteOnce: Missing CALLBACK.
(Some version of the compilers for windows produced warnings)
2018-07-18 21:21:35 +02:00
Patrick Pelissier 7cfb6b16e4 Update cross compilation information. 2018-07-18 01:01:13 +02:00
Patrick Pelissier d5a8b9648f Simplify 2018-07-18 01:01:04 +02:00
Patrick Pelissier 1657d7af7d Support of LOG_COMPILER for cross-compilation test. 2018-07-18 00:28:35 +02:00
Patrick Pelissier 318f1a2818 Update/Add comments. 2018-07-17 23:03:45 +02:00
Patrick Pelissier 8b0b115709 Update comment. 2018-07-17 23:03:33 +02:00
Patrick Pelissier 7d2494b7f9 Fix bug of SPMC / MPMC snapshots: not all items in buffers
were properly cleared.
2018-07-17 21:10:14 +02:00
Patrick Pelissier 3b642ffeb3 Update after internal review. 2018-07-17 21:00:55 +02:00
Patrick Pelissier 815bf5f0e0 Include proper header and do not depend on other headers to include it. 2018-07-16 21:45:28 +02:00
Patrick Pelissier 4af48724ce Update after internal review. 2018-07-16 21:43:17 +02:00
Patrick Pelissier 001272820c Update example to latests API. 2018-07-15 23:11:00 +02:00
Patrick Pelissier 4aca0664ef Add methods _find_again, _find_again_if, _mismatch_again and _mismatch_again_if 2018-07-15 23:06:22 +02:00
Patrick Pelissier 6721a86768 Add method _mismatch and _mismatch_if 2018-07-15 22:34:29 +02:00
Patrick Pelissier 5025b3690d Add test. 2018-07-15 22:26:59 +02:00
Patrick Pelissier 42b37a718f Add test. 2018-07-15 22:26:21 +02:00
Patrick Pelissier fcdfd836ec Add method _count_if 2018-07-15 11:05:58 +02:00
Patrick Pelissier b2f005239b API breakage:
rename _constains into _contain_p
to be more uniform with other predicate functions,
and to use infinitive form for the method.
2018-07-15 10:54:38 +02:00
Patrick Pelissier bc3fa890bd Add method _find_if. 2018-07-15 10:48:22 +02:00
Patrick Pelissier 95dbbf6e75 Simplification of the example. 2018-07-15 02:53:35 +02:00
Patrick Pelissier 22f2ae7341 Add methods _all_of_p, _any_of_p and _none_of_p. 2018-07-14 18:21:46 +02:00
Patrick Pelissier 96202aa78f Do not use reserved identifiers of the C standard (See 7.1.3 Reserved identifiers) 2018-07-14 14:35:14 +02:00
Patrick Pelissier cef8a13cc2 Add a new test. 2018-07-14 14:18:25 +02:00
Patrick Pelissier 54271231a0 Update doc. 2018-07-14 14:13:18 +02:00
Patrick Pelissier d1bb651596 Update doc. 2018-07-14 14:03:17 +02:00
Patrick Pelissier 10b7f65437 Update DEV. 2018-07-13 10:54:41 +02:00
Patrick Pelissier 3a70324d43 Fix spelling. 2018-07-13 10:42:26 +02:00
Patrick Pelissier 21b72cd174 Update 2018-07-13 10:40:45 +02:00
Patrick Pelissier ce8656f1b2 Update 2018-07-13 10:40:40 +02:00
Patrick Pelissier 9277dc967b Update 2018-07-11 00:04:45 +02:00
Patrick Pelissier 67b1e65d97 Update comment 2018-07-08 00:33:18 +02:00
Patrick Pelissier 79fe9da2da Update 2018-07-05 12:21:02 +02:00
Patrick Pelissier 3164e2698d Improve comments. 2018-07-05 12:12:16 +02:00
Patrick Pelissier ffae33e6f7 Update 2018-07-05 12:12:09 +02:00
Patrick Pelissier 8dafe3b7a6 Add macro M_GET_AT. 2018-07-04 17:39:03 +02:00
Patrick Pelissier e23616169b Improve comments. 2018-07-04 17:38:48 +02:00
Patrick Pelissier 9d7fc942f7 Rename DICT_SET_DEF2 into DICT_SET_DEF 2018-07-03 15:50:14 +02:00
Patrick Pelissier 982d8cf19d DICT_*_DEF* doesn't require anymore oplists for defining the function:
it can get them from global oplists or default oplits.
2018-07-03 15:44:40 +02:00
Patrick Pelissier aae460623d Replace _set_at by _push 2018-07-03 15:07:36 +02:00
Patrick Pelissier df14e5bcb6 Update 2018-07-03 10:42:51 +02:00
Patrick Pelissier 15d5a83c16 Update doc 2018-07-03 10:42:11 +02:00
Patrick Pelissier 4b0f7543ee Rename _set_at to _push method for SET dictionnary.
Add proper _splice method for SET dictionnary.
2018-07-03 10:40:26 +02:00
Patrick Pelissier 362c64aa9c Fix URL to show preview. 2018-07-03 10:11:40 +02:00
Patrick Pelissier 56c497cf2c Add ref card. 2018-07-03 10:08:16 +02:00
Patrick Pelissier e0dc2da80a Add synthesis of the API. 2018-07-03 09:35:35 +02:00
Patrick Pelissier 41362f12ea Add comment about _hash for dict. 2018-07-01 22:54:19 +02:00
Patrick Pelissier 3765c0b0fb Add _equal_p method for OA dictionary. 2018-07-01 22:44:56 +02:00
Patrick Pelissier d1f2b62f66 Replace _set_at2 by _get_at for array.
(Break API)
2018-07-01 22:35:05 +02:00
Patrick Pelissier 26b8c1d0a2 Add method _move & _init_move. 2018-07-01 17:16:36 +02:00
Patrick Pelissier aa195f9f6f Add methods _cget. 2018-07-01 17:16:25 +02:00
Patrick Pelissier cd1d59264e Fix build issue with C++ 2018-06-29 22:50:41 +02:00
Patrick Pelissier 923e009fd3 Fix issue for new method _get_at for OA dict. 2018-06-29 22:32:10 +02:00
Patrick Pelissier 70a4fd99f4 iFix buiild issue with mutex based atomics. 2018-06-29 21:43:33 +02:00
Patrick Pelissier feb4011f57 Add new example. 2018-06-29 19:41:55 +02:00
Patrick Pelissier bf2a04ff66 Add method _get_at:
_get or create an entry.
2018-06-29 19:41:34 +02:00
Patrick Pelissier e16d352e71 Fix prototype (forgot const) 2018-06-29 19:41:20 +02:00
Patrick Pelissier 817724ce39 Fix issue for get_word:
the separators need to be skipped first.
2018-06-29 19:40:35 +02:00
Patrick Pelissier 0fda117ffe Add push/pop_front/back_move methods. 2018-06-27 20:10:42 +02:00
Patrick Pelissier e5be1e1f05 Add a name to the structure. 2018-06-27 20:10:28 +02:00
Patrick Pelissier 7d7af8cf59 Add a name to the structure. 2018-06-27 20:10:12 +02:00
Patrick Pelissier cbc09f83d4 Disable MOVE & INIT_MOVE for such containers (TBC). 2018-06-27 20:09:45 +02:00
Patrick Pelissier 31913ae80e Add bench for DualPushList. 2018-06-27 20:09:24 +02:00
Patrick Pelissier 522632bea6 Remove deprecated macro SNAPSHOT_DEF.
Shall use SNAPSHOT_SPSC_DEF
2018-06-27 20:04:18 +02:00
Patrick Pelissier 397f75a4db Add missing method _front for bitset. 2018-06-27 07:33:23 +02:00
Patrick Pelissier 0bf84a321b Add missing methods _capacity for MPMC queue & SPSC queue. 2018-06-27 07:28:34 +02:00
Patrick Pelissier 5915208dee API breakage:
Rename _pop method into _pop_at method
as the provided function is a _pop_at method, not a _pop method.
Remove _pop from the oplist as it doesn't follow _pop convention.
2018-06-27 07:17:33 +02:00
Patrick Pelissier 2167931e3f Remove _pop method from OPLIST as it doesn't follow _pop
convention.
2018-06-27 07:17:09 +02:00
Patrick Pelissier 2d18171914 Rename _pop method into _pop_at methods
as the provided function follow the prototype of a _pop_at method
rather than a _pop method.
2018-06-27 07:13:10 +02:00
Patrick Pelissier 28f6a7d399 Add missing _empty_p methods for dictionnaries. 2018-06-27 07:10:18 +02:00
Patrick Pelissier 8e042435e5 Update comment and use of a different hash function. 2018-06-23 13:26:46 +02:00
Patrick Pelissier cddf8712e4 Update comment. 2018-06-23 13:12:28 +02:00
Patrick Pelissier 88fd96e022 Add a more complete example of use. 2018-06-23 13:10:33 +02:00
Patrick Pelissier 17e715f408 Remove _last as it is a duplicate with _back! 2018-06-23 11:07:06 +02:00
Patrick Pelissier c821d9ea65 Add methods _front & _last for arrays. 2018-06-23 11:05:29 +02:00
Patrick Pelissier 5cb8a9ddd0 Document priority queue API. 2018-06-20 23:25:41 +02:00
Patrick Pelissier f0131b2c39 Fix prototype of _front:
missing const.
2018-06-20 23:25:25 +02:00
Patrick Pelissier bc3c68a3cb Move a section. 2018-06-14 22:34:42 +02:00
Patrick Pelissier b81d387778 Simplify the code by factoring similar branches. 2018-06-11 23:08:56 +02:00
Patrick Pelissier 0afbe72672 Fix spelling. 2018-06-11 21:09:20 +02:00
Patrick Pelissier 5fc63485f3 Update 2018-06-07 21:56:09 +02:00
Patrick Pelissier 1bc84c98f8 Update 2018-06-07 21:51:19 +02:00
Patrick Pelissier 737af45c85 Adding the long term ISSUES in configuration. 2018-06-07 21:47:23 +02:00
Patrick Pelissier e08fbc7d86 Move all core macros to support up to 52 arguments. 2018-06-06 23:33:23 +02:00
Patrick Pelissier 91090d20de Remove non defined operators from the oplist. 2018-06-06 00:00:08 +02:00
Patrick Pelissier 05a22e20b8 Add documentation on B+TREE. 2018-06-05 23:58:49 +02:00
Patrick Pelissier 7c43f343a3 Update 2018-06-05 00:39:07 +02:00
Patrick Pelissier 029e6aa251 Remove reference to libraries without working sites. 2018-06-04 23:11:18 +02:00
Patrick Pelissier 2f26d4ffd3 Add more tests / improve coverage. 2018-06-02 21:26:54 +02:00
Patrick Pelissier d306bae035 Generate cleaner coverage. 2018-06-02 21:26:32 +02:00
Patrick Pelissier 34dc8b32cb Fix broken _swap 2018-06-02 21:26:20 +02:00
Patrick Pelissier fe38244d7c Micro optimisation. 2018-06-02 19:31:07 +02:00
Patrick Pelissier 25e8c9f637 Update comments. 2018-06-02 19:23:46 +02:00
Patrick Pelissier 337b568b5e Update comments. 2018-06-02 19:21:59 +02:00
Patrick Pelissier e696192ef7 Update comments.
Cosmetic change.
2018-05-31 23:09:11 +02:00
Patrick Pelissier 928fc077b8 Remove a warning. 2018-05-30 23:17:37 +02:00
Patrick Pelissier 2a448b932c Update doc 2018-05-30 23:17:30 +02:00
Patrick Pelissier d40d23a50e Add test for _splice_at for Intrusive list. 2018-05-30 20:21:37 +02:00
Patrick Pelissier c4548c4355 Fix splice_at which was broken. 2018-05-30 20:21:27 +02:00
Patrick Pelissier 50fdea813b Update doc 2018-05-30 19:26:13 +02:00
Patrick Pelissier 0ff163e541 Fix issue for intrusive list and _splice_back. 2018-05-29 23:00:25 +02:00
Patrick Pelissier 38cd8847a2 Adding a generic merge sort if container has both _splice_back & _splice_at
methods
2018-05-28 23:51:41 +02:00
Patrick Pelissier e62ae9fb94 Fix the iterators calculation of _splice_at.
NOTE: iterator seems broken for _splice_back. To check
2018-05-28 23:51:10 +02:00
Patrick Pelissier 187b6cf072 Update to new specification. 2018-05-28 23:46:30 +02:00
Patrick Pelissier a3fad5966a Update test to reflect the new specifications. 2018-05-28 23:46:10 +02:00
Patrick Pelissier 74350f3638 Reuse a local variable instead of reading once again from memory. 2018-05-28 23:45:40 +02:00
Patrick Pelissier ee2c7caac2 Update _splice_at with new specification.
Fix issue of _splice_at (iterator not properly set).
Export _split_at methods in oplist.
2018-05-28 23:44:15 +02:00
Patrick Pelissier c915a6912e Update specification of _splice_at.
It is more interressing to have the iterator of the target list
to advance too. It allows copying items from list to list in the same order.
2018-05-28 23:43:05 +02:00
Patrick Pelissier d5cd232441 Add method SPLICE_AT as a generic method. 2018-05-28 23:42:46 +02:00
Patrick Pelissier c4c14b0fc6 Add _splice_at 2018-05-28 22:08:55 +02:00
Patrick Pelissier e5f5563249 Add method _splice_at for dual_push_list 2018-05-28 21:58:45 +02:00
Patrick Pelissier 2a6893efb4 Add method _splice_at for singly list. 2018-05-28 21:22:14 +02:00
Patrick Pelissier 9fd920945a Remove condition that the list shall be different for splice_back.
No need.
2018-05-28 20:59:27 +02:00
Patrick Pelissier a116daa7a1 Splice_back may work if old & new are the same list. 2018-05-28 20:58:51 +02:00
Patrick Pelissier 0b8759380b Fix name of called method to avoid infinite recursion... 2018-05-27 11:17:11 +02:00
Patrick Pelissier 00c5e3544f Export _splice_back methods.
Add _push_move & _pop_move methods for dual push list.
2018-05-27 11:16:16 +02:00
Patrick Pelissier 53a79e3170 Export splice_back method for list 2018-05-27 11:06:35 +02:00
Patrick Pelissier 5810bf9acd Add method SPLICE_BACK. 2018-05-27 11:05:00 +02:00
Patrick Pelissier 7815feddba Proper expand algorithms for intrusive list. 2018-05-24 22:58:16 +02:00
Patrick Pelissier f529a5eea8 Use of a trick to make the function compatible with other
definition of the _it method (use of const for the container).
2018-05-24 22:57:27 +02:00
Patrick Pelissier 40e9550b29 Generate algo for intrusive list. 2018-05-23 21:10:58 +02:00
Patrick Pelissier 64d0202f95 ALGO macros accept now a type instead of an oplist
if a global oplist has been registered for the type.

Add macro ALGO_LET_INIT_VA which declares, initializes, sets and clears
a container.
2018-05-22 20:39:51 +02:00
Patrick Pelissier da9483aa3a Add reference to C standard about CONST_CAST. 2018-05-22 19:38:38 +02:00
Patrick Pelissier 0221e555fe Fix bug in deque: _clean doesn't call CLEAR on all created objects. 2018-05-22 05:46:37 +02:00
Patrick Pelissier 494ee2c099 Add proper check of allocation for testing object. 2018-05-22 05:46:04 +02:00
Patrick Pelissier dc92ad9c02 Fix compatbility issue with C++ which doesn't support
compound literal...
2018-05-21 23:00:48 +02:00
Patrick Pelissier 8a4a019973 Fix build issue with C++ 2018-05-21 07:23:19 +02:00
Patrick Pelissier a1a49ebf4b Update comment 2018-05-20 22:38:00 +02:00
Patrick Pelissier 45263a6261 Fix parenthesis 2018-05-20 18:07:58 +02:00
Patrick Pelissier 7ccede36b1 Test with deque too. 2018-05-19 12:52:40 +02:00
Patrick Pelissier 482e1631fa Remove warning in case of some kind of type
(Famous problem of C not allowing to cast
a T (*)[N] into a const T (*)[N].
2018-05-19 12:51:31 +02:00
Patrick Pelissier d0fe7f3e25 Add method _push_move & _pop_move.
Add assertion on _pop_back to check that the container is not empty.
2018-05-19 11:21:57 +02:00
Patrick Pelissier 86083e3a92 Add methods push_move & pop_move.
Rewrite _pop_back to be a little bit faster for complex structure as it used
DO_MOVE instead of plain _set.
2018-05-19 11:21:18 +02:00
Patrick Pelissier 281f567d97 Add proper usage of const. 2018-05-19 11:10:09 +02:00
Patrick Pelissier 7f887720cc Add methods PUSH_MOVE & POP_MOVE. 2018-05-19 11:09:36 +02:00
Patrick Pelissier 717268add9 Simplify the macro usage. 2018-05-19 11:09:20 +02:00
Patrick Pelissier 2f7e3b1596 Update documentation. 2018-05-19 11:08:54 +02:00
Patrick Pelissier 1a3cd2f626 Add experimental support for cmp function with user define order. 2018-05-16 22:36:26 +02:00
Patrick Pelissier 2fbea43a5c Add M_MAP3 macro. 2018-05-16 20:00:41 +02:00
Patrick Pelissier 70788e932e Update doc 2018-05-16 19:47:06 +02:00
Patrick Pelissier 949d12052e Update 2018-05-16 07:52:37 +02:00
Patrick Pelissier 2af35673bb Update comments about TODO. 2018-05-15 21:02:34 +02:00
Patrick Pelissier 4a64ca58ac Update tested systems. 2018-05-15 20:41:15 +02:00
Patrick Pelissier 815c594fa6 Add new libraries 2018-05-15 20:41:06 +02:00
Patrick Pelissier fc4dba4569 Add DSC versions of the _sort function to reverve the sort (DSC=descending). 2018-05-14 23:08:46 +02:00
Patrick Pelissier 1d8df98339 Rename undocumented methods _union & _intersect to _sort_union & _sort_intersect
as they depend on the sort order.
2018-05-14 22:58:52 +02:00
Patrick Pelissier 745aabcfd5 Factorisation of all calls to GET_CMP within the sort functions
to a single function.
2018-05-14 22:55:17 +02:00
Patrick Pelissier be100790f1 Uniformize the comparaisons to always be in the same order. 2018-05-14 22:51:07 +02:00
Patrick Pelissier afc5e82906 Externalize _sort generation from the main macro. 2018-05-14 22:49:11 +02:00
Patrick Pelissier 2951d4b7fa Document new methods. 2018-05-14 22:30:08 +02:00
Patrick Pelissier dcd300211f Add methods _cmp_field for tuples. 2018-05-14 22:29:58 +02:00
Patrick Pelissier f796d27bb4 Update 2018-05-13 09:55:08 +02:00
Patrick Pelissier 47a2553356 Add new test on B*TREE as set. 2018-05-11 22:41:56 +02:00
Patrick Pelissier 67bf199254 Fix comparison of object.
memcmp compares object as little endian, which is not what we want.
2018-05-11 22:41:22 +02:00
Patrick Pelissier 4b1ed25ef3 Rewrite inner loop. should be a little bit faster. 2018-05-11 22:41:00 +02:00
Patrick Pelissier a5ac43e5f3 Fix issue with oplist of set. 2018-05-11 17:57:33 +02:00
Patrick Pelissier 0557527bfe Update format of serialisation for B+TREE:
as the tree is ordered, a '[]' has to be used.
2018-05-11 17:32:32 +02:00
Patrick Pelissier dc1445e0d6 Remove TODO comment. 2018-05-10 23:12:38 +02:00
Patrick Pelissier 431401bc96 Improve example. 2018-05-10 01:59:05 +02:00
Patrick Pelissier fded6c404a Add new libraries. 2018-05-10 01:34:20 +02:00
Patrick Pelissier 680a850618 Add test of _in_str, _out_str, _get_str and _parse_str 2018-05-09 21:48:42 +02:00
Patrick Pelissier 7d08a7ba1a Fix issue in _parse_str 2018-05-09 21:48:32 +02:00
Patrick Pelissier e9f603fa30 Rename internal calls to my_mpz* to testobj* 2018-05-08 10:40:29 +02:00
Patrick Pelissier 1a064e033b Rename mympz.h --> test-obj.h
as it is used only for testing purpose.
2018-05-08 10:29:51 +02:00
Patrick Pelissier ebcfcf9bfa Add I/O test for B+TREE 2018-05-07 23:08:47 +02:00
Patrick Pelissier cf063b75ff Update oplist to reflect all methods. 2018-05-07 23:08:12 +02:00
Patrick Pelissier 951a9d7989 Fix issue in parse. 2018-05-07 23:08:01 +02:00
Patrick Pelissier 37191a53c3 Fix unclear variable. 2018-05-04 00:14:52 +02:00
Patrick Pelissier 42d420db6a Add generation for string. 2018-05-03 22:12:32 +02:00
Patrick Pelissier 122cf63a3f Preshift the state by 8, to avoid a shift within the decoder.
Rewrite the size_u to avoid a branch.
2018-05-03 22:11:43 +02:00
Patrick Pelissier fafce60f9e Add method _parse_str
NOTE: _in_str, _out_str, _get_str & _parse_str remain to be tested.
2018-05-03 18:06:11 +02:00
Patrick Pelissier e855f27c8c Add method _parse_str for RBTREE.
NOTE: A refactorisation seems needed as all the in_str / parse_str
share a lot of the same algorithms. Maybe move them into algo?
2018-05-03 17:59:34 +02:00
Patrick Pelissier f01a593ff7 Update 2018-05-03 17:59:21 +02:00
Patrick Pelissier 6af34b1c16 Update doc to add _parse_str method 2018-05-03 17:51:36 +02:00
Patrick Pelissier 188b329eae Add method _parse_str for dict. 2018-05-03 17:43:40 +02:00
Patrick Pelissier 7b0f71cc99 Add method _parse_str for deque
(missing tests for in, out, get_str & parse_str).
2018-05-03 17:34:17 +02:00
Patrick Pelissier 73f8baf464 Add method _parse_str for array. 2018-05-03 17:29:10 +02:00
Patrick Pelissier 3a09efde91 Skip spaces in serialisation. 2018-05-03 17:22:50 +02:00
Patrick Pelissier 4efa8c1ef9 Add function _parse_str for list. 2018-05-03 17:14:10 +02:00
Patrick Pelissier 9f178208f2 Fix bug in new function. 2018-05-03 17:13:47 +02:00
Patrick Pelissier 0f2113ee12 Fix 2018-05-03 17:03:09 +02:00
Patrick Pelissier 139e003b0b Add method _parse_str for variant. 2018-05-03 17:01:03 +02:00
Patrick Pelissier 1bf80cf315 Add function _parse_str 2018-05-03 13:37:19 +02:00
Patrick Pelissier bcf182f515 Add _parse_str method 2018-05-03 13:30:39 +02:00
Patrick Pelissier c34fb94e77 Proper export of the method PARSE_STR 2018-05-03 13:19:36 +02:00
Patrick Pelissier 64080fadd3 Update comments. 2018-05-03 13:07:52 +02:00
Patrick Pelissier 1009cd6ed4 Export _parse_str function. 2018-05-03 13:05:45 +02:00
Patrick Pelissier 839c1af124 Export _parse_str function. 2018-05-03 13:04:04 +02:00
Patrick Pelissier 6b24842704 Add _parse_str interface.
Rewrite _set_str to use the _parse_str
2018-05-03 12:59:58 +02:00
Patrick Pelissier 2ad8ae3451 Rewrite parse_str interface to be more like strol 2018-05-03 12:59:10 +02:00
Patrick Pelissier b1597f6f61 Rewrite comments. 2018-05-02 23:57:24 +02:00
Patrick Pelissier 47a49263ae Add function _parse_str for string. 2018-05-02 19:36:51 +02:00
Patrick Pelissier 9fed336c9e Add proper const to prototype. 2018-05-01 12:44:43 +02:00
Patrick Pelissier ac5b689950 Add method _set2 for tuple. 2018-04-30 20:00:26 +02:00
Patrick Pelissier 3d43bb260a Update 2018-04-30 13:32:24 +02:00
Patrick Pelissier 2eaeb26d4c Make Serialization input of array / dict more flexible
by supporting space characters within the input.
2018-04-30 11:02:42 +02:00
Patrick Pelissier 34d0f8ec81 Add missing function for variant:
variant_init_element
variant_init_set_element
2018-04-30 10:24:37 +02:00
Patrick Pelissier ddc1c7e98f Update to display checksum correctly. 2018-04-30 08:24:31 +02:00
Patrick Pelissier e41475f672 Rewrite 2018-04-29 11:52:19 +02:00
Patrick Pelissier e954622925 Rewrite 2018-04-29 11:48:02 +02:00
Patrick Pelissier fae899e52e Add benchmark udb2. 2018-04-29 10:45:03 +02:00
Patrick Pelissier 7b11f5afbc Add benchmark UDB 2018-04-28 19:44:52 +02:00
Patrick Pelissier 40c0bf79b1 Update 2018-04-28 19:09:30 +02:00
Patrick Pelissier df2df593b6 Replace complex convertion from int to string into a simple call to to_string 2018-04-27 21:54:57 +02:00
Patrick Pelissier 1dc59d610d Use of new function m_thread_yield 2018-04-27 21:54:34 +02:00
Patrick Pelissier 8fc8decd09 Add m_thread_yield & m_thread_sleep.
Rewrite the windows package (it wasn't fully working before) and use functions
available since WINDOWS VISTA (increasing minimum supported version).
2018-04-27 20:12:44 +02:00
Patrick Pelissier 08f44845ce Fix issue for SPSC queue:
computing the number of elements in the queue is not reliable enough
as we can get more than than the size of the queue.
2018-04-26 22:41:11 +02:00
Patrick Pelissier 4fbffc4788 Use plain 'unsigned int' instead of 'unsigned long long'
for index of SPSC queues. There is no issue as we don't compare
the index, but only substract them.
2018-04-26 22:38:43 +02:00
Patrick Pelissier 8cf4050d3d Remove uneeded alignment constraint for SPSC queue. 2018-04-26 22:26:30 +02:00
Patrick Pelissier 31c18fc56d Bench SPSC queues too. 2018-04-26 22:05:54 +02:00
Patrick Pelissier 005207cfd6 Remove a warning in release mode. 2018-04-26 22:05:36 +02:00
Patrick Pelissier c8d668ad96 Merge branch 'master' of https://github.com/P-p-H-d/mlib 2018-04-25 22:04:42 +02:00
Patrick Pelissier a39c4ba9a4 Rewrite the code by adding an additional cast to avoid new warning with GCC8. 2018-04-25 22:03:52 +02:00
Patrick Pelissier f4deea05bf Update doc. 2018-04-24 22:42:19 +02:00
Patrick Pelissier 1c18133603 Close the handle of the thread in case of joining thread. 2018-04-22 19:30:24 +02:00
Patrick Pelissier 15566b4b65 Add bench of library SDS for string 2018-04-21 18:53:38 +02:00
Patrick Pelissier 737185752c Merge branch 'master' of https://github.com/P-p-H-d/mlib 2018-04-21 12:46:26 +02:00
Patrick Pelissier a12e3534c6 Rewrite I/O functions so that they handle properly
non printable characters.
2018-04-21 10:26:05 +02:00
Patrick Pelissier 51575b341b Use of static library for liblfds. 2018-04-20 22:24:20 +02:00
Patrick Pelissier 06b330ec51 Update doc 2018-04-20 21:44:59 +02:00
Patrick Pelissier e0a6bce5b0 Add SDS 2018-04-20 21:43:19 +02:00
Patrick Pelissier da07a5b613 string_strim allows optionnal second argument which list the characters to strim. 2018-04-20 20:46:44 +02:00
Patrick Pelissier fbbca37b55 Ignore a*.dat file too. 2018-04-20 19:51:56 +02:00
Patrick Pelissier 7fa1c2264e Fix issue for C++ layer (memory order constants have to be put in the current namespace too). 2018-04-20 19:34:22 +02:00
Patrick Pelissier dcfbe53713 Fix conformity bug in _splice. 2018-04-20 19:30:45 +02:00
Patrick Pelissier 8b2993c979 Add more assertions to help scan-build with false positive. 2018-04-20 19:30:15 +02:00
Patrick Pelissier 131da660c8 Fix bug in _clean. 2018-04-20 19:30:01 +02:00
Patrick Pelissier f3de34a62b Optimization for MPMC queues:
* use of atomic_explicit functions to give the memory order we want,
* reduce the size of the index to unsigned int: even if there is an
overflow, it doesn't matter as we don't compare the index with < or >
but with ==.
2018-04-19 22:15:37 +02:00
Patrick Pelissier 928d6bafcb Fix issue if there is no working stdatomic / atomic header:
definition of atomic_compare_exchange_strong_explicit was wrong.
Same for atomic_compare_exchange_weak_explicit
2018-04-19 22:14:16 +02:00
Patrick Pelissier 704fb0cbd9 Add a cast to avoid a warning with some compilers
(I am not sure the warning was justified).
2018-04-19 22:13:53 +02:00
Patrick Pelissier 3be27aefa3 Fix the format for size_t 2018-04-19 22:13:27 +02:00
Patrick Pelissier 8be81ac09e Update the list of tested systems. 2018-04-19 22:13:05 +02:00
Patrick Pelissier e2fc9c3699 Update 2018-04-19 20:21:54 +02:00
Patrick Pelissier 472eaff6f0 Add gene queue 2018-04-19 20:20:18 +02:00
Patrick Pelissier 805b016aee Reformat test and reduce pressure on thread creation for some tests
(Some systems didn't handle so much thread creation).
2018-04-17 23:01:05 +02:00
Patrick Pelissier 624a13ba1a Update version with added parallelism for long computation. 2018-04-17 00:00:56 +02:00
Patrick Pelissier 9692bf3991 Start list of API breakage. 2018-04-17 00:00:29 +02:00
Patrick Pelissier 3f5406f1d4 API breakage:
worker_sync takes an extra parameter (worker_t) to perform a
wait-on-event instead of a busy waiting, reducing consummer ressources.

worker_init takes an int instead of an unsigned int for the number of workers.
It allows additional extra value -1 to initialize the number of workers to
twice the number of CPU, allowing to overload the CPU with loads and keep
them fully working in some scenario.
2018-04-16 23:57:28 +02:00
Patrick Pelissier b684a62f8b Add more local information about the origin of a failure. 2018-04-16 22:05:17 +02:00
Patrick Pelissier 6bb94db22f Add function dict_splice 2018-04-15 21:02:14 +02:00
Patrick Pelissier d70a971d2e Add note 2018-04-15 20:59:04 +02:00
Patrick Pelissier b05b515f3d Reformat 2018-04-15 20:58:52 +02:00
Patrick Pelissier 12b15e0037 Add function array_splice just like function list_splice
as it can be done faster at low level.
2018-04-15 11:41:32 +02:00
Patrick Pelissier 2c9169308b Add code generation for OA dict 2018-04-15 10:53:55 +02:00
Patrick Pelissier 74a3d50c88 Minor optimization for _clean
No need to fill in the complete array with invalid param
as the array will be reduced.
2018-04-15 10:53:15 +02:00
Patrick Pelissier 6b8bb33221 Convert unsigned long to size_t in function prototypes for 32 bits systems. 2018-04-15 01:10:49 +02:00
Patrick Pelissier 175fb46508 Fix issue for 32 bits system 2018-04-15 00:48:31 +02:00
Patrick Pelissier 2f65400641 Add target checkall to lanch the test suite wih multiple compilers / options. 2018-04-15 00:40:48 +02:00
Patrick Pelissier b5f4f2fc4b Finish splitting m_core_clz into m_core_clz32 & m_core_clz64 2018-04-15 00:10:38 +02:00
Patrick Pélissier 8c34f44dae Finish fixing for 32 bits / clz 2018-04-14 23:14:21 +02:00
Patrick Pélissier d97f46133b Fix issue for 32 bits systems
mpz_core_clz wasn't reproductible for 32 bits system.
2018-04-14 23:08:53 +02:00
Patrick Pélissier a94a5feb84 Fix issue with 32 bits systems (size_t != unsigned long) 2018-04-14 20:52:29 +02:00
Patrick Pelissier ead5d7511a Use of proper oplist with the CMP method. 2018-04-14 08:33:37 +02:00
Patrick Pelissier 1cd7446013 Add more instantiation of the containter 2018-04-14 08:27:26 +02:00
Patrick Pelissier 40747a463b Remove dependecies from GMP header, allowing to run the test suite without GMP 2018-04-14 08:16:31 +02:00
Patrick Pelissier 8e20118ab6 Reformat 2018-04-14 08:12:58 +02:00
Patrick Pelissier 19f330e670 Rename also MYMPZ2_OPLIST into MY_MPZ_CMP_OPLIST 2018-04-14 08:12:27 +02:00
Patrick Pelissier 20d549b5e1 Rename MYMPZ_OPLIST into MY_MPZ_OPLIST 2018-04-14 08:11:14 +02:00
Patrick Pelissier e09fa89781 Little bit rewrite to help the compiler optimizes the loop a bit more:
as the variables are not longer on the stack, it uses more registers
and more instructions in the critical loop than before. By
copying the data into stack local variable, the compiler is once again
able to optimize the loop properly.
2018-04-14 08:07:01 +02:00
Patrick Pelissier 70b1421114 Update after review 2018-04-13 20:56:48 +02:00
Patrick Pelissier b80a30785a Fix 2018-04-13 20:28:57 +02:00
Patrick Pelissier c3245ac301 Full refactorisation and proper use of M*LIB.
Should be as fast as before.
2018-04-13 20:28:01 +02:00
Patrick Pelissier 085d5b411c Add oplist for B*TREE 2018-04-13 20:16:23 +02:00
Patrick Pelissier 8c26d6d0b5 Update 2018-04-12 23:39:19 +02:00
Patrick Pelissier 76f5085e84 Merge branch 'master' of https://github.com/P-p-H-d/mlib 2018-04-11 22:42:46 +02:00
Patrick Pelissier 80d78e3270 Add bounded string cte macro 2018-04-11 22:42:22 +02:00
Patrick Pelissier 654521b74c Add some tests of _equal_p 2018-04-10 23:25:04 +02:00
Patrick Pelissier 353d2d4cc9 Fix issue in copy of B*TREE:
the _next field wasn't properly set for the deepest nodes.
2018-04-10 23:23:36 +02:00
Patrick Pelissier b38d84419a Add methods hash, eqal_p, out_str, in_str & get_str
Format of serialisation needs to be properly uniformised between:
dictionnary,
red-black tree,
set,
b+tree with key,value,
b+tree with only key.
2018-04-09 18:42:34 +02:00
Patrick Pelissier 0d085771e4 Add tests for new methods 2018-04-09 18:07:03 +02:00
Patrick Pelissier f3b2b6b41e Add methods _swap, _move & _init_move 2018-04-09 18:06:48 +02:00
Patrick Pelissier fb35ad7ab3 Update due no new LIBSRT API. 2018-04-08 21:43:25 +02:00
Patrick Pelissier cf6e721994 Fix wrong usage of CollectionC by the benchmark. 2018-04-08 21:32:57 +02:00
Patrick Pelissier 9faea5ff15 Rewrite bench code so that it computes what is expected. 2018-04-08 21:10:29 +02:00
Patrick Pelissier fe18009bc0 Rewrite the loop so that the result is the same for dict and dict_oa. 2018-04-08 21:07:20 +02:00
Patrick Pelissier 10f900ea3e Rewrite code to avoid warning if optimisations are turned on. 2018-04-08 20:27:37 +02:00
Patrick Pelissier 63874694f8 Rewrite the code to avoid some silly warnings on some implementation. 2018-04-08 20:01:04 +02:00
Patrick Pelissier 9b9e246e66 More tests 2018-04-08 11:16:31 +02:00
Patrick Pelissier e6a6eb9be5 Fix issue in it_from for B+TREE 2018-04-08 11:13:48 +02:00
Patrick Pelissier 050bb5dbfa Add tests for it_from & it_to_p for B+TREE 2018-04-08 11:13:22 +02:00
Patrick Pelissier b005ec1845 Fix function max for B+TREE 2018-04-08 10:48:02 +02:00
Patrick Pelissier f158a94e0a Add tests for min & max 2018-04-08 10:47:52 +02:00
Patrick Pelissier a166620a6d Add method _from, _to_p, _min & _max for B+TREE 2018-04-08 10:39:27 +02:00
Patrick Pelissier 47fb467878 Add basic it test for B+tree 2018-04-08 07:24:56 +02:00
Patrick Pelissier 67af80c772 Fix issue in it_end for B+TREE 2018-04-08 07:22:34 +02:00
Patrick Pelissier 1f44155127 Add one more corner case 2018-04-07 12:11:50 +02:00
Patrick Pelissier 87a3ac4a76 Add test for methods _init_set & _set for B+tree 2018-04-07 12:10:50 +02:00
Patrick Pelissier 73354a04d5 Add method _init_set & _set for B+tree 2018-04-07 12:10:19 +02:00
Patrick Pelissier cb3038e73c Add more checks 2018-04-07 11:51:38 +02:00
Patrick Pelissier b6ec31fe11 Add more test. 2018-04-07 11:50:18 +02:00
Patrick Pelissier c7059a60ee Add more tests 2018-04-07 11:44:16 +02:00
Patrick Pelissier af49aaf8b1 Add more tests. 2018-04-07 11:42:12 +02:00
Patrick Pelissier 4149b8388b Improve quality of coverage report by not expanding contract function. 2018-04-07 11:41:43 +02:00
Patrick Pelissier df0d723714 Compute coverage for B+TREE 2018-04-07 11:28:50 +02:00
Patrick Pelissier 44209ed93e Add more tests 2018-04-07 11:19:58 +02:00
Patrick Pelissier 930a3c8637 More tests. 2018-04-07 11:14:31 +02:00
Patrick Pelissier d2f5698397 iMake oplist optional for BPTREE_DEF 2018-04-07 11:10:13 +02:00
Patrick Pelissier 42d9fc3284 Update doc 2018-04-07 00:43:51 +02:00
Patrick Pelissier 91db494898 Add basic iterator for B+tree
I can't match the exact interface of a dictionary of the key and the value are in separate place in the structure
(so I can't return a pair of (key,value)). Instead I return a pair of pointer (key_ptr, value_ptr).
I don't see any other way which doesn't evolve copy of the key/value.
For the RBtree interface, no such problem occurs.
2018-04-05 22:44:36 +02:00
Patrick Pelissier 75217d4914 Rewrite / cleanup 2018-04-04 22:57:47 +02:00
Patrick Pelissier ceaf004719 Remove wrong comment 2018-04-04 22:55:19 +02:00
Patrick Pelissier 27b7380433 B+TREE: Add _size operator 2018-04-04 22:54:39 +02:00
Patrick Pelissier 7ca254ade9 B+TREE: Add _pop function. 2018-04-04 22:39:46 +02:00
Patrick Pelissier c91864c2a7 B+TREE: _remove returns a boolean indicating if the item
removed was present or not
2018-04-04 22:35:47 +02:00
Patrick Pelissier 4925297a35 Update doc 2018-04-04 20:27:44 +02:00
Patrick Pelissier d7ece50ed0 Replace "Dict" by "DictOA" since KLIB uses an OpenAddressing dictionnary. 2018-04-03 20:13:18 +02:00
Patrick Pelissier 3ef4a9f413 Fix issue with the way of benching the BTREE of KLIB.
Forgot to initialize the key in the search pass.
2018-04-03 20:10:11 +02:00
Patrick Pelissier 9768b5b37a Add bench of B+TREE 2018-04-03 20:07:21 +02:00
Patrick Pelissier e4cd999bd0 Test expansion of new container. 2018-04-03 20:01:24 +02:00
Patrick Pelissier 8e335535cc Add new container without a value. 2018-04-03 20:01:04 +02:00
Patrick Pelissier 2f53d98806 Fix issue with kind_s being redefined if the macro is used again.
Fix issue of not using proper method for type.
Render usage of value optional so that we can create B+TREE with only keys.
2018-04-03 18:29:28 +02:00
Patrick Pelissier 5d99331358 Add expansion of a user type. 2018-04-03 18:29:16 +02:00
Patrick Pelissier 316022cde4 Add oplist with CMP operator 2018-04-03 18:28:51 +02:00
Patrick Pelissier 8d1abca8de Add link to the comparison between STL & M*LIB 2018-04-03 12:47:44 +02:00
Patrick Pelissier 1b2e73eaa2 Prepare doc 2018-04-03 12:43:30 +02:00
Patrick Pelissier 6127371920 Add B+TREE container.
Fix #4
2018-04-03 12:40:29 +02:00
Patrick Pelissier 7936dbbf8c Minor fix 2018-03-31 16:31:47 +02:00
Patrick Pelissier 8aa45309a2 Update doc 2018-03-31 12:10:50 +02:00
Patrick Pelissier d75237ba66 Update 2018-03-31 12:05:42 +02:00
Patrick Pelissier 9909877771 Avoid warning with C++ 2018-03-31 12:01:05 +02:00
Patrick Pelissier 64ef1f3aba Fix classic issue with C++ 2018-03-31 11:56:28 +02:00
Patrick Pelissier d670e1a6bb Update 2018-03-31 10:05:24 +02:00
Patrick Pelissier 7082a4dc24 Fix wrong initialization of ATOMIC_VAR_INIT for the emulation layer
(wrong since the simplication of the atomic structure).
2018-03-31 10:04:41 +02:00
Patrick Pelissier e6c9314c91 Reorder the list of other libraries. 2018-03-30 21:39:10 +02:00
Patrick Pelissier 3dccd4484e Add nedtries 2018-03-30 21:35:19 +02:00
Patrick Pelissier 170ed98701 Remove dependency of boost/glib to the pool benchmark 2018-03-29 20:51:42 +02:00
Patrick Pelissier 54fa2c8635 Don't assume boost is always installed on the platform. 2018-03-29 20:12:53 +02:00
Patrick Pelissier bc83fe43e5 Remove dependency on GMP, so that it is easier to run the bench suite. 2018-03-29 20:01:45 +02:00
Patrick Pelissier 18710567f2 Make QT & GLIB optional libraries 2018-03-29 19:19:48 +02:00
Patrick Pelissier 9a98ed5cb6 Add PHONY target. 2018-03-29 19:15:11 +02:00
Patrick Pelissier 35a98fd3b5 Remove unused header file 2018-03-29 19:13:59 +02:00
Patrick Pelissier cd74fdf234 time command doesn't work reliabely in Makefile 2018-03-29 19:13:35 +02:00
Patrick Pelissier 6cccbfc73a Add worker_sync_p function. 2018-03-26 22:13:50 +02:00
Patrick Pelissier e59308a9a3 Update documentation. 2018-03-26 21:55:51 +02:00
Patrick Pelissier 85a3be4ae3 Update documentation 2018-03-26 21:45:06 +02:00
Patrick Pelissier d873693fe7 Update to ignore 'other' directory 2018-03-24 12:47:19 +01:00
Patrick Pelissier 2df967d5c2 add TODO 2018-03-24 08:52:20 +01:00
Patrick Pelissier 20989a7ab5 Add test for bounded string. 2018-03-24 08:42:47 +01:00
Patrick Pelissier 8e114d6fe4 Add bounded string definition so that you can easily use them for a container. 2018-03-24 08:42:15 +01:00
Patrick Pelissier 479ad2d4ba Update doc 2018-03-23 22:07:04 +01:00
Patrick Pelissier d05bb49f5d Add more assertions.
Change prototype of fputs to return the error / success status.
2018-03-23 21:25:55 +01:00
Patrick Pelissier fc9755379d Add ctemplates 2018-03-23 20:10:37 +01:00
Patrick Pelissier a85db0c383 Add libgenerics 2018-03-21 23:13:34 +01:00
Patrick Pelissier d19ec5bf49 Fix format of _size 2018-03-21 20:11:31 +01:00
Patrick Pelissier ba72e4ebbf Remove check in protected contract:
since there are two mutex now, there are data race when we want to check it.
TODO: Properly separate the contract.
2018-03-17 19:54:59 +01:00
Patrick Pelissier 5936e25ce4 Add test 2018-03-17 09:24:26 +01:00
Patrick Pelissier d82dc196e0 Fix datarace (?) if support for pop deferred has been activated
(a rewake of thread may be lost)
2018-03-16 23:55:16 +01:00
Patrick Pelissier c174b368e8 Fix issue with atomic variables needing writable variable even for reading only. 2018-03-16 23:27:08 +01:00
Patrick Pelissier 81e3515f40 Add missing tests 2018-03-16 23:06:15 +01:00
Patrick Pelissier bffec6206b Remove usage of variable array extension in C tests to avoid warning with C++ compilers. 2018-03-16 23:02:28 +01:00
Patrick Pelissier d269c23948 Remove warning with C++.
It'll will increase the size of the type a little bit.
TODO: Check compatibility between code using C def and code using C++ def
2018-03-16 23:00:46 +01:00
Patrick Pelissier f2b2335080 Fix issue with C++ not supporting passing an int constant value to an unsigned atomic type...
I definitely find this disturbing.
2018-03-16 22:59:51 +01:00
Patrick Pelissier 5dded0d9aa Clarify the displayed message 2018-03-16 22:30:59 +01:00
Patrick Pelissier 33142c3a0d Fix build for flathaspmap (needs of C++14 and not C++11). 2018-03-16 09:27:00 +01:00
Patrick Pelissier 06e256f77a Fix build with newer version of library 2018-03-16 09:13:34 +01:00
Patrick Pelissier 3f13cca666 Fix compilation with newer version of library 2018-03-16 09:04:32 +01:00
Patrick Pelissier 73117708e5 Analyze generated code 2018-03-14 23:51:51 +01:00
Patrick Pelissier f88b53dce5 Fix issue 2018-03-14 23:51:43 +01:00
Patrick Pelissier 3e609bab04 Add method pop_until 2018-03-14 23:46:30 +01:00
Patrick Pelissier a6bf530c5f Remove a warning. 2018-03-14 22:19:16 +01:00
Patrick Pelissier 5d003b7678 Generate more functions 2018-03-14 22:18:48 +01:00
Patrick Pelissier a5cbc3f34a Rename method into GET_KEY. Add method SET_KEY 2018-03-13 22:22:26 +01:00
Patrick Pelissier 52e92e1ebf Bench stable sort 2018-03-11 00:36:51 +01:00
Patrick Pelissier 5307b6c097 Add stable sort for array 2018-03-11 00:36:38 +01:00
Patrick Pelissier dd4f4761de Add GET_AT method
:
2018-03-10 19:26:59 +01:00
Patrick Pelissier 96f3533e46 Fix bug for default oplist used by M_A1_OPLIST
o
2018-03-10 18:52:16 +01:00
Patrick Pelissier 774e1720b7 Add M_SWAP_DEFAULT 2018-03-10 18:48:27 +01:00
Patrick Pelissier 83f8eadce9 Fix name of variable without a macro (shadow variable) 2018-03-10 18:09:23 +01:00
Patrick Pelissier f8e75761ef Change SNAPSHOT_SRSW to SNAPSHOT_SPSC to be uniform with queue definition
(Replace Reader / Writer term by Consummer / Producer term)
2018-03-10 17:46:02 +01:00
Patrick Pelissier cbb881df86 Fix overwrite branch which has a data race on number.
Rewrite it to be simpler & faster and without data race.
2018-03-10 10:13:54 +01:00
Patrick Pelissier 103e5304e8 Use of 2 mutex for buffer:
* one for the enqueue
* one for the dequeue
The number of items in the buffer is the only variable which can be accessed
by both at the same time.
This improve the performance of the communication and reduce the possibility
of priority inversion between the producer and the consummer thread.
2018-03-09 21:07:21 +01:00
Patrick Pelissier e152ff0377 Add TODO 2018-03-09 21:07:07 +01:00
Patrick Pelissier 65f39ab22e Optmize the buffer queue:
don't broadcast the condition variable if we know that
 no thread is waiting for it.
 Improve bench test from 700ms to 450ms
2018-03-09 19:15:47 +01:00
Patrick Pelissier df51c6dc64 Add m_cond_broad_cast 2018-03-09 19:15:31 +01:00
Patrick Pelissier b7c86ee01e Fix detection
of GCC
2018-03-06 21:58:41 +01:00
Patrick Pelissier 992bb6a73a Add bench of hash computation library (XXHASH) 2018-03-04 01:16:37 +01:00
Patrick Pelissier 557dd3e0d5 Fix spelling 2018-03-03 18:32:08 +01:00
Patrick Pelissier c8d5b5440f Add documentation for LIST_DUAL_PUSH_DEF 2018-03-03 18:30:01 +01:00
Patrick Pelissier 0900e72070 Add TODO 2018-02-28 21:44:52 +01:00
Patrick Pelissier dcdc64f974 Update graph 2018-02-28 21:34:47 +01:00
Patrick Pelissier 58898adbcd Add target to Generate depend.png 2018-02-28 21:34:31 +01:00
Patrick Pelissier ff2050466b Fix spelling 2018-02-28 21:17:16 +01:00
Patrick Pelissier dd209c05b9 Use new kind of dictionary for bench 2018-02-28 21:07:53 +01:00
Patrick Pelissier 12a4806b85 Add documentation 2018-02-28 21:07:43 +01:00
Patrick Pelissier 82575962ed Add expansion of the new kind of dictionary 2018-02-28 21:07:29 +01:00
Patrick Pelissier 1db2813aa3 Add dictionary with store computed hash, avoiding the need
of recomputation in some cases.
2018-02-28 21:06:58 +01:00
Patrick Pelissier c71613dcfe Don't display warnings about unused variables in release mode. 2018-02-28 21:05:15 +01:00
Patrick Pelissier 762d8a8e74 Update assertion 2018-02-27 22:43:17 +01:00
Patrick Pelissier b7966f6b61 Update assertions 2018-02-27 22:41:20 +01:00
Patrick Pelissier b1443533e0 Update comment 2018-02-27 22:41:11 +01:00
Patrick Pelissier 6f12ac1d19 Update comments & add new assertions 2018-02-27 22:32:03 +01:00
Patrick Pelissier 0295eea145 Rewrite the prototype of intrusive shared object
to be more clear when there is a shared pointer
and when there is not.
2018-02-26 21:38:33 +01:00
Patrick Pelissier 171402f99f Fix bug 2018-02-25 11:52:21 +01:00
Patrick Pelissier 3d57d35536 Update test 2018-02-25 11:52:14 +01:00
Patrick Pelissier bacf2b9eec Add more test for dual push list 2018-02-25 11:43:31 +01:00
Patrick Pelissier fd885bdb19 Rewrite a little bit singly linked list
to be up-to-date with standards of other containers.
2018-02-25 11:24:44 +01:00
Patrick Pelissier a497c07b0b Update test 2018-02-24 21:41:30 +01:00
Patrick Pelissier ffd5f23406 Update due to new generic type 2018-02-24 21:41:16 +01:00
Patrick Pelissier 97e3cc35c2 Add more tests. 2018-02-24 20:26:32 +01:00
Patrick Pelissier 44d8363292 Fix issue with assertion 2018-02-24 20:26:19 +01:00
Patrick Pelissier e540a95ad0 Fix spelling 2018-02-24 15:28:07 +01:00
Patrick Pelissier db63b0d515 Fix code generation analyzis for list
Add dual push list
2018-02-24 15:21:23 +01:00
Patrick Pelissier 7637c2fb58 Update test 2018-02-24 15:16:11 +01:00
Patrick Pelissier 5b40b43435 Add some tests. 2018-02-24 15:13:34 +01:00
Patrick Pelissier 6ec277ec99 Add dual push singly linked list 2018-02-24 15:07:58 +01:00
Patrick Pelissier 1befa63c9b Remove duplicate code. 2018-02-24 15:04:44 +01:00
Patrick Pelissier 3d3e052f23 Add reference to wikipedia data structure in the documentation 2018-02-24 09:03:47 +01:00
Patrick Pelissier aea816dcaf Fix data race 2018-02-24 07:44:16 +01:00
Patrick Pelissier d258fb3eff Fix memory leak 2018-02-23 23:13:10 +01:00
Patrick Pelissier 1463d83b9f Fix memory leak in test 2018-02-23 23:10:26 +01:00
Patrick Pelissier 10c947d9d4 Support of monocore cpu 2018-02-23 22:21:20 +01:00
Patrick Pelissier 2da51ea6e9 Remove constraint on size > 0
as workers can create a queue of size 0
in mono core system.
2018-02-23 22:12:17 +01:00
Patrick Pelissier d930c0a4ad Fix memory leak in test suite 2018-02-23 22:00:15 +01:00
Patrick Pelissier d0e7cc41bf Add more assertions 2018-02-23 20:40:44 +01:00
Patrick Pelissier 8c610adee5 Update README 2018-02-23 20:39:37 +01:00
Patrick Pelissier a1e791d1e9 Add queue SPSC 2018-02-23 20:38:51 +01:00
Patrick Pelissier 264249fb8b Improve queue MPMC contract checking 2018-02-23 20:13:04 +01:00
Patrick Pelissier e2b00d7c73 Split function definitions into separate macro 2018-02-21 22:16:55 +01:00
Patrick Pelissier ff3964e2eb Factorization of the mympz type 2018-02-21 22:06:45 +01:00
Patrick Pelissier 2ed95bfe16 Fix generic issue for deque
(was totaly broken before).
2018-02-21 22:05:51 +01:00
Patrick Pelissier 958936319f Activate test 2018-02-21 21:51:54 +01:00
Patrick Pelissier 39fca33958 Reuse memory node in case of pop from the opposite side
if there is at least one free node in the opposite side.
This reduce the memory consumption ***a lot*** for FIFO queues
usage.
2018-02-21 21:50:40 +01:00
Patrick Pelissier eb4cbe863e Add some TODO 2018-02-20 23:10:47 +01:00
Patrick Pelissier d146ea6a63 Add new test (disabled) 2018-02-20 22:57:58 +01:00
Patrick Pelissier f4b2ba8322 Fix coverage 2018-02-20 22:12:09 +01:00
Patrick Pelissier acb1aa8104 Add new restriction 2018-02-20 22:12:03 +01:00
Patrick Pelissier 03f7a75e54 Add more check 2018-02-20 22:01:26 +01:00
Patrick Pelissier 2dc71cc5fd Fix bug 2018-02-20 22:01:19 +01:00
Patrick Pelissier 4a92419bbe Add tests 2018-02-20 21:55:16 +01:00
Patrick Pelissier 92699501f6 Add more assertions 2018-02-19 23:41:29 +01:00
Patrick Pelissier b49ed97be0 Update comment 2018-02-19 23:38:22 +01:00
Patrick Pelissier aec661edea Add shared ressource container 2018-02-19 23:37:10 +01:00
Patrick Pelissier 675fa2cf85 Add note to document 2018-02-19 23:36:36 +01:00
Patrick Pelissier b442d72ef7 Add forgotten free + check for memory allocation 2018-02-19 23:14:16 +01:00
Patrick Pelissier 2ff197c670 Add function _size 2018-02-19 23:14:09 +01:00
Patrick Pelissier 5c9a610823 Fix data race issue 2018-02-19 22:49:05 +01:00
Patrick Pelissier 8d871256ed Reformat 2018-02-19 22:48:46 +01:00
Patrick Pelissier 3d59fb0d9a Fix a deadlock due to an ABA like bug. 2018-02-19 19:00:24 +01:00
Patrick Pelissier 057d783158 Add comment 2018-02-18 19:19:41 +01:00
Patrick Pelissier ffb8a2a00a Improve test suite
(Test fails randomly)
2018-02-18 19:18:40 +01:00
Patrick Pelissier b32b93340c Improve tests & fix bugs.
Still one bug remain.
2018-02-18 17:01:41 +01:00
Patrick Pelissier 35a3f7c02a Improve test 2018-02-18 09:55:31 +01:00
Patrick Pelissier 6de4c4092f Add MRMW snapshot 2018-02-18 08:27:10 +01:00
Patrick Pelissier c5de5e07d8 Update comment 2018-02-18 08:00:14 +01:00
Patrick Pelissier 0176071011 Add proper test for MRSW snapshot 2018-02-18 07:58:48 +01:00
Patrick Pelissier 3f2a641cdf Add contract 2018-02-17 20:25:40 +01:00
Patrick Pelissier 20be2d8850 Externalized currentWrite 2018-02-17 20:21:53 +01:00
Patrick Pelissier b37cd7dba7 Rewrite / simplify code 2018-02-17 20:08:00 +01:00
Patrick Pelissier e274a49ffd Fix bug as we can't fail spuriously here. 2018-02-17 19:28:09 +01:00
Patrick Pelissier cba37abfae Reword 2018-02-17 19:24:16 +01:00
Patrick Pelissier be198a7040 Add MRSW snapshots. 2018-02-17 19:19:51 +01:00
Patrick Pelissier 29d181c113 Add macro M_CTYPE_FROM_FIELD like M_TYPE_FROM_FIELD
but for const pointer.
2018-02-17 19:19:09 +01:00
Patrick Pelissier 3d5914f389 Fix issue in test 2018-02-17 18:50:03 +01:00
Patrick Pelissier c78c75d89a Add more test 2018-02-17 18:48:52 +01:00
Patrick Pelissier a4753be5ae Add test for int snapshot MRSW 2018-02-17 18:20:25 +01:00
Patrick Pelissier 3bda360196 Add first step of MRSW snapshot 2018-02-17 17:35:18 +01:00
Patrick Pelissier 2b4de98aee Update comment 2018-02-17 17:35:07 +01:00
Patrick Pelissier bbd887fd2c Update comments 2018-02-17 14:21:54 +01:00
Patrick Pelissier 84a5add410 Recursive clean 2018-02-17 14:03:24 +01:00
Patrick Pelissier c999563ecd Fix spelling 2018-02-17 12:02:54 +01:00
Patrick Pelissier 80c7cdbdd2 Rename snapshot_def as snapshot_srsw_def 2018-02-17 12:00:45 +01:00
Patrick Pelissier 9c7b453732 Update doc 2018-02-17 11:56:04 +01:00
Patrick Pelissier 45982c4b01 Improve test suite 2018-02-17 11:06:28 +01:00
Patrick Pelissier 438704be37 Rewrite & fix int queue generator.
Limitation to 4096 max entries.
A classic queue may be way faster (too much sharing for thread) but will use much more memory...
To bench
2018-02-16 21:22:46 +01:00
Patrick Pelissier 1753ddc4d6 Improve bitset_at version by avoiding a redundant variable shift. 2018-02-16 20:42:42 +01:00
Patrick Pelissier 96178194b3 Update size to long long 2018-02-16 18:00:08 +01:00
Patrick Pelissier 57fb2192be Add libdict 2018-02-16 07:40:20 +01:00
Patrick Pelissier 546a78be33 Fix spelling 2018-02-15 22:48:25 +01:00
Patrick Pelissier 29acb67566 Update example of snapshot to a better one. 2018-02-15 22:43:43 +01:00
Patrick Pelissier 0afad9a825 Update comments. 2018-02-15 22:34:36 +01:00
Patrick Pelissier c99b3de902 Fix double minus which transformed into + ! 2018-02-15 22:31:50 +01:00
Patrick Pelissier e8cf1d6b9a Update doc 2018-02-14 21:12:38 +01:00
Patrick Pelissier a9aa1e13ee Add relaxed shared pointer (thread unsafe with non-atomic counter). 2018-02-10 17:31:12 +01:00
Patrick Pelissier f5857c8aab Create an oplist for the counter and use it.
It will allow to use other oplist.
2018-02-10 17:22:51 +01:00
Patrick Pelissier 67856a98af Avoid false sharing for snapshot by aligning properly the
data of the triple buffer to a cache boundary.
2018-02-10 08:29:34 +01:00
Patrick Pelissier c71de15cb0 Update comment 2018-02-10 08:22:37 +01:00
Patrick Pelissier 73f15c1980 Update comment 2018-02-10 08:22:12 +01:00
Patrick Pelissier 646df4f34e Move aligned cache line constant to commun header. 2018-02-10 08:20:37 +01:00
Patrick Pelissier 8489d0df97 Clarify algorithm 2018-02-09 20:56:28 +01:00
Patrick Pelissier 86dfad860e Improve coverage 2018-02-05 22:28:35 +01:00
Patrick Pelissier c97a68029b Improve coverage 2018-02-05 22:18:56 +01:00
Patrick Pelissier 08a4dfbe4c Improve coverage 2018-02-05 22:14:14 +01:00
Patrick Pelissier adcacf3cf9 Improve coverage 2018-02-05 22:04:50 +01:00
Patrick Pelissier 7950b69136 Improve coverage 2018-02-05 21:47:22 +01:00
Patrick Pelissier 5d75416948 Improve coverage 2018-02-05 21:41:28 +01:00
Patrick Pelissier 2e96a00fde Improve coverage. 2018-02-05 21:25:11 +01:00
Patrick Pelissier 1f08bcf0f8 Add clear callback for workers. 2018-01-29 22:08:21 +01:00
Patrick Pelissier a3e2e75021 Use new macro. 2018-01-29 21:59:57 +01:00
Patrick Pelissier 603bdd2941 Add new macro M_DEFAULT_ARGS 2018-01-29 21:59:39 +01:00
Patrick Pelissier 6fb7a4f24b Update doc 2018-01-29 21:21:54 +01:00
Patrick Pelissier 0c2d290835 Add missing copyright notice. 2018-01-29 20:29:54 +01:00
Patrick Pelissier 210e05f7fc Update copyright to 2018 2018-01-29 20:28:30 +01:00
Patrick Pelissier f4b443c33e Finish documenting the string API. 2018-01-27 22:28:48 +01:00
Patrick Pelissier 52ffa09f73 Fix issue 2018-01-24 22:30:12 +01:00
Patrick Pelissier 52bb23bb6f Update doc 2018-01-24 22:28:52 +01:00
Patrick Pelissier 2089af8fd3 Fix broken code due to previous commits. 2018-01-24 22:26:46 +01:00
Patrick Pelissier 778bc4072b Rename parameter 2018-01-24 22:23:56 +01:00
Patrick Pelissier 2b2ebecae7 Improve comments. 2018-01-23 23:03:02 +01:00
Patrick Pelissier 69cbd61915 Document string API. 2018-01-23 22:53:37 +01:00
Patrick Pelissier 4d41774345 Rename STRING_CONTRACT into internal macro STRINGI_CONTRACT 2018-01-23 21:45:45 +01:00
Patrick Pelissier ff6772aaa7 Ignore depend files 2018-01-13 13:55:36 +01:00
Patrick Pelissier bbe9d43532 Move chapter ERRORS below 2018-01-13 00:14:52 +01:00
Patrick Pelissier 6549bea3e0 Fix issue for BSD 2018-01-12 20:11:11 +01:00
Patrick Pelissier 9d727589aa Fix data race reported by thread sanitinizer 2018-01-12 18:58:27 +01:00
Patrick Pelissier aede0547d3 Optimization: replace a division into a conditional move. 2018-01-12 18:56:01 +01:00
Patrick Pelissier 55332d61fd Fix data race for buffer in POP_RELEASE mode. 2018-01-12 18:52:34 +01:00
Patrick Pelissier 83eeb5d6d0 Make the code more robust 2018-01-12 18:48:38 +01:00
Patrick Pelissier 5eaf796aab Remove warnings 2018-01-12 18:46:26 +01:00
Patrick Pelissier d46c40d3fa Don't compute coverage of the float expansion. 2018-01-09 20:05:05 +01:00
Patrick Pelissier 2461afa573 Improve coverage suite. 2018-01-09 20:03:05 +01:00
Patrick Pelissier 6acacf5efe Fix issue for hash computation. 2018-01-09 20:02:53 +01:00
Patrick Pelissier 075a74ea0c Don't expand contract for coverage. 2018-01-09 18:52:18 +01:00
Patrick Pelissier f9ddc43349 Add bench k-nucleotide as per
http://benchmarksgame.alioth.debian.org/u64q/knucleotide.html
2018-01-09 18:44:11 +01:00
Patrick Pelissier 1c9a232a9d Add 'worker_flush' function.
Factorization of the exec function.
2018-01-07 22:15:58 +01:00
Patrick Pelissier 9c727a8d8c Update doc 2018-01-07 21:57:23 +01:00
Patrick Pelissier 7c241c65de Add new lib for smart pointers in Gnu C 2018-01-07 00:09:38 +01:00
Patrick Pelissier 4f3823bfeb Add new function _blocking allowing to use
runtime behavior of the blocking characteristic of the
buffer regardless on how it has been configured.
2018-01-07 00:07:27 +01:00
Patrick Pelissier 3f7b16c736 If the size is constant (!= 0) when defining the buffer
then the array is embedded within the structure rather
than allocated when initialization of the structure
occurs.
2018-01-06 22:57:14 +01:00
Patrick Pelissier 362fbb39d4 Update comments 2018-01-06 21:58:30 +01:00
Patrick Pelissier 38fe24085f Improve comments. 2018-01-05 23:26:27 +01:00
Patrick Pelissier af3d5cb273 Proper semantic 2018-01-05 20:46:28 +01:00
Patrick Pelissier 9200b742c5 Add iterators for priority queue. 2018-01-04 22:09:20 +01:00
Patrick Pelissier f39c8ebaba Fix comment 2018-01-04 22:08:08 +01:00
Patrick Pelissier bbb1831c5f Add missing exit(0) 2017-12-31 11:42:56 +01:00
Patrick Pelissier ecae3d0e1e Update doc 2017-12-31 02:10:18 +01:00
147 changed files with 30205 additions and 4476 deletions

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
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 check
shell: bash
run: mingw32-make CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
build_windows_visual:
strategy:
matrix:
platform: [windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v1
- name: make check
shell: cmd
working-directory: ./tests
run: Make-check-cl.bat
+9
View File
@@ -7,6 +7,8 @@
# Precompiled Headers
*.gch
*.pch
depend
*.depend
# Libraries
*.lib
@@ -31,3 +33,10 @@
# Debug files
*.dSYM/
*.su
# Others
other/
a*.dat
config
fail*.log
.vscode
+1 -1
View File
@@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2017, Patrick Pelissier
Copyright (c) 2017-2020, Patrick Pelissier
All rights reserved.
Redistribution and use in source and binary forms, with or without
+55 -5
View File
@@ -1,6 +1,26 @@
PREFIX=/usr/local
# Environnement variable. To customize if needed.
RM=rm -f
MKDIR=mkdir -p
INSTALL=install
INSTALL_PROGRAM=${INSTALL}
INSTALL_DATA=${INSTALL} -m 644
# Installation directory
PREFIX=/usr/local
DESTDIR=
# Package name & version.
PACKAGE=m_lib-$(VERSION)
VERSION=0.4.0
# Define the contain of the distribution tarball
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-funcobj.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/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-mfuncobj.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
all:
@echo "Nothing to be done."
@@ -11,21 +31,51 @@ check:
cd tests && $(MAKE) check
cd example && $(MAKE) all
doc: README.md
checkall:
cd tests && $(MAKE) checkall
cd example && $(MAKE) all
html: doc
doc: README.md doc/depend.png
markdown < README.md > README.html
clean:
cd tests && $(MAKE) clean
cd example && $(MAKE) clean
cd bench && $(MAKE) clean
$(RM) -f README.html
$(RM) README.html depend.dot
$(RM) -r 'm*lib-$(VERSION)'
$(RM) 'm*lib-$(VERSION).tar.bz2'
distclean:
distclean: clean
cd tests && $(MAKE) distclean
depend:
cd tests && $(MAKE) depend
doc/depend.png: $(HEADER)
(echo "digraph g { " ; for i in *.h ; do list=$$(grep "include \"" $$i |cut -f2 -d\") ; for j in $$list ; do echo "\"$$i\" -> \"$$j\" ;" ; done ; done ; echo "}" )> depend.dot
dot -Tpng depend.dot -o doc/depend.png
optipng -o7 doc/depend.png
dist: $(HEADER) $(DOC1) $(DOC2) $(EXAMPLE) $(TEST) Makefile clean
$(MKDIR) '$(PACKAGE)'
$(MKDIR) '$(PACKAGE)/doc'
$(MKDIR) '$(PACKAGE)/tests'
$(MKDIR) '$(PACKAGE)/example'
cp $(HEADER) $(DOC1) Makefile '$(PACKAGE)'
cp $(EXAMPLE) '$(PACKAGE)/example'
cp $(TEST) '$(PACKAGE)/tests'
cp $(DOC2) '$(PACKAGE)/doc'
cd '$(PACKAGE)/tests' && $(MAKE) depend
tar jcf '$(PACKAGE).tar.bz2' '$(PACKAGE)'
distcheck: dist
cd '$(PACKAGE)' && make check
install:
$(MKDIR) $(DESTDIR)$(PREFIX)/include
cp m-*.h $(DESTDIR)$(PREFIX)/include
$(INSTALL_DATA) $(HEADER) $(DESTDIR)$(PREFIX)/include
uninstall:
for i in $(HEADER) ; do $(RM) $(DESTDIR)$(PREFIX)/include/$$i ; done
+4032 -672
View File
File diff suppressed because it is too large. Load diff
+131 -49
View File
@@ -1,15 +1,23 @@
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 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-emilib 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
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-densehashmap bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-hopscotchmap bench-uthash bench-qlibc bench-libsrt bench-nedtries bench-rigtorp-hashmap
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
queue: bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost
@@ -30,46 +38,65 @@ 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
@./bench-mlib.exe 30
@./bench-mlib.exe 31
@./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
@./bench-mlib-mempool.exe 30
@./bench-mlib-mempool.exe 31
@./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
# QT is supposed to be installed
# QT shall point to the the directory containing the file .pc are stored
bench-qt:
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
bench-qt0:
$(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
@@ -78,9 +105,11 @@ bench-qt:
@./bench-qt.exe 42
@./bench-qt.exe 50
#GLIB is supposed to be installed
# GLIB shall point to the the directory containing the file .pc are stored
bench-glib:
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `pkg-config --libs --cflags glib-2.0`
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
bench-glib0:
$(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
@@ -93,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
@@ -106,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
@@ -129,17 +155,17 @@ 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
# COLLECTIONC shall point to the directory where the 'src' directory is.
# COLLECTIONC shall point to the directory where it is installed
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/collectc" bench-collectionc.c common.c "$${COLLECTIONC}/lib/libcollectc.a" -o bench-collectionc.exe
@./bench-collectionc.exe 10
@./bench-collectionc.exe 20
@./bench-collectionc.exe 30
@@ -152,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
@@ -164,17 +190,29 @@ bench-flathashmap:
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
bench-flathashmap0:
$(CXX) $(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
# EMILIB shall point to the directory where the headers are
bench-emilib:
@if test -n "$${EMILIB}" ; then $(MAKE) bench-emilib0 ; else echo "Nothing to be done for EMILIB." ; fi
bench-emilib0:
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${EMILIB}" bench-emilib.cpp common.c -o bench-emilib.exe
@./bench-emilib.exe 41
@./bench-emilib.exe 42
@./bench-emilib.exe 46
@./bench-emilib.exe 43
# HOPSCOTCHMAP shall point to the directory where the source are
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
@@ -184,12 +222,11 @@ 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 40
@./bench-uthash.exe 41
@./bench-uthash.exe 42
@./bench-uthash.exe 43
@./bench-uthash.exe 50
# QLIBC shall point to the directory where the library is installed.
@@ -197,24 +234,18 @@ 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
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 42
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 41
# LIBSRT shall point to the directory where the source are
# LIBSRT shall point to the directory where it is installed
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
@./bench-libsrt.exe 10
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/include/libsrt" bench-libsrt.c common.c -o bench-libsrt.exe $${LIBSRT}/lib/libsrt.a
@./bench-libsrt.exe 20
@./bench-libsrt.exe 41
@./bench-libsrt.exe 42
@./bench-libsrt.exe 43
@./bench-libsrt.exe 50
@@ -222,9 +253,8 @@ bench-libsrt0:
# The library shall be built.
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.so -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
@@ -232,54 +262,106 @@ bench-liblfds0:
# The library shall be built.
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
# BOOST is assumed to be installed in the system
# 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) $(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:
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-boost.cpp -o bench-boost.exe -lpthread
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
bench-boost0:
$(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) $(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
############################################################################
bench-mempool:
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc `pkg-config --libs --cflags glib-2.0` -lboost_system -o bench-mempool.exe
@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) $(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
############################################################################
# BSTRLIB shall point to the BSTRLIB sources
# LIBSRT shall point to the LIBSRT sources
# LIBSRT shall point to the LIBSRT install directory
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-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-exe 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}/include/libsrt $${LIBSRT}/lib/libsrt.a" ; else $(MAKE) bench-string-step2 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)
#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-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-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-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);
}
+24 -19
View File
@@ -93,7 +93,7 @@ static void test_rbtree(size_t n)
stat = treetable_new(compare, &tree);
if (stat != CC_OK) abort();
for (size_t i = 0; i < n; i++) {
void *key = (void*)(uintptr_t) i;
void *key = (void*)(uintptr_t) rand_get();
stat = treetable_add(tree, key, key);
if (stat != CC_OK) abort();
}
@@ -122,11 +122,11 @@ static int equal_func(const void *key1, const void *key2)
{
const unsigned long pa = (uintptr_t) key1;
const unsigned long pb = (uintptr_t) key2;
return pa == pb;
return (pa < pb) ? -1 : pa > pb;
}
static void
test_dict(unsigned long n)
test_dict(size_t n)
{
HashTable *dict;
enum cc_stat stat;
@@ -138,8 +138,8 @@ test_dict(unsigned long n)
if (stat != CC_OK) abort();
for (size_t i = 0; i < n; i++) {
void *key = (void*)(uintptr_t) rand_get();
void *value = (void*)(uintptr_t) rand_get();
void *key = (void*)(uintptr_t) rand_get();
stat = hashtable_add(dict, key, value );
if (stat != CC_OK) abort();
}
@@ -164,7 +164,7 @@ static int char_equal (const void* a, const void * b)
{
const char_array_t *pa = (const char_array_t *)a;
const char_array_t *pb = (const char_array_t *)b;
return strcmp(*pa,*pb)==0;
return strcmp(*pa,*pb);
}
static size_t char_hash(const void *a, int l, uint32_t seed)
@@ -178,7 +178,7 @@ static size_t char_hash(const void *a, int l, uint32_t seed)
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
HashTable *dict;
enum cc_stat stat;
@@ -189,9 +189,14 @@ test_dict_big(unsigned long n)
stat = hashtable_new_conf(&htc, &dict);
if (stat != CC_OK) abort();
char_array_t **tab = malloc (sizeof(char_array_t *)*2*n);
if (!tab) abort();
size_t cpt = 0;
for (size_t i = 0; i < n; i++) {
char_array_t *key = malloc (sizeof (char_array_t));
char_array_t *value = malloc (sizeof (char_array_t));
tab[cpt++] = key;
tab[cpt++] = value;
sprintf(*key, "%u", rand_get());
sprintf(*value, "%u", rand_get());
stat = hashtable_add(dict, key, value );
@@ -209,6 +214,9 @@ test_dict_big(unsigned long n)
}
g_result = s;
hashtable_destroy(dict);
if (cpt != 2*n) abort();
for(size_t i = 0; i < 2*n;i++)
free(tab[i]);
}
/********************************************************************************************/
@@ -242,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);
}
+10 -10
View File
@@ -25,7 +25,7 @@ using namespace std;
/********************************************************************************************/
static void
test_dict2(unsigned long n)
test_dict2(size_t n)
{
dense_hash_map<unsigned long, unsigned long> dict;
dict.set_empty_key(-1);
@@ -75,7 +75,7 @@ namespace std {
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
dense_hash_map<char_array_s, char_array_s> dict;
struct char_array_s empty, del;
@@ -104,7 +104,7 @@ test_dict_big(unsigned long n)
/********************************************************************************************/
static void
test_dict_str(unsigned long n)
test_dict_str(size_t n)
{
dense_hash_map<string, string> dict;
string empty, del;
@@ -131,14 +131,14 @@ test_dict_str(unsigned long 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);
}
+138
View File
@@ -0,0 +1,138 @@
#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 "emilib/hash_map.hpp"
using namespace emilib;
using namespace std;
#include "common.h"
/********************************************************************************************/
static void
test_dict2(size_t n)
{
HashMap<unsigned long, unsigned long> dict;
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>::iterator it = dict.find(rand_get());
if (it != dict.end())
s += it->second;
}
g_result = s;
}
static void
test_dict2_linear(size_t n)
{
HashMap<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++) {
HashMap<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];
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;
};
};
}
static void
test_dict_big(size_t n)
{
HashMap<char_array_s, char_array_s> dict;
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>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
}
g_result = s;
}
/********************************************************************************************/
static void
test_dict_str(size_t n)
{
HashMap<string, string> dict;
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>::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},
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
};
int main(int argc, const char *argv[])
{
test("EMILIB", numberof(table), table, argc, argv);
exit(0);
}
+28 -10
View File
@@ -26,7 +26,7 @@ using namespace std;
/********************************************************************************************/
static void
test_dict2(unsigned long n)
test_dict2(size_t n)
{
flat_hash_map<unsigned long, unsigned long> dict;
for (size_t i = 0; i < n; i++) {
@@ -42,6 +42,23 @@ test_dict2(unsigned long 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];
@@ -62,7 +79,7 @@ namespace std {
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
flat_hash_map<char_array_s, char_array_s> dict;
@@ -86,7 +103,7 @@ test_dict_big(unsigned long n)
/********************************************************************************************/
static void
test_dict_str(unsigned long n)
test_dict_str(size_t n)
{
flat_hash_map<string, string> dict;
@@ -108,14 +125,15 @@ test_dict_str(unsigned long 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("FLAT-HASPMAP", numberof(table), table, argc, argv);
exit(0);
}
+12 -15
View File
@@ -97,7 +97,7 @@ static gboolean equal_func(gconstpointer a, gconstpointer b)
}
static void
test_dict(unsigned long n)
test_dict(size_t n)
{
GHashTable *dict = g_hash_table_new_full(hash_func, equal_func, free, free);
for (size_t i = 0; i < n; i++) {
@@ -140,7 +140,7 @@ static guint char_hash(gconstpointer a)
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
GHashTable *dict = g_hash_table_new_full(char_hash, char_equal, free, free);
for (size_t i = 0; i < n; i++) {
@@ -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);
}
+12 -11
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;
@@ -25,7 +25,7 @@ using namespace std;
/********************************************************************************************/
static void
test_dict2(unsigned long n)
test_dict2(size_t n)
{
tsl::hopscotch_map<unsigned long, unsigned long> dict;
@@ -63,7 +63,7 @@ namespace std {
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
hopscotch_map<char_array_s, char_array_s> dict;
@@ -87,7 +87,7 @@ test_dict_big(unsigned long n)
/********************************************************************************************/
static void
test_dict_str(unsigned long n)
test_dict_str(size_t n)
{
hopscotch_map<string, string> dict;
@@ -109,14 +109,15 @@ test_dict_str(unsigned long 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);
}
+13 -16
View File
@@ -82,7 +82,7 @@ static void test_rbtree(size_t n)
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long j = 0;
unsigned long j = rand_get();
if (kb_getp(kTree, tree, &j) != 0)
s+= j;
}
@@ -98,7 +98,7 @@ static void test_rbtree(size_t n)
KHASH_INIT(iun, unsigned long, unsigned long, 1, hash_func, hash_eq)
static void
test_dict(unsigned long n)
test_dict(size_t n)
{
khash_t(iun) *dict = kh_init(iun);
@@ -137,7 +137,7 @@ static inline size_t char_hash(const char_array_t a) {
KHASH_INIT(iub, char_array_t, char_array_t, 1, char_hash, char_equal_p)
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
khash_t(iub) *dict = kh_init(iub);
@@ -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("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("KLIB", numberof(table), table, argc, argv);
exit(0);
}
+42 -28
View File
@@ -5,6 +5,10 @@
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
// Needed due to compilation error in pool.h
#include <pthread.h>
#include "dynamic.h"
@@ -29,30 +33,37 @@ static void test_array(size_t n)
s += *(unsigned int*) vector_at(&a1, i) * *(unsigned int*) vector_at(&a2, i);
}
g_result = s;
vector_destruct(&a1);
vector_destruct(&a2);
vector_destruct(&a1, NULL);
vector_destruct(&a2, NULL);
}
/********************************************************************************************/
static size_t hash_func (map *m, void *a)
static size_t hash_func (void *a)
{
return hash_data(a, sizeof (unsigned long));
}
static int equal_func(map *m, void *a, void *b)
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;
}
static void set_func(map *m, 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;
@@ -63,13 +74,13 @@ struct map_element
};
static void
test_dict(unsigned long n)
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;
@@ -86,24 +97,30 @@ test_dict(unsigned long n)
typedef char char_array_t[256];
static int char_equal (map *m, void *a, void *b)
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;
}
static size_t char_hash(map *m, void *a)
static size_t char_hash(void *a)
{
char_array_t *pa = (char_array_t *)a;
return hash_string(*pa);
}
static void char_set(map *m, void *a, void *b)
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;
@@ -114,19 +131,16 @@ struct big_map_element
};
static void
test_dict_big(unsigned long n)
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;
@@ -143,14 +157,14 @@ test_dict_big(unsigned long 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);
}
+10 -8
View File
@@ -14,8 +14,8 @@
static void test_array(size_t n)
{
sv_t *a1 = sv_alloc ( sizeof (unsigned int), 16, NULL);
sv_t *a2 = sv_alloc ( sizeof (unsigned int), 16, NULL);
srt_vector *a1 = sv_alloc ( sizeof (unsigned int), 16, NULL);
srt_vector *a2 = sv_alloc ( sizeof (unsigned int), 16, NULL);
for(size_t i = 0; i < n; i++) {
unsigned int value = rand_get();
@@ -48,7 +48,7 @@ static int cmp_float (const void *a, const void *b)
static void test_sort(size_t n)
{
sv_t *a1 = sv_alloc(sizeof(float), 16, cmp_float);
srt_vector *a1 = sv_alloc(sizeof(float), 16, cmp_float);
for(size_t i = 0; i < n; i++) {
unsigned int value = rand_get();
sv_push(&a1, &value );
@@ -60,13 +60,15 @@ 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[])
{
int n = (argc > 1) ? atoi(argv[1]) : 0;
if (n == 20)
test_function("Array time", 100000000, test_array);
if (n == 50)
test_function("Sort time", 10000000, test_sort);
test("LIBSRT", numberof(table), table, argc, argv);
exit(0);
}
+446 -82
View File
@@ -7,12 +7,15 @@
#include "m-array.h"
#include "m-list.h"
#include "m-rbtree.h"
#include "m-bptree.h"
#include "m-dict.h"
#include "m-algo.h"
#include "m-mempool.h"
#include "m-string.h"
#include "m-buffer.h"
#include "m-mutex.h"
#include "m-deque.h"
#include "m-concurrent.h"
#include "common.h"
@@ -68,6 +71,37 @@ static void test_list (size_t n)
/********************************************************************************************/
// Two benched mode: with or without mempool
#ifdef USE_MEMPOOL
LIST_DUAL_PUSH_DEF(dlist_uint, unsigned int, (MEMPOOL( dlist_mpool), MEMPOOL_LINKAGE(static)))
#else
LIST_DUAL_PUSH_DEF(dlist_uint, unsigned int)
#endif
static void test_dlist (size_t n)
{
#ifdef USE_MEMPOOL
dlist_uint_mempool_init(dlist_mpool);
#endif
M_LET(a1, a2, LIST_OPLIST(dlist_uint)) {
for(size_t i = 0; i < n; i++) {
dlist_uint_push_back(a1, rand_get() );
dlist_uint_push_back(a2, rand_get() );
}
unsigned int s = 0;
dlist_uint_it_t it1, it2;
for(dlist_uint_it(it1, a1), dlist_uint_it(it2,a2); !dlist_uint_end_p(it1); dlist_uint_next(it1), dlist_uint_next(it2)) {
s += *dlist_uint_cref(it1) * *dlist_uint_cref(it2);
}
g_result = s;
}
#ifdef USE_MEMPOOL
dlist_uint_mempool_clear(dlist_mpool);
#endif
}
/********************************************************************************************/
#ifdef USE_MEMPOOL
RBTREE_DEF(rbtree_ulong, unsigned long, (MEMPOOL( rbtree_mpool), MEMPOOL_LINKAGE(static)))
#else
@@ -99,6 +133,27 @@ static void test_rbtree(size_t n)
/********************************************************************************************/
BPTREE_DEF(bptree_ulong, 21, unsigned long, M_DEFAULT_OPLIST)
static void test_bptree(size_t n)
{
M_LET(tree, BPTREE_OPLIST(bptree_ulong)) {
for (size_t i = 0; i < n; i++) {
bptree_ulong_push(tree, rand_get());
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long *p = bptree_ulong_get(tree, rand_get());
if (p)
s += *p;
}
g_result = s;
}
}
/********************************************************************************************/
#ifdef USE_MEMPOOL
DICT_DEF2(dict_ulong, unsigned long, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(dict_mpool), MEMPOOL_LINKAGE(static)), unsigned long, M_DEFAULT_OPLIST)
#else
@@ -106,15 +161,16 @@ DICT_DEF2(dict_ulong, unsigned long, M_DEFAULT_OPLIST, unsigned long, M_DEFAULT_
#endif
static void
test_dict(unsigned long n)
test_dict(size_t n)
{
#ifdef USE_MEMPOOL
dict_ulong_list_pair_mempool_init(dict_mpool);
#endif
M_LET(dict, DICT_OPLIST(dict_ulong)) {
for (size_t i = 0; i < n; i++) {
unsigned long value = rand_get();
unsigned long key = rand_get();
dict_ulong_set_at(dict, key, rand_get() );
dict_ulong_set_at(dict, key, value);
}
rand_init();
unsigned int s = 0;
@@ -147,7 +203,7 @@ DICT_OA_DEF2(dict_oa_ulong,
unsigned long, M_DEFAULT_OPLIST)
static void
test_dict_oa(unsigned long n)
test_dict_oa(size_t n)
{
M_LET(dict, DICT_OPLIST(dict_oa_ulong)) {
for (size_t i = 0; i < n; i++) {
@@ -164,26 +220,46 @@ test_dict_oa(unsigned long 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)); }
// NOTE: Can't use the name OPLIST as a macro!
#define CHAR_OPLIST (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_DEF2(dict_char, char_array_t, M_OPEXTEND(CHAR_OPLIST,MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, CHAR_OPLIST)
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())
#else
DICT_DEF2(dict_char, char_array_t, CHAR_OPLIST, char_array_t, CHAR_OPLIST)
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(unsigned long n)
test_dict_big(size_t n)
{
#ifdef USE_MEMPOOL
dict_char_list_pair_mempool_init(dict_mpool2);
@@ -211,6 +287,31 @@ test_dict_big(unsigned long 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
@@ -220,7 +321,7 @@ DICT_DEF2(dict_str, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
#endif
static void
test_dict_str(unsigned long n)
test_dict_str(size_t n)
{
#ifdef USE_MEMPOOL
dict_str_list_pair_mempool_init(dict_mpool3);
@@ -263,75 +364,89 @@ static void test_sort(size_t n)
}
}
static void test_stable_sort(size_t n)
{
M_LET(a1, ARRAY_OPLIST(array_float)) {
for(size_t i = 0; i < n; i++) {
array_float_push_back(a1, rand_get() );
}
array_float_special_stable_sort(a1);
g_result = *array_float_get(a1, 0);
}
}
/********************************************************************************************/
#define SIZE_LIMIT (UINT_MAX/2)
BUFFER_DEF(buffer_uint, unsigned int, 0, BUFFER_QUEUE|BUFFER_BLOCKING)
buffer_uint_t g_buff;
buffer_uint_t g_buff_lock;
BUFFER_DEF(buffer_ull, unsigned long long, 0, BUFFER_QUEUE|BUFFER_BLOCKING)
buffer_ull_t g_final;
buffer_ull_t g_final_lock;
static void final(void *arg)
static void final_lock(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++) {
buffer_ull_pop(&j, g_final);
buffer_ull_pop(&j, g_final_lock);
s += j;
}
g_result = s;
}
static void conso(void *arg)
static void conso_lock(void *arg)
{
unsigned int j;
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
buffer_uint_pop(&j, g_buff);
buffer_uint_pop(&j, g_buff_lock);
s += j;
}
buffer_ull_push(g_final, s);
buffer_ull_push(g_final_lock, s);
}
static void prod(void *arg)
static void prod_lock(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
buffer_uint_push(g_buff, r );
buffer_uint_push(g_buff_lock, r );
r = r * 31421U + 6927U;
}
}
static void test_buffer(size_t n)
{
const int cpu_count = get_cpu_count();
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
const int prod_count = cpu_count/2;
const int conso_count = cpu_count - prod_count;
if (cpu_count < 2) {
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
return;
}
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
// Init
buffer_uint_init(g_buff, 64*cpu_count);
buffer_ull_init (g_final, 64*cpu_count);
buffer_uint_init(g_buff_lock, 64*cpu_count);
buffer_ull_init (g_final_lock, 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, &n);
m_thread_create (idx_p[i], prod_lock, &n);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso, &n);
m_thread_create (idx_c[i], conso_lock, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final, &n2);
m_thread_create(idx_final, final_lock, &n2);
// Wait for jobs to be done.
for(int i = 0; i < prod_count; i++) {
@@ -343,79 +458,80 @@ static void test_buffer(size_t n)
m_thread_join(idx_final);
// Clear & quit
buffer_ull_clear(g_final);
buffer_uint_clear(g_buff);
buffer_ull_clear(g_final_lock);
buffer_uint_clear(g_buff_lock);
}
/********************************************************************************************/
QUEUE_MPMC_DEF(queue_uint, unsigned int, BUFFER_QUEUE)
queue_uint_t g_buff2;
queue_uint_t g_buff_mpmc;
QUEUE_MPMC_DEF(queue_ull, unsigned long long, BUFFER_QUEUE)
queue_ull_t g_final2;
queue_ull_t g_final_mpmc;
static void final2(void *arg)
static void final_mpmc(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_ull_pop(&j, g_final2));
while (!queue_ull_pop(&j, g_final_mpmc)) m_thread_yield();
s += j;
}
g_result = s;
}
static void conso2(void *arg)
static void conso_mpmc(void *arg)
{
unsigned int j;
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
while (!queue_uint_pop(&j, g_buff2));
while (!queue_uint_pop(&j, g_buff_mpmc)) m_thread_yield();
s += j;
}
while (!queue_ull_push(g_final2, s));
while (!queue_ull_push(g_final_mpmc, s));
}
static void prod2(void *arg)
static void prod_mpmc(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!queue_uint_push(g_buff2, r ));
while (!queue_uint_push(g_buff_mpmc, r )) m_thread_yield();
r = r * 31421U + 6927U;
}
}
static void test_queue(size_t n)
{
const int cpu_count = get_cpu_count();
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
const int prod_count = cpu_count/2;
const int conso_count = cpu_count - prod_count;
if (cpu_count < 2) {
fprintf(stderr, "WARNING: Can not measure Queue performance.\n");
return;
}
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
// Init
queue_uint_init(g_buff2, 64*cpu_count);
queue_ull_init (g_final2, 64*cpu_count);
queue_uint_init(g_buff_mpmc, 64*cpu_count);
queue_ull_init (g_final_mpmc, 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], prod2, &n);
m_thread_create (idx_p[i], prod_mpmc, &n);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso2, &n);
m_thread_create (idx_c[i], conso_mpmc, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final2, &n2);
m_thread_create(idx_final, final_mpmc, &n2);
// Wait for jobs to be done.
for(int i = 0; i < prod_count; i++) {
@@ -427,8 +543,266 @@ static void test_queue(size_t n)
m_thread_join(idx_final);
// Clear & quit
queue_ull_clear(g_final2);
queue_uint_clear(g_buff2);
queue_ull_clear(g_final_mpmc);
queue_uint_clear(g_buff_mpmc);
}
/********************************************************************************************/
QUEUE_SPSC_DEF(queue_single_uint, unsigned int, BUFFER_QUEUE)
queue_single_uint_t g_buff_spsc;
QUEUE_SPSC_DEF(queue_single_ull, unsigned long long, BUFFER_QUEUE)
queue_single_ull_t g_final_spsc;
static void final_spsc(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(void *arg)
{
unsigned int j;
size_t *p_n = 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)) m_thread_yield();
s += j;
}
while (!queue_single_ull_push(g_final_spsc, s));
}
static void prod_spsc(void *arg)
{
size_t *p_n = 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 )) m_thread_yield();
r = r * 31421U + 6927U;
}
}
static void test_queue_single(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, &n);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso_spsc, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final_spsc, &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);
}
/********************************************************************************************/
#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)))
DEQUE_DEF(deque_ull, unsigned long long)
CONCURRENT_DEF(cdeque_ull, deque_ull_t, M_OPEXTEND(DEQUE_OPLIST(deque_ull, M_DEFAULT_OPLIST), PUSH(deque_ull_push_front)))
cdeque_uint_t g_buff_conc;
cdeque_ull_t g_final_conc;
static void final_conc(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++) {
cdeque_ull_pop_blocking(&j, g_final_conc, true);
s += j;
}
g_result = s;
}
static void conso_conc(void *arg)
{
unsigned int j;
size_t *p_n = arg;
size_t n = *p_n;
unsigned long long s = 0;
for(int i = 0; i < n;i++) {
cdeque_uint_pop_blocking(&j, g_buff_conc, true);
s += j;
}
cdeque_ull_push(g_final_conc, s);
}
static void prod_conc(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
cdeque_uint_push(g_buff_conc, r );
r = r * 31421U + 6927U;
}
}
static void test_queue_concurrent(size_t n)
{
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
const int prod_count = cpu_count/2;
const int conso_count = cpu_count - prod_count;
if (cpu_count < 2) {
fprintf(stderr, "WARNING: Can not measure CONCURRENT performance.\n");
return;
}
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
// Init
cdeque_uint_init(g_buff_conc);
cdeque_ull_init (g_final_conc);
// 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_conc, &n);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso_conc, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final_conc, &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
cdeque_ull_clear(g_final_conc);
cdeque_uint_clear(g_buff_conc);
}
/********************************************************************************************/
@@ -465,43 +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 == 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 == 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 == 60)
test_function("Buffer time", 1000000, test_buffer);
if (n == 61)
test_function("Queue MPMC time", 1000000, test_queue);
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);
}
+99
View File
@@ -0,0 +1,99 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "nedtrie.h"
#include "common.h"
/********************************************************************************************/
typedef struct foo_s {
NEDTRIE_ENTRY(foo_s) link;
unsigned long key;
unsigned long value;
} foo_t;
typedef struct foo_tree_s foo_tree_t;
NEDTRIE_HEAD(foo_tree_s, foo_s);
static inline size_t fookeyfunct(const foo_t *r)
{
return r->key;
}
NEDTRIE_GENERATE(static, foo_tree_s, foo_s, link, fookeyfunct, NEDTRIE_NOBBLEZEROS(foo_tree_s))
static void
test_dict(size_t n)
{
foo_tree_t footree;
NEDTRIE_INIT(&footree);
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
unsigned long val = rand_get();
foo_t * p = malloc(sizeof(foo_t));
if (p == NULL) abort();
p->key = key;
p->value = val;
NEDTRIE_INSERT(foo_tree_s, &footree, p);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
foo_t key = { .key = rand_get() };
foo_t *p = NEDTRIE_FIND(foo_tree_s, &footree, &key);
if (p)
s += p->value;
}
g_result = s;
foo_t *r, *next;
NEDTRIE_FOREACH_SAFE(r, foo_tree_s, &footree, next) {
// Remove is needed, otherwise it segfault
NEDTRIE_REMOVE(foo_tree_s, &footree, r);
free(r);
}
}
static void
test_dict_noalloc(size_t n)
{
foo_tree_t footree;
NEDTRIE_INIT(&footree);
foo_t *tab = malloc(n*sizeof(foo_t));
if (tab == NULL) abort();
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
unsigned long val = rand_get();
tab[i].key = key;
tab[i].value = val;
NEDTRIE_INSERT(foo_tree_s, &footree, &tab[i]);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
foo_t key = { .key = rand_get() };
foo_t *p = NEDTRIE_FIND(foo_tree_s, &footree, &key);
if (p)
s += p->value;
}
g_result = s;
free(tab);
}
/********************************************************************************************/
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[])
{
test("NEDTRIES", numberof(table), table, argc, argv);
exit(0);
}
+10 -7
View File
@@ -1,4 +1,5 @@
#define NDEBUG
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
@@ -97,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);
}
+14 -18
View File
@@ -72,7 +72,7 @@ static void test_rbtree(size_t n)
/********************************************************************************************/
static void
test_dict1(unsigned long n)
test_dict1(size_t n)
{
QMap<unsigned long, unsigned long> dict;
@@ -92,7 +92,7 @@ test_dict1(unsigned long n)
/********************************************************************************************/
static void
test_dict2(unsigned long n)
test_dict2(size_t n)
{
QHash<unsigned long, unsigned long> dict;
@@ -126,7 +126,7 @@ uint qHash(const char_array_s &k)
};
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
QHash<char_array_s, char_array_s> dict;
@@ -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);
}
+10 -10
View File
@@ -25,7 +25,7 @@ using namespace std;
/********************************************************************************************/
static void
test_dict2(unsigned long n)
test_dict2(size_t n)
{
sparse_hash_map<unsigned long, unsigned long> dict;
@@ -63,7 +63,7 @@ namespace std {
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
sparse_hash_map<char_array_s, char_array_s> dict;
@@ -87,7 +87,7 @@ test_dict_big(unsigned long n)
/********************************************************************************************/
static void
test_dict_str(unsigned long n)
test_dict_str(size_t n)
{
sparse_hash_map<string, string> dict;
@@ -109,14 +109,14 @@ test_dict_str(unsigned long 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);
}
+54 -30
View File
@@ -73,7 +73,7 @@ static void test_rbtree(size_t n)
/********************************************************************************************/
static void
test_dict1(unsigned long n)
test_dict1(size_t n)
{
map<unsigned long, unsigned long> dict;
@@ -93,7 +93,7 @@ test_dict1(unsigned long n)
/********************************************************************************************/
static void
test_dict2(unsigned long n)
test_dict2(size_t n)
{
unordered_map<unsigned long, unsigned long> dict;
@@ -110,6 +110,24 @@ test_dict2(unsigned long 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 {
@@ -131,7 +149,7 @@ namespace std {
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
unordered_map<char_array_s, char_array_s> dict;
@@ -155,19 +173,19 @@ test_dict_big(unsigned long n)
/********************************************************************************************/
static void
test_dict_str(unsigned long n)
test_dict_str(size_t n)
{
unordered_map<string, string> dict;
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();
string s1 = to_string(rand_get());
string s2 = to_string(rand_get());
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();
string s1 = to_string (rand_get());
unordered_map<string, string>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
@@ -176,19 +194,19 @@ test_dict_str(unsigned long n)
}
static void
test_dict_str2(unsigned long n)
test_dict_str2(size_t n)
{
map<string, string> dict;
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();
string s1 = to_string(rand_get());
string s2 = to_string(rand_get());
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();
string s1 = to_string(rand_get());
map<string, string>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
@@ -208,28 +226,34 @@ static void test_sort(size_t n)
g_result = a1[0];
}
static void test_stable_sort(size_t n)
{
vector<float> a1;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
}
stable_sort(a1.begin(), a1.end());
g_result = a1[0];
}
/********************************************************************************************/
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);
test("STL", numberof(table), table, argc, argv);
exit(0);
}
+338
View File
@@ -0,0 +1,338 @@
/*
* M*LIB - String Bench
*
* Copyright (c) 2017-2020, 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;
}
+741 -168
View File
File diff suppressed because it is too large. Load diff
+11 -13
View File
@@ -124,7 +124,7 @@ static int equal_func(const void* arg, const void *obj_p)
}
static void
test_dict(unsigned long n)
test_dict(size_t n)
{
tommy_hashlin dict;
tommy_hashlin_init(&dict);
@@ -174,7 +174,7 @@ static size_t char_hash(const char *s)
}
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
tommy_hashlin dict;
tommy_hashlin_init(&dict);
@@ -206,18 +206,16 @@ test_dict_big(unsigned long 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);
}
+12 -14
View File
@@ -1,4 +1,5 @@
#define NDEBUG
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
@@ -85,7 +86,7 @@ struct hash_i_node_s {
};
static void
test_dict(unsigned long n)
test_dict(size_t n)
{
struct hash_i_node_s *dict = NULL;
@@ -131,7 +132,7 @@ struct hash_b_node_s {
};
static void
test_dict_big(unsigned long n)
test_dict_big(size_t n)
{
struct hash_b_node_s *dict = NULL;
@@ -199,19 +200,16 @@ 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},
{ 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);
}
+42
View File
@@ -0,0 +1,42 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "xxhash.h"
#include "common.h"
static unsigned long *g_p;
static void test_hash_prepare(size_t n)
{
g_p = malloc (n * sizeof(unsigned long));
if (g_p == NULL) abort();
for(size_t i = 0; i < n ; i++)
g_p[i] = ((rand_get()*RAND_MAX + rand_get())*RAND_MAX+rand_get())*RAND_MAX + rand_get();
}
static void test_hash_final(void)
{
free(g_p);
}
static void test_hash(size_t n)
{
g_result = XXH64(g_p, sizeof(unsigned long)*n, 0);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 70,"XXHASH64", 100000000, test_hash_prepare, test_hash, test_hash_final}
};
int main(int argc, const char *argv[])
{
test("XXHASH", numberof(table), table, argc, argv);
exit(0);
}
+304
View File
@@ -0,0 +1,304 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2020, 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"
// Maximum supported value for repeat argument
#define MAX_REPEAT 10000
// 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;
double within;
unsigned repeat;
bool graph, best, best_within, 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->best_within = false;
opt->average = false;
opt->quiet = false;
opt->repeat = 1;
opt->within = .05;
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);
opt->repeat = (opt->repeat > MAX_REPEAT) ? MAX_REPEAT : opt->repeat;
} else if (strcmp(argv[i], "--graph") == 0) {
opt->graph = true;
} else if (strcmp(argv[i], "--best") == 0) {
opt->best = true;
opt->average = false;
opt->best_within = false;
} else if (strcmp(argv[i], "--best-within") == 0) {
opt->best_within = true;
opt->best = false;
opt->average = false;
i++;
opt->within = strtod(argv[i], &end) / 100.0;
} else if (strcmp(argv[i], "--average") == 0) {
opt->average = true;
opt->best = false;
opt->best_within = 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)] [--grapĥ]\n"
"[--repeat number] [--best|--average|--best-within] [--quiet]\n");
exit(-1);
}
// Utility function for qsort
static int cmp_double(const void *pa, const void *pb)
{
const double *a = (const double *) pa, *b = (const double *)pb;
return (*a < *b) ? -1 : *a > *b;
}
/* Get the longest sequence of values within 'f'% */
static int
get_sequence(int i, int n, double tab[], double f)
{
int j;
for(j = i + 1; j < n; j++) {
if (tab[j] > f * tab[i])
break;
}
return j-i;
}
/* Return the minimum of the longest sequence of values with 'f'% */
static double
get_best_within(int n, double tab[], double f)
{
qsort(tab, n, sizeof(double), cmp_double);
int best_i = 0;
int best_seq = get_sequence(0, n, tab, f);
for(int i = 1; i < n && (n-i) > best_seq ; i++) {
int seq = get_sequence(i, n, tab, f);
if (seq > best_seq) {
best_i = i;
best_seq = seq;
}
}
return tab[best_i];
}
// Integer sqrt function (to avoid depending on libm)
static unsigned integer_sqrt(unsigned n)
{
// Find greatest shift.
int shift = 2;
unsigned nShifted = n >> shift;
// We check for nShifted being n, since some implementations
// perform shift operations modulo the word size.
while (nShifted != 0 && nShifted != n){
shift += 2;
nShifted = n >> shift;
}
shift -= 2;
// Find digits of result.
unsigned result = 0;
while (shift >= 0) {
result = result << 1;
unsigned candidateResult = result + 1;
if (candidateResult*candidateResult <= n >> shift) {
result = candidateResult;
}
shift -= 2;
}
return result;
}
void
test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[])
{
// Init & parse arguments
// 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.to == 0) ? functions[i].default_n : arg.to;
// Open plot file if needed
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;
double variance = 0.0;
static double measure[MAX_REPEAT];
double ba = 0.0;
if (functions[i].init != NULL)
functions[i].init(n);
// Measure the time of the test_function
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;
variance += t0 * t0;
measure[r] = t0;
}
// Closure of the tests
if (functions[i].clear != NULL)
functions[i].clear();
// Finish computing the different times
if (arg.repeat > 1) {
avg /= arg.repeat;
variance = (variance - arg.repeat * avg * avg) / (arg.repeat - 1);
ba = get_best_within(arg.repeat, measure, 1. + arg.within);
}
// The average value shall be around best_within (and greater)
// otherwise the result doesn't seem reliable.
bool reliable = (ba <= avg && ba * (1.0+arg.within/2) + 0.5 > avg);
// Print the result
if (arg.graph) {
fprintf(graph_file, "%f %f\n", n, arg.average ? avg : arg.best ? best : ba);
} else if (arg.quiet) {
printf("%ld\n", (long)(arg.average ? avg : arg.best ? best : reliable ? ba : -1));
} else if (arg.repeat > 1) {
if (arg.average == false && arg.best_within == 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 && arg.best_within == false)
printf ("%20.20s time %lu ms +/- %u ms for n = %lu *** AVERAGE ***\n", functions[i].funcname, (unsigned long) avg, 2*integer_sqrt(variance), (unsigned long) n);
if (arg.best == false && arg.average == false)
printf ("%20.20s time %lu ms for n = %lu *** BEST within %d%% *** %s\n", functions[i].funcname, (unsigned long) ba, (unsigned long) n, (int)(100*arg.within), reliable ? "" : "(unreliable result)");
}
}
// Close the FILE if needed
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, Patrick Pelissier
* Copyright (c) 2017-2020, 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, Patrick Pelissier
* Copyright (c) 2017-2020, 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);
+22
View File
@@ -0,0 +1,22 @@
CC=cc -std=c11
CXX=c++ -std=c++11
CFLAGS=-O2 -march=native -Wall
CPPFLAGS=-I../..
all: k-nucleotide
k-nucleotide: k-nucleotide.exe data.txt
./k-nucleotide.exe < data.txt
k-nucleotide.exe: nucleotide.c
$(CC) $(CFLAGS) $(CPPFLAGS) nucleotide.c -o k-nucleotide.exe -lpthread
data.txt: fasta.exe
./fasta.exe 25000000 > data.txt
fasta.exe: fasta.c
$(CC) $(CFLAGS) $(CPPFLAGS) -fopenmp fasta.c -o fasta.exe
clean:
rm -f *.exe *~ *.o data.txt
+344
View File
@@ -0,0 +1,344 @@
// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by Jeremy Zerfas
// rewritten by Аноним Легионов, inspired by fasta Rust #2 program
// use two OpenMP locks instead of one critical section
// decouples IO activity from random number generation
//
// modified by Josh Goldfoot, adding use of a buffer for fasta_repeat
// This controls the width of lines that are output by this program.
#define MAXIMUM_LINE_WIDTH 60
// This program will generate the random nucleotide sequences in parallel which
// are worked on in blocks of lines. The number of lines in those blocks is
// controlled by this setting.
#define LINES_PER_BLOCK 1024
#define CHARACTERS_PER_BLOCK (MAXIMUM_LINE_WIDTH*LINES_PER_BLOCK)
#define THREADS_TO_USE 4
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef _OPENMP
#include <omp.h>
#endif
// intptr_t should be the native integer type on most sane systems.
typedef intptr_t intnative_t;
typedef struct
{
char letter;
float probability;
} nucleotide_info;
// Repeatedly print string_To_Repeat until it has printed
// number_Of_Characters_To_Create. The output is also wrapped to
// MAXIMUM_LINE_WIDTH columns.
static char* make_Sequence_Buffer(const char string_To_Repeat[])
{
const intnative_t string_To_Repeat_Length = strlen(string_To_Repeat);
// JG: Changed to that this writes a sequence to a buffer, which is used
intnative_t number_Of_Characters_To_Create = string_To_Repeat_Length * MAXIMUM_LINE_WIDTH;
char* buffer = (char*) malloc(number_Of_Characters_To_Create +
number_Of_Characters_To_Create / MAXIMUM_LINE_WIDTH + 1);
if (buffer == NULL)
exit(-1);
char* bufferOffset = buffer;
// Create an extended_String_To_Repeat which is a copy of string_To_Repeat
// but extended with another copy of the first MAXIMUM_LINE_WIDTH characters
// of string_To_Repeat appended to the end. Later on this allows us to
// generate a line of output just by doing simple memory copies using an
// appropriate offset into extended_String_To_Repeat.
char extended_String_To_Repeat[string_To_Repeat_Length + MAXIMUM_LINE_WIDTH];
for (intnative_t column = 0; column<string_To_Repeat_Length + MAXIMUM_LINE_WIDTH;
column++)
extended_String_To_Repeat[column] =
string_To_Repeat[column%string_To_Repeat_Length];
intnative_t offset = 0;
char line[MAXIMUM_LINE_WIDTH + 1];
line[MAXIMUM_LINE_WIDTH] = '\n';
for (intnative_t current_Number_Of_Characters_To_Create =
number_Of_Characters_To_Create;
current_Number_Of_Characters_To_Create>0;)
{
// Figure out the length of the line we need to write. If it's less than
// MAXIMUM_LINE_WIDTH then we also need to add a line feed in the right
// spot too.
intnative_t line_Length = MAXIMUM_LINE_WIDTH;
if (current_Number_Of_Characters_To_Create<MAXIMUM_LINE_WIDTH)
{
line_Length = current_Number_Of_Characters_To_Create;
line[line_Length] = '\n';
}
memcpy(line, extended_String_To_Repeat + offset, line_Length);
// Update the offset, reducing it by string_To_Repeat_Length if
// necessary.
offset += line_Length;
if (offset>string_To_Repeat_Length)
offset -= string_To_Repeat_Length;
// "Output" that line to our buffer and update the
// current_Number_Of_Characters_To_Create.
memcpy(bufferOffset, line, line_Length + 1); // JG: used to be fwrite(line, line_Length + 1, 1, stdout);
bufferOffset += line_Length + 1;
current_Number_Of_Characters_To_Create -= line_Length;
}
*bufferOffset = 0;
return buffer;
}
void repeat_And_Wrap_String(const char string_To_Repeat[], intnative_t number_Of_Characters_To_Create)
{
/* JG: fasta_repeat repeats every len(alu) * line-length = 287 * 61 = 17507 characters.
So, calculate this once, then just print that buffer over and over. */
char* sequence = make_Sequence_Buffer(string_To_Repeat);
intnative_t sequenceLen = (intnative_t) strlen(sequence);
intnative_t outputBytes = number_Of_Characters_To_Create + number_Of_Characters_To_Create / 60;
while (outputBytes >= sequenceLen) {
fwrite(sequence, sequenceLen, 1, stdout);
outputBytes -= sequenceLen;
}
if (outputBytes > 0) {
fwrite(sequence, outputBytes, 1, stdout);
printf("\n");
}
free(sequence);
}
// Generate a pseudorandom number from 0 to max using a linear
// congruential generator.
#define IM 139968
#define IA 3877
#define IC 29573
uint32_t seed = 42;
int rng_tid; //Thread ID
int rng_tnum = 1; //Thread number
intnative_t rng_cnt = 0;
#ifdef _OPENMP
omp_lock_t rng_lock;
#define RNG_LOCK_INIT() omp_init_lock(&rng_lock)
#define RNG_LOCK() omp_set_lock(&rng_lock)
#define RNG_FREE() omp_unset_lock(&rng_lock)
#else
#define RNG_LOCK_INIT() do{}while(0)
#define RNG_LOCK() do{}while(0)
#define RNG_FREE() do{}while(0)
#endif
static void rng_init(void)
{
RNG_LOCK_INIT();
rng_tid = 0;
}
static intnative_t rng_gen_blk(uint32_t * buf, intnative_t len, int curr_tid)
{
intnative_t gen_cnt = -1;//Error by default
RNG_LOCK();
if (rng_tid == curr_tid)
{
if (++rng_tid >= rng_tnum)
{
rng_tid = 0;
}
gen_cnt = (len<rng_cnt) ? len : rng_cnt;
rng_cnt -= gen_cnt;
len = gen_cnt;
while (0 != len--)
{
seed = (seed*IA + IC) % IM;
*(buf++) = seed;//This is stupid actually!
}
}
RNG_FREE();
return gen_cnt;
}
int out_tid; //Thread ID
int out_tnum = 1; //Thread number
#ifdef _OPENMP
omp_lock_t out_lock;
#define OUT_LOCK_INIT() omp_init_lock(&out_lock)
#define OUT_LOCK() omp_set_lock(&out_lock)
#define OUT_FREE() omp_unset_lock(&out_lock)
#else
#define OUT_LOCK_INIT() do{}while(0)
#define OUT_LOCK() do{}while(0)
#define OUT_FREE() do{}while(0)
#endif
static void out_init(void)
{
OUT_LOCK_INIT();
rng_tid = 0;
}
static intnative_t out_write(char * buf, intnative_t len, int curr_tid)
{
intnative_t wr_cnt = -1;//Error by default
OUT_LOCK();
if (out_tid == curr_tid)
{
if (++out_tid >= out_tnum)
{
out_tid = 0;
}
wr_cnt = fwrite(buf, len, 1, stdout);
}
OUT_FREE();
return wr_cnt; //-1 - thread error, 0 - IO error, 1 - ОК
}
static void generate_And_Wrap_Pseudorandom_DNA_Sequence(
const nucleotide_info nucl_info[],
const intnative_t nucl_num,
const intnative_t char_num)
{
uint32_t cumul_p[nucl_num];
float cumul_acc = 0.0;
for (intnative_t i = 0; i<nucl_num; i++)
{
cumul_acc += nucl_info[i].probability;
cumul_p[i] = 1ul + (uint32_t)(cumul_acc*(float)IM); //Compensate rounding errors on test file
}
#ifdef _OPENMP
intnative_t tnum = omp_get_num_procs();
if (tnum>THREADS_TO_USE) tnum = THREADS_TO_USE;
omp_set_num_threads(tnum);
rng_tnum = tnum;
out_tnum = tnum;
#endif
rng_tid = 0;
out_tid = 0;
rng_cnt = char_num;
#pragma omp parallel
{
char block[CHARACTERS_PER_BLOCK + LINES_PER_BLOCK];
char * line;
uint32_t rnd[CHARACTERS_PER_BLOCK], r;
intnative_t cnt, col, prid, nid, ncnt;
int cur_tid;
#ifdef _OPENMP
cur_tid = omp_get_thread_num();
#else
cur_tid = 0;
#endif
while (1)
{
do
{
cnt = rng_gen_blk(rnd, CHARACTERS_PER_BLOCK, cur_tid);
} while (-1 == cnt);
if (0 == cnt)
{
break;//Work finished!
}
line = block;
for (col = 0, prid = 0; prid < cnt; prid++)
{
r = rnd[prid];
ncnt = 0;
for (nid = 0; nid < nucl_num; nid++)
{
if (cumul_p[nid] <= r)
{
ncnt++;
}
}
*line++ = nucl_info[ncnt].letter;
if (++col >= MAXIMUM_LINE_WIDTH)
{
col = 0;
*line++ = '\n';
}
}
//Check if we need to end the line
if (0 != col)
{
//Last iteration didn't end the line, so finish the job.
*line++ = '\n';
}
//Print results
do
{
cnt = out_write(block, line - block, cur_tid);
} while (-1 == cnt);
//Check fot IO error
if (0 == cnt)
{
exit(1);
}
}
}
}
int main(int argc, char ** argv)
{
const intnative_t n = atoi(argv[1]);
fputs(">ONE Homo sapiens alu\n", stdout);
const char homo_Sapiens_Alu[] =
"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTC"
"AGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCG"
"TGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGG"
"AGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
repeat_And_Wrap_String(homo_Sapiens_Alu, 2 * n);
rng_init();
out_init();
fputs(">TWO IUB ambiguity codes\n", stdout);
nucleotide_info iub_Nucleotides_Information[] =
{
{ 'a', 0.27 },{ 'c', 0.12 },{ 'g', 0.12 },{ 't', 0.27 },{ 'B', 0.02 },
{ 'D', 0.02 },{ 'H', 0.02 },{ 'K', 0.02 },{ 'M', 0.02 },{ 'N', 0.02 },
{ 'R', 0.02 },{ 'S', 0.02 },{ 'V', 0.02 },{ 'W', 0.02 },{ 'Y', 0.02 }
};
generate_And_Wrap_Pseudorandom_DNA_Sequence(iub_Nucleotides_Information,
sizeof(iub_Nucleotides_Information) / sizeof(nucleotide_info), 3 * n);
fputs(">THREE Homo sapiens frequency\n", stdout);
nucleotide_info homo_Sapien_Nucleotides_Information[] =
{
{ 'a', 0.3029549426680 },{ 'c', 0.1979883004921 },
{ 'g', 0.1975473066391 },{ 't', 0.3015094502008 }
};
generate_And_Wrap_Pseudorandom_DNA_Sequence(homo_Sapien_Nucleotides_Information,
sizeof(homo_Sapien_Nucleotides_Information) / sizeof(nucleotide_info), 5 * n);
return 0;
}
+267
View File
@@ -0,0 +1,267 @@
// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// Original version Contributed by Jeremy Zerfas
// M*LIB port contributed by Patrick Pélissier
#define NDEBUG
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "m-worker.h"
#include "m-dict.h"
#include "m-bptree.h"
// This controls the maximum length for each set of oligonucleotide frequencies
// and each oligonucleotide count output by this program.
#define MAXIMUM_OUTPUT_LENGTH 4096
// Number of outputs to display
#define MAXMUM_NUMBER_OUTPUT 7
/* Define the needed containers */
/* Define Open Addressing dictionary of uint64_t --> uint32_t. Need to define how to handle out of bound data. */
static inline bool my_oor_equal_p(uint64_t k, unsigned char n)
{
return k == ~(uint64_t) n;
}
static inline void my_oor_set(uint64_t *k, unsigned char n)
{
*k = ~(uint64_t)n;
}
static inline size_t my_hash(uint64_t key)
{
return ((key) ^ (key)>>7);
}
static inline void update_val(uint32_t *p, uint32_t v) { *p += v;}
DICT_OA_DEF2(dict_oligonucleotide,
uint64_t, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)),
uint32_t, M_OPEXTEND(M_DEFAULT_OPLIST, UPDATE(update_val M_IPTR)))
#define M_OPL_dict_oligonucleotide_t() DICT_OPLIST(dict_oligonucleotide, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)), M_DEFAULT_OPLIST)
// Define a B*TREE-21 of uint32_t --> uint64_t and register its oplist
#define TREE_CMP(a,b) ((a) > (b) ? -1 : (a) < (b)) /* reverse natural order */
BPTREE_DEF2(tree_dict_oligonucleotide, 21, uint32_t, M_OPEXTEND(M_DEFAULT_OPLIST, CMP(TREE_CMP)), uint64_t, M_DEFAULT_OPLIST)
#define M_OPL_tree_dict_oligonucleotide_t() BPTREE_OPLIST2(tree_dict_oligonucleotide, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
/* Define the data to analyse (a plain array) */
ARRAY_DEF(polynucleotide, unsigned char)
// Macro to convert a nucleotide character to a code. Note that upper and lower
// case ASCII letters only differ in the fifth bit from the right and we only
// need the three least significant bits to differentiate the letters 'A', 'C',
// 'G', and 'T'. Spaces in this array/string will never be used as long as
// characters other than 'A', 'C', 'G', and 'T' aren't used.
#define CODE_FOR_NUCLEOTIDE(nucleotide) (" \0 \1\3 \2"[nucleotide & 0x7])
// And one more macro to convert the codes back to nucleotide characters.
#define NUCLEOTIDE_FOR_CODE(code) ("ACGT"[code & 0x3])
static worker_t worker;
// Read from the given stream the data and fill in the array with the converted one.
static void polynucleotide_init_stream(polynucleotide_t p, FILE *stream)
{
char buffer[MAXIMUM_OUTPUT_LENGTH], c;
polynucleotide_init(p);
polynucleotide_reserve(p, MAXIMUM_OUTPUT_LENGTH);
// Find the start of the third polynucleotide.
while(fgets(buffer, sizeof(buffer), stream) && memcmp(">THREE", buffer, 6));
// Start reading and encoding the third polynucleotide.
while(fgets(buffer, sizeof(buffer), stream) && buffer[0]!='>'){
for(size_t i = 0; (c = buffer[i]) !='\0'; i++) {
if (c != '\n') {
polynucleotide_push_back(p, CODE_FOR_NUCLEOTIDE(c));
}
}
}
}
// This is the function which does 95% of the work.
/* NOTE: paralelisation is not done by cutting the input set in two.
This is not efficient as the size of the output table will be the same
as for the whole table. */
static void init_hash(dict_oligonucleotide_t hash_Table, const size_t size, const unsigned char tab[size], const size_t desiredLength, const size_t offset, const size_t skip)
{
uint64_t key = 0;
const uint64_t mask = (1ULL<<(2*desiredLength))-1;
assert (2*desiredLength < 64 && desiredLength < size);
assert ((1 == skip || 4 == skip) && offset < size);
// For the first several nucleotides we only need to append them to key in
// preparation for the insertion of complete oligonucleotides to hash.
for(size_t i = 0; i < desiredLength - 1 + offset; i++)
key = (key<<2) | tab[i];
key &= mask;
dict_oligonucleotide_t hash;
dict_oligonucleotide_init(hash);
// Add all the complete oligonucleotides of
// desiredLength to hash, updating their count
if (skip == 1) {
for(size_t i = desiredLength-1+offset ; i < size; i++) {
key = (key<<2) | tab[i+0];
key &= mask;
uint32_t *p = dict_oligonucleotide_get(hash, key);
if (p != NULL) {
(*p) ++;
} else {
dict_oligonucleotide_set_at(hash, key, 1);
}
}
} else { // skip = 4
for(size_t i = desiredLength-1+offset ; i < size; i+=4) {
for (size_t j = 0; j < 4; j++)
key = (key<<2) | tab[i+j];
key &= mask;
uint32_t *p = dict_oligonucleotide_get(hash, key);
if (p != NULL) {
(*p) ++;
} else {
dict_oligonucleotide_set_at(hash, key, 1);
}
}
}
dict_oligonucleotide_init_move(hash_Table, hash);
}
static void compute_hash(dict_oligonucleotide_t hash_Table, const polynucleotide_t p, const size_t desiredLength)
{
size_t size = polynucleotide_size(p);
unsigned char *tab = polynucleotide_get(p, 0);
if (desiredLength < 8) {
init_hash(hash_Table, size, tab, desiredLength, 0, 1);
return;
}
worker_sync_t block; // Define a synchronization block
worker_start(block, worker);
dict_oligonucleotide_t hash_Table3, *p_hash_Table3 = &hash_Table3;
dict_oligonucleotide_t hash_Table2, *p_hash_Table2 = &hash_Table2;
dict_oligonucleotide_t hash_Table1, *p_hash_Table1 = &hash_Table1;
WORKER_SPAWN(block, (p_hash_Table1, size, tab, desiredLength), {
init_hash(*p_hash_Table1, size, tab, desiredLength, 3, 4);
}, (/*no output*/));
WORKER_SPAWN(block, (p_hash_Table2, size, tab, desiredLength), {
init_hash(*p_hash_Table2, size, tab, desiredLength, 2, 4);
}, (/*no output*/));
WORKER_SPAWN(block, (p_hash_Table3, size, tab, desiredLength), {
init_hash(*p_hash_Table3, size, tab, desiredLength, 1, 4);
}, (/*no output*/));
init_hash(hash_Table, size, tab, desiredLength, 0, 4);
worker_sync(block);
// Merge all hashtables
dict_oligonucleotide_splice(hash_Table, hash_Table1);
dict_oligonucleotide_splice(hash_Table, hash_Table2);
dict_oligonucleotide_splice(hash_Table, hash_Table3);
// Clear extra hashtables
dict_oligonucleotide_clear(hash_Table3);
dict_oligonucleotide_clear(hash_Table2);
dict_oligonucleotide_clear(hash_Table1);
}
// Generate frequencies for all oligonucleotides in polynucleotide that are of
// desiredLength and then save it to output.
static void compute_freq(const polynucleotide_t p, const size_t desiredLength, char output[])
{
dict_oligonucleotide_t hash_Table;
compute_hash(hash_Table, p, desiredLength);
// Order key is the count frequency (inverse value & key)
tree_dict_oligonucleotide_t tree;
tree_dict_oligonucleotide_init(tree);
for M_EACH(item, hash_Table, dict_oligonucleotide_t) {
tree_dict_oligonucleotide_set_at (tree, item->value, item->key);
}
// Create Output
char oligonucleotide[64];
const double invSum = 100.0 / (double) (polynucleotide_size(p) - desiredLength + 1);
int output_Position = 0;
for M_EACH(item, tree, tree_dict_oligonucleotide_t) {
// Convert the key for the oligonucleotide to a string.
uint64_t freq = *item->value_ptr;
uint32_t count = *item->key_ptr;
for(size_t j = desiredLength-1; j < desiredLength; j--){
oligonucleotide[j]=NUCLEOTIDE_FOR_CODE(freq);
freq>>=2;
}
oligonucleotide[desiredLength]='\0';
// Output the frequency for oligonucleotide to output.
output_Position+=snprintf(output+output_Position, MAXIMUM_OUTPUT_LENGTH-output_Position,
"%s %.3f\n", oligonucleotide, invSum * count);
}
dict_oligonucleotide_clear(hash_Table);
tree_dict_oligonucleotide_clear(tree);
}
// Generate a count for the number of times oligonucleotide appears in
// polynucleotide and then save it to output.
static void compute_count(polynucleotide_t p, const char oligonucleotide[], char output[])
{
const size_t oligonucleotide_Length = strlen(oligonucleotide);
dict_oligonucleotide_t hash_Table;
compute_hash(hash_Table, p, oligonucleotide_Length);
// Generate the key for oligonucleotide.
uint64_t key=0;
for(size_t i=0; i<oligonucleotide_Length; i++)
key = (key<<2) | CODE_FOR_NUCLEOTIDE(oligonucleotide[i]);
// Output the count for oligonucleotide to output.
uint32_t *val = dict_oligonucleotide_get(hash_Table, key);
uintmax_t count= val== NULL ? 0 : *val;
snprintf(output, MAXIMUM_OUTPUT_LENGTH, "%ju\t%s", count, oligonucleotide);
dict_oligonucleotide_clear(hash_Table);
}
int main()
{
// Initialize the different worker threads by auto-detecting the number of cores of the system
worker_init(worker, -1, 0, NULL);
// Read the input
polynucleotide_t p, *ptr = &p;
polynucleotide_init_stream(p, stdin);
// Prepare outputs
char outputBuffer[MAXMUM_NUMBER_OUTPUT][MAXIMUM_OUTPUT_LENGTH];
char (*output)[MAXIMUM_OUTPUT_LENGTH] = outputBuffer;
// Do the following functions in parallel.
worker_sync_t block; // Define a synchronization block
worker_start(block, worker);
WORKER_SPAWN(block, (ptr, output), {
compute_count(*ptr, "GGTATTTTAATTTATAGT", output[6]);
}, (/*no output*/));
WORKER_SPAWN(block, (ptr, output), {
compute_count(*ptr, "GGTATTTTAATT", output[5]);
}, (/*no output*/));
WORKER_SPAWN(block, (ptr, output), {
compute_count(*ptr, "GGTATT", output[4]);
}, (/*no output*/));
WORKER_SPAWN(block, (ptr, output), {
compute_count(*ptr, "GGTA", output[3]);
}, (/*no output*/));
compute_count(*ptr, "GGT", output[2]);
compute_freq(*ptr, 2, output[1]);
compute_freq(p, 1, output[0]);
worker_sync(block);
// Output the results to stdout.
for(int i=0; i<MAXMUM_NUMBER_OUTPUT; i++)
printf("%s\n", outputBuffer[i]);
polynucleotide_clear(p);
worker_clear(worker);
return 0;
}
+18 -5
View File
@@ -51,9 +51,13 @@
#include <iostream>
#include <string>
#ifdef USE_BOOST
#include <boost/pool/object_pool.hpp>
#endif
#ifdef USE_GLIB
#include <glib.h>
#endif
static const size_t PAGE_SIZE = 4096;
@@ -115,6 +119,8 @@ benchmark(std::string &&desc, std::function<void ()> cb)
* Boost::pool
* ------------------------------------------------------------------------
*/
#ifdef USE_BOOST
template<class T>
void
benchmark_boost_pool()
@@ -166,6 +172,7 @@ benchmark_boost_pool_create_and_destroy()
}
});
}
#endif
/*
* ------------------------------------------------------------------------
@@ -220,6 +227,7 @@ benchmark_mallocfree_free()
* GLib slices
* ------------------------------------------------------------------------
*/
#ifdef USE_GLIB
template<class T>
void
benchmark_glib_slices()
@@ -262,6 +270,7 @@ benchmark_glib_slices_free()
}
});
}
#endif
/*
* ------------------------------------------------------------------------
@@ -798,7 +807,7 @@ benchmark_mempool_small()
mempool_small_init (p);
mempool_small_t *ptr = &p; // C++ lambda !
benchmark(std::move(Small() + "mempool"), [=]() {
benchmark(std::move(Small() + "M*LIB mempool"), [=]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(Small); ++i) {
Small *o = mempool_small_alloc(*ptr);
touch_obj(o);
@@ -815,7 +824,7 @@ benchmark_mempool_big()
mempool_big_init (p);
mempool_big_t *ptr = &p; // C++ lambda !
benchmark(std::move(Big() + "mempool"), [=]() {
benchmark(std::move(Big() + "M*LIB mempool"), [=]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(Big); ++i) {
Big *o = mempool_big_alloc(*ptr);
touch_obj(o);
@@ -836,7 +845,7 @@ benchmark_mempool_mix_free()
mempool_big_init (b);
mempool_huge_init (h);
benchmark(std::move(std::string("mempool w/ free (Mix)")), [=]() {
benchmark(std::move(std::string("M*LIB mempool w/ free (Mix)")), [=]() {
for (size_t i = 0; i < N; ++i) {
if (__builtin_expect(!(i & 0xfff), 0)) {
Huge *o = mempool_huge_alloc (*ptr_h);
@@ -892,19 +901,23 @@ main()
benchmark_mallocfree_free<Big>();
std::cout << std::endl;
#ifdef USE_GLIB
benchmark_glib_slices<Small>();
benchmark_glib_slices_free<Small>();
benchmark_glib_slices<Big>();
benchmark_glib_slices_free<Big>();
std::cout << std::endl;
#endif
#ifdef USE_BOOST
benchmark_boost_pool<Small>();
benchmark_boost_pool_free<Small>();
benchmark_boost_pool<Big>();
benchmark_boost_pool_free<Big>();
benchmark_boost_pool_create_and_destroy();
std::cout << std::endl;
#endif
benchmark_ngx_pool<Small>();
benchmark_ngx_pool<Big>();
benchmark_ngx_pool_mix_free();
+53
View File
@@ -0,0 +1,53 @@
CC= gcc
CXX= g++
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
CXXFLAGS= $(CFLAGS)
LDFLAGS=
OBJS=
LIBS= -lm
all: run
benchmarks:
git clone https://github.com/attractivechaos/benchmarks.git
build: benchmarks
mkdir -p benchmarks/udb/mlib
cp test-mlib.c benchmarks/udb/mlib/test.c
cp Makefile-mlib benchmarks/udb/mlib/Makefile
mkdir -p benchmarks/udb/mlib-oa
cp test-mlib-oa.c benchmarks/udb/mlib-oa/test.c
cp Makefile-mlib benchmarks/udb/mlib-oa/Makefile
touch build
all-recur: build
cd benchmarks/udb; \
wdir=`pwd`; \
list_c=`find . -name 'test.c'`; \
list_cc=`find . -name 'test.cc'`; \
for t in $$list_c $$list_cc ; do \
subdir=`dirname $$t`; \
cd $$subdir; \
$(MAKE) CC="$(CC)" CXX="$(CXX)" DFLAGS="$(DFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" || exit 1; \
cd $$wdir; \
done;
run: all-recur
@cd benchmarks/udb; \
echo ; echo "LIBRARY | INT TIME | STR TIME" ; \
for i in `find . -name 'test'` ; do \
printf "%s|" `dirname $$i|sed -e 's+./++g'`; \
$$i -is | awk ' \
/\[int data\] # element/ { nbInt= $$5 } \
/\[int data\] CPU time/ {timeInt= $$5 } \
/\[str data\] # element/ { nbStr= $$5 } \
/\[str data\] CPU time/ {timeStr= $$5 } \
END { \
if (nbInt != 624444) timeInt="BRK" ; \
if (nbStr != 624444) timeStr="BRK" ; \
print timeInt,"|" ,timeStr \
}' ; \
done
clean:
rm -rf benchmarks build
+22
View File
@@ -0,0 +1,22 @@
CC= gcc
CFLAGS= -g -Wall -O2 -march=native
OBJS= ../benchmark/benchmark.o ../runit/runlib.o test.o
DFLAGS=
INCLUDES= -I../benchmark -I../runit -I../../../../..
LIBS=
.SUFFIXES:.c .o
.c.o:
$(CC) -std=gnu11 -DNDEBUG -c $(INCLUDES) $(CFLAGS) $(DFLAGS) $< -o $@
all:test
test:$(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
cleanlocal:
rm -f *.o a.out *~ test
clean:cleanlocal
+73
View File
@@ -0,0 +1,73 @@
#include <string.h>
#include "benchmark.h"
#include "m-dict.h"
#include "m-mempool.h"
static inline bool oor_equal_p(int k, unsigned char n)
{
return k == (int)-n - 1;
}
static inline void oor_set(int *k, unsigned char n)
{
*k = (int)-n - 1;
}
DICT_OA_DEF2(dict_int,
int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
int, M_DEFAULT_OPLIST)
static const char *const oor[2] = { "EMPTY", "DELETED" };
static inline bool str_oor_equal_p(const char *k, unsigned char n)
{
return k == oor[n];
}
static inline void str_oor_set(const char * *k, unsigned char n)
{
*k = oor[n];
}
DICT_OA_DEF2(dict_str, const char *,
M_OPEXTEND (M_CSTR_OPLIST, OOR_EQUAL(str_oor_equal_p), OOR_SET(str_oor_set M_IPTR)),
int, M_DEFAULT_OPLIST)
int test_int(int N, const unsigned *data)
{
int ret;
M_LET(h, DICT_OPLIST(dict_int)) {
for (int i = 0; i < N; ++i) {
if (dict_int_get (h, data[i]) == NULL) {
dict_int_set_at (h, data[i], i);
} else {
dict_int_erase(h, data[i]);
}
}
ret = (int)dict_int_size(h);
}
return ret;
}
int test_str(int N, char * const *data)
{
int ret;
M_LET(h, DICT_OPLIST(dict_str)) {
for (int i = 0; i < N; ++i) {
if (dict_str_get (h, data[i]) == NULL) {
dict_str_set_at (h, data[i], i);
} else {
dict_str_erase(h, data[i]);
}
}
ret = (int)dict_str_size(h);
}
return ret;
}
int main(int argc, char *argv[])
{
return udb_benchmark(argc, argv, test_int, test_str);
}
+53
View File
@@ -0,0 +1,53 @@
#include <string.h>
#include "benchmark.h"
#include "m-dict.h"
#include "m-mempool.h"
DICT_STOREHASH_DEF2(dict_int,
int, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(mpool_int), MEMPOOL_LINKAGE(static)),
int, M_DEFAULT_OPLIST)
DICT_STOREHASH_DEF2(dict_str,
const char *, M_OPEXTEND (M_CSTR_OPLIST, MEMPOOL(mpool_str), MEMPOOL_LINKAGE(static)),
int, M_DEFAULT_OPLIST)
int test_int(int N, const unsigned *data)
{
int ret;
dict_int_list_pair_mempool_init(mpool_int);
M_LET(h, DICT_OPLIST(dict_int)) {
for (int i = 0; i < N; ++i) {
if (dict_int_get (h, data[i]) == NULL) {
dict_int_set_at (h, data[i], i);
} else {
dict_int_erase(h, data[i]);
}
}
ret = (int)dict_int_size(h);
}
dict_int_list_pair_mempool_clear(mpool_int);
return ret;
}
int test_str(int N, char * const *data)
{
int ret;
dict_str_list_pair_mempool_init(mpool_str);
M_LET(h, DICT_OPLIST(dict_str)) {
for (int i = 0; i < N; ++i) {
if (dict_str_get (h, data[i]) == NULL) {
dict_str_set_at (h, data[i], i);
} else {
dict_str_erase(h, data[i]);
}
}
ret = (int)dict_str_size(h);
}
dict_str_list_pair_mempool_clear(mpool_str);
return ret;
}
int main(int argc, char *argv[])
{
return udb_benchmark(argc, argv, test_int, test_str);
}
+33
View File
@@ -0,0 +1,33 @@
CC= gcc
CXX= g++
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
CXXFLAGS= $(CFLAGS)
LDFLAGS=
OBJS=
LIBS=
all: run
udb2:
git clone https://github.com/attractivechaos/udb2.git
build: udb2
mkdir -p udb2/mlib
cp test-mlib.c udb2/mlib/test.c
cp Makefile-mlib udb2/mlib/Makefile
touch build
all-recur: build
cd udb2/stb && make CC="gcc -lm"
cd udb2 && for dir in `ls | egrep -v '^(Makefile|common.c|_)'`; do cd $$dir && make && cd -; done
run: all-recur
@cd udb2; \
echo ; echo RUNNING BENCH ; echo ; \
for i in `find . -name 'run-test'` ; do \
echo ; echo `dirname $$i|sed -e 's+./++g'`; \
$$i ; \
done
clean:
rm -rf udb2 build *~
+8
View File
@@ -0,0 +1,8 @@
all:run-test
run-test:test.c ../common.c
$(CC) -std=gnu99 -O2 -Wall -I../../../.. $< -o $@
clean:
rm -f run-test* *~
rm -rf mlib
+34
View File
@@ -0,0 +1,34 @@
#include "../common.c"
#define DICTI_OA_PROBING(s) 1
#include "m-dict.h"
static inline bool oor_equal_p(unsigned int k, unsigned char n)
{
return k == (unsigned int)n;
}
static inline void oor_set(unsigned int *k, unsigned char n)
{
*k = (unsigned int)n;
}
DICT_OA_DEF2(dict_oa_uint,
unsigned int, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(hash_fn), OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
unsigned int, M_DEFAULT_OPLIST)
uint32_t test_int(uint32_t n, uint32_t x0)
{
uint32_t i, x, z = 0;
dict_oa_uint_t h;
dict_oa_uint_init(h);
for (i = 0, x = x0; i < n; ++i) {
x = hash32(x);
unsigned int key = get_key(n, x);
unsigned int *ptr = dict_oa_uint_get_at(h, key);
(*ptr)++;
z += *ptr;
}
fprintf(stderr, "# unique keys: %d; checksum: %u\n", (int) dict_oa_uint_size(h), z);
z = dict_oa_uint_size(h);
dict_oa_uint_clear(h);
return z;
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

+47
View File
@@ -0,0 +1,47 @@
API incompatibility
-------------------
V0.1.*: * First formal version of API
V0.2.*: * worker_start takes a 'worker_t' as an additional parameter
(This enables to perform a wait-on-event instead of a busy waiting).
* worker_spawn doesn't take into a 'worker_t' anymore.
* worker_init takes an int as numWorker instead of an unsigned int.
* RBTREE pop method has been renamed into _pop_at (as it takes a key parameter).
* SNAPSHOT_DEF has been removed. use SNAPSHOT_SPSC_DEF
* array's _set_at2 has been replaced by array's _get_at.
The interface is similar to dict _get_at, making the API cleaner and more generic.
_set_at2(a,i,x) ==> set(*_get_at(a,i),x)
* DICT_SET:
- Rename method _set_at as _push
Since there is no key, it is a push method, not a _set_at method.
- Rename DICT_SET_DEF2 as DICT_SET_DEF
since it only requires one argument.
- The method _ref returns a pointer to the data,
instead of a pointer to a single tuple like other DICT_SET interface.
- The format of the output to character string as changed.
* ALGO:
- Rename _contains as _contain_p
since it is a predicate '_p' and it shall be in infinitive form.
- Rename _map as _for_each (Standard name).
* M_PRINT_ARG & M_FPRINT_ARG don't put a final semicolon at the end
of their expansion anymore (cleaner and no longer needed with the
new available tools).
* For the TUPLE and the VARIANT
change name of _get_ ## field into _get_at_ ## fields
Since there is a name collision with other methods
if a field is named 'str'.
* Rename method _remove into _erase for dictionary & B+TREE (adding a compatibility layer) for API uniformization.
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 ).
* Deprecated buffer options BUFFER_BLOCKING* and BUFFER_UNBLOCKING*
as the _push_blocking and _pop_blocking functions can do the same and are more versatil.
* Iteration over the items of a dictionnary/tree returns const iterators for access through
OPLIST (impact: algorithm & M_EACH) as the IT_REF operator is no longer exported.
The _it_ref method is still defined and can be used, so you can still modify the values
but it won't be automatically used anymore (as the keys CANNOT be modified).
(Previously it generates incorrect algorithm when used with m-dict).
+84
View File
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by LibreOffice - see http://cgit.freedesktop.org/libreoffice/core/tree/filter/source/xslt for the code.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xml:lang="en-US">- no title specified</title><meta name="DCTERMS.title" content="" xml:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2018-07-01T11:06:00.978729880" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.provenance" content="" xml:lang="en-US"/><meta name="DCTERMS.subject" content="," xml:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css">
@page { }
table { border-collapse:collapse; border-spacing:0; empty-cells:show }
td, th { vertical-align:top; font-size:10pt;}
h1, h2, h3, h4, h5, h6 { clear:both }
ol, ul { margin:0; padding:0;}
li { list-style: none; margin:0; padding:0;}
<!-- "li span.odfLiEnd" - IE 7 issue-->
li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; }
span.footnodeNumber { padding-right:1em; }
span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; }
* { margin:0;}
.ta1 { writing-mode:lr-tb; }
.Default { font-family:Liberation Sans; }
.ce100 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce101 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce102 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce103 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
.ce104 { font-family:Liberation Sans; background-color:#e6e6e6; font-size:11pt; text-decoration:underline; font-weight:bold; }
.ce57 { font-family:Liberation Sans; background-color:transparent; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
.ce58 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
.ce59 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-size:11pt; text-decoration:underline; font-weight:bold; }
.ce60 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
.ce61 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
.ce62 { font-family:Liberation Sans; background-color:#e6e6e6; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
.ce63 { font-family:Liberation Sans; background-color:#e6e6e6; border-width:0,0621cm; border-style:solid; border-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; font-weight:bold; }
.ce64 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-weight:bold; }
.ce65 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
.ce66 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
.ce67 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
.ce68 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
.ce69 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-weight:bold; }
.ce70 { font-family:Liberation Sans; background-color:#e6e6e6; font-weight:bold; }
.ce71 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
.ce72 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
.ce73 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
.ce74 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce75 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce76 { font-family:Liberation Sans; border-bottom-style:none; background-color:transparent; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce77 { font-family:Liberation Sans; text-align:center ! important; margin-left:0cm; }
.ce78 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
.ce79 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
.ce80 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
.ce81 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce82 { font-family:Liberation Sans; background-color:transparent; text-align:center ! important; margin-left:0cm; }
.ce83 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce84 { font-family:Liberation Sans; background-color:#ffff00; text-align:center ! important; margin-left:0cm; }
.ce85 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#ffff00; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce86 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
.ce87 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
.ce88 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
.ce89 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
.ce90 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce91 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce92 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce93 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce94 { font-family:Liberation Sans; border-bottom-style:none; background-color:transparent; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce95 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.ce96 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
.ce97 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
.ce98 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
.ce99 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
.co1 { width:3.399cm; }
.co10 { width:1.304cm; }
.co11 { width:1.942cm; }
.co12 { width:1.815cm; }
.co13 { width:1.348cm; }
.co14 { width:1.432cm; }
.co15 { width:2.258cm; }
.co2 { width:6.281cm; }
.co3 { width:1.475cm; }
.co4 { width:1.277cm; }
.co5 { width:1.602cm; }
.co6 { width:1.39cm; }
.co7 { width:1.686cm; }
.co8 { width:1.644cm; }
.co9 { width:2.452cm; }
.ro1 { height:0.487cm; }
.ro2 { height:0.885cm; }
.ro3 { height:0.452cm; }
<!-- ODF styles with no properties representable as CSS -->
{ }
</style></head><body dir="ltr" style="writing-mode:lr-tb; "><table border="0" cellspacing="0" cellpadding="0" class="ta1"><colgroup><col width="149"/><col width="274"/><col width="64"/><col width="56"/><col width="70"/><col width="61"/><col width="74"/><col width="72"/><col width="107"/><col width="57"/><col width="107"/><col width="107"/><col width="74"/><col width="85"/><col width="79"/><col width="79"/><col width="59"/><col width="61"/><col width="63"/><col width="99"/></colgroup><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce57"> </td><td style="text-align:left;width:6.281cm; " class="ce57"> </td><td colspan="6" style="text-align:left;width:1.475cm; " class="ce71"><p>Sequence Containers</p></td><td colspan="4" style="text-align:left;width:2.452cm; " class="ce71"><p>Associative Containers</p></td><td style="text-align:left;width:1.686cm; " class="ce96"> </td><td colspan="6" style="text-align:left;width:1.942cm; " class="ce71"><p>Synchronization Containers</p></td><td style="text-align:left;width:2.258cm; " class="ce104"> </td></tr><tr class="ro2"><td style="text-align:left;width:3.399cm; " class="ce58"> </td><td style="text-align:left;width:6.281cm; " class="ce63"><p>Method</p></td><td style="text-align:left;width:1.475cm; " class="ce72"><p>ARRAY</p></td><td style="text-align:left;width:1.277cm; " class="ce79"><p>LIST</p></td><td style="text-align:left;width:1.602cm; " class="ce79"><p>DUALP</p><p>LIST</p></td><td style="text-align:left;width:1.39cm; " class="ce79"><p>I-LIST</p></td><td style="text-align:left;width:1.686cm; " class="ce79"><p>DEQUE</p></td><td style="text-align:left;width:1.644cm; " class="ce88"><p>BITSET</p></td><td style="text-align:left;width:2.452cm; " class="ce72"><p>B+TREE</p><p>DICT/SET</p></td><td style="text-align:left;width:1.304cm; " class="ce79"><p>R/B</p><p>TREE</p></td><td style="text-align:left;width:2.452cm; " class="ce79"><p>[S]HASH</p><p>DICT / SET</p></td><td style="text-align:left;width:2.452cm; " class="ce88"><p>OA DICT</p></td><td style="text-align:left;width:1.686cm; " class="ce97"><p>PRIO </p><p>QUEUE</p></td><td style="text-align:left;width:1.942cm; " class="ce72"><p>BUFFER</p></td><td style="text-align:left;width:1.815cm; " class="ce79"><p>QUEUE </p><p>MPMC</p></td><td style="text-align:left;width:1.815cm; " class="ce79"><p>QUEUE </p><p>SPSC</p></td><td style="text-align:left;width:1.348cm; " class="ce79"><p>SNAP</p><p>SPSC</p></td><td style="text-align:left;width:1.39cm; " class="ce79"><p>SNAP</p><p>SPMC</p></td><td style="text-align:left;width:1.432cm; " class="ce88"><p>SNAP </p><p>MPMC</p></td><td style="text-align:left;width:2.258cm; " class="ce70"> </td></tr><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce59"><p>CONSTRUCTOR</p></td><td style="text-align:left;width:6.281cm; " class="ce64"><p>_init</p></td><td style="text-align:left;width:1.475cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.277cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.602cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.686cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.644cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.304cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:1.686cm; " class="ce98"><p>X</p></td><td style="text-align:left;width:1.942cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.815cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.815cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.348cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.432cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:2.258cm; " class="Default"> </td></tr><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce60"> </td><td style="text-align:left;width:6.281cm; " class="ce65"><p>_set</p></td><td style="text-align:left;width:1.475cm; " class="ce74"><p>X</p></td><td style="text-align:left;width:1.277cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.602cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce77"> </td><td style="text-align:left;width:1.686cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.644cm; " class="ce90"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce74"><p>X</p></td><td style="text-align:left;width:1.304cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce90"><p>X</p></td><td style="text-align:left;width:1.686cmLine truncated
BIN
View File
Binary file not shown.
+86 -20
View File
@@ -2,34 +2,100 @@ How to make a release?
======================
1) Check that all test pass on all primary systems.
Run 'make check' on all primary systems.
Run 'make check' or 'make checkall' on all primary systems.
No error shall be reported.
2) Check that no memory leak is reported by valgrind:
Run 'make valgrind' in tests folder on a system.
3) Check that no undefined behavior, nor data race, ... is reported by sanitizer:
2) Check that no undefined behavior, nor data race, ... is reported by sanitizer:
Run 'make sanitize' in tests folder on a system.
This needs at least GCC 7
4) Check that no new warning is reported by static analyser:
Run 'make scan-build' in tests folder on a system.
This needs at least clang 4.
Currently there is a false positive detected in m-shared:
"Argument to free() is offset by 8 bytes from the start of memory allocated by malloc()"
scan-build is not able to properly track that this case cannot open.
5) Check that coverage is reasonnable.
3) Check that coverage is reasonable.
Run 'make coverage' and analyze result.
In case of problem, open a problem error.
4) Publish the release
List of primary systems
In case of problem, open a problem report.
List of tested systems
=======================
* gcc 4.9 on linux/x86-64
* gcc 7 on linux/x86-64
* clang 3.5 on linux/x86-64
* clang 4 on linux/x86-64
* g++ 7 on linux/x86-64
* gcc 4.9, 7 & 8 on linux/x86-64
* clang 3.5, 4 & 6 on linux/x86-64
* g++ 7 & 8 on linux/x86-64
* tcc on linux/x86-64
* gcc 7 on windows/x86-64
* gcc 4.9 on linux/powerpc
* gcc 6 on linux/armv7l
* clang 3.8 on linux/armv7l
Cross compilation
=================
There is no need to cross compile the library as it is a header only library.
However if you want to run the test with a cross-compiler, do like the following
command. It performs a cross-compilation of the test suite from linux to windows
and run the test suite with wine:
make CC="i586-mingw32msvc-gcc -std=c99 -DWINVER=0x600 -D_WIN32_WINNT=0x600" LOG_COMPILER=wine check
Handling issues
===============
Issues are opened in the bug tracker of the project (typically https://github.com/P-p-H-d/mlib/issues )
If the issue cannot be closed and taken into account in less than (typically) one month,
or before a release,
it shall be moved into the long term issues included in the repository:
https://github.com/P-p-H-d/mlib/blob/master/doc/ISSUES.org
This is a file in ORG mode: the issues are created with the proper tags.
The original issue in the tracker can then be closed with the issuer agreement.
This will have the following gains:
- no hard dependencies on any bug tracking infrastructure (can be migrated easily).
- a release will automatically contain all its open problem reports.
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
+395
View File
@@ -0,0 +1,395 @@
*Long term issues*
------------------
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
Specialize Hashmap for huge objects on supported hardware:
based on OA hash map of taggued pointer (needs space for tagging)
low bits = low bits of hash
pointer to object { key, hash, value }
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:
Add a new kind of priority queue.
See https://en.wikipedia.org/wiki/Bucket_queue
Check if it will be better as intrusive or non-intrusive container.
To test if a bucket is empty or not, a bitfield can be used to check if
the bit associated to the bucket is set or not. To get the highest bucket
non empty, we can perform a CLZ of the bitset, which shall be much faster than
performing a linear search of the buckets (algorithm complexity is the same,
except that we can scan 64 entries at a time).
Check if we can use BITSET, or introduce fixed size BITSET or use ad-hock
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:
Some containers don't have all the methods they should.
See the cells in yellow here:
http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Container.html
* TODO #17: Ressource handler :ENHANCEMENT:
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 if the program
creates one billion ressources per second, the counter won't overflow
until 585 years).
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 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:
- reduce fragmentation,
- no cycle dependencies,
- shared_ref & weak_ref becomes only HANDLE,
- all ressources can be freed in one pass.
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:
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.
** First step: backoff methods :DONE:
** Second step: lock free node pool : :DONE:
Done as m-c-mempool header.
** Third step: Implement generic lock free list on top of it.
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.
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: 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 Exception
Handling Stack so that throwing exceptions may work reliably.
Proposal:
- M_CONSTRUCTOR_PROLOGUE(object, oplist);
- M_CONSTRUCTOR_EPILOGUE(object, oplist);
- 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 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 advanced allocators:
- non-default alignment requirements for types,
- instance-based allocator (may need instance based variable access),
- expected life of created type (temporary or permanent),
- stack based allocator,
- global variable access for allocator,
- maximum allocation before failure.
Most of theses are already more or less supported. Examples shall be created to show how to deal with this:
- alignement shall be implemented with the attributes of <stdalign.h>
However I sill don't know how to implement "instance-based allocator" which is what is missing.
The problem is how to give to methods context local information store within the container itself.
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:
Add an extension to the SHARED_PTR API:
- New type atomic_shared_ptr
- name_init_atomic_set (&atomic_shared_ptr, shared_ptr);
- name_init_set_atomic (shared_ptr, &atomic_shared_ptr);
- name_init_atomic_set_atomic (&atomic_shared_ptr, &atomic_shared_ptr);
- name_atomic_set (&atomic_shared_ptr, shared_ptr);
- name_set_atomic (shared_ptr, &atomic_shared_ptr);
- name_atomic_set_atomic (&atomic_shared_ptr, &atomic_shared_ptr);
- name_atomic_clear
No _ref or direct _init: we need to init first a normal shared_ptr then the atomic (TBC)
** _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 like a normal shared pointer.
All 3 steps are safe.
** _set_atomic method:
It needs to perform the following atomic operation : <read the pointer, deref pointer and increment the pointed value> I don't known how to do it properly.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4162.pdf
Proposition for _set_atomic we store temporary NULL to the atomic_ptr struct to request an exclusive access to the data (this looks like a lock and other operations need to handle NULL) :
#+BEGIN_SRC C
void shared_ptr_set_atomic(ptr a, atomic_ptr *ptr)
{
// Get exclusive access to the data
p = atomic_load(ptr);
do {
if (p == NULL) {
// TODO: exponential backoff
p = atomic_load(ptr);
continue;
}
} while (!atomic_compare_exchange_weak(ptr, &p, NULL));
// p has exclusive access to the pointer
p->data->cpt ++;
a->data = p->data;
atomic_exchange (ptr, p);
}
#+END_SRC
This prevents using NULL which obliges atomic shared pointer to point to a created object...
Other alternative solution is to use the bit 0 to mark the pointer as being updated, preventing other from using it (TBC only clear):
#+BEGIN_SRC C
void shared_ptr_set_atomic(ptr a, atomic_ptr *ptr)
{
// Get exclusive access to the data
p = atomic_load(ptr);
do {
if ( (p&1) != 0) {
// TODO: exponential backoff
p = atomic_load(ptr);
continue;
}
} while (!atomic_compare_exchange_weak(ptr, &p, p|1));
// Exclusive access (kind of lock).
p->data->cpt ++;
a->data = p->data;
atomic_set (ptr, p);
}
#+END_SRC
Other implementation seems to have it hard to be lock-free: cf. https://github.com/llvm-mirror/libcxx/commit/5fec82dc0db3623546038e4a86baa44f749e554f
* TODO #5: Concurrent dictionary Container :ENHANCEMENT:
Implement a more efficient dictionary than lock + std dictionary for all operations when dealing with threads.
See https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
** Multiple locks within the dictionnary
A potential implementation may be to request at initialization time the number of concurrent thread N.
Create a static array of N dictionnary with N mutex. Then to access the data will perform :
- compute hash of object,
- access high bits of hash and select which dictionnary shall have the data,
- lock it,
- perform classic access to the data (check if the compiler can properly optimize the hash computation),
- unlock it.
The property of the hash shall allow a good dispersion of the data across multiple locks, reducing the constraints on the lock. This implementation could be build easily upon the already existent dictionary.
To test.
See also https://github.com/simonhf/sharedhashfile
** Lock Free dictionnary
Evaluate also lock-free dictionary (easier with open addressing).
It needs a complete rewrite of the inner loop through. The hard part is the dynamic resizing of the internal array (see http://preshing.com/20160222/a-resizable-concurrent-map/ for a potential solution and http://www.cs.toronto.edu/~tomhart/papers/tomhart_thesis.pdf for memory reclamation techniques). See also https://www.research.ibm.com/people/m/michael/spaa-2002.pdf
https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2017.11.22a.pdf persents different techniques used by linux kernel.
It needs before lock-free list: http://www.cse.yorku.ca/~ruppert/papers/lfll.pdf http://www.cse.yorku.ca/~ruppert/Mikhail.pdf
** Context
The best parallel algorithm is still when there is as few synchronization as possible. A concurrent dictionary will fail at this and will result in average performance at best.
The typical best case will be in RCU context (a lot of readers, few writers), so the interface shall be compatible with such structure.
** Linked list
Another solution is to create a huge list of items which is:
- atomically updated,
- in reverse order of the hash (bit 0 is highest bit, Bit 63 is the lowest bit).
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.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+12 -5
View File
@@ -1,18 +1,25 @@
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
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 -g
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'`
if test "$(CC)" = "cc -std=c99" ; then $(MAKE) `ls ex11-*.c|sed -e 's/\.c/\.exe/g'` CC="cc -std=c11" ; fi
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.c config-test.exe
clean:
$(RM) *.exe *.s *~ *.o ./a.dat
$(RM) *.exe *.s *~ *.o ./a.dat config
+53
View File
@@ -0,0 +1,53 @@
#include <stdio.h>
#include "m-array.h"
#include "m-algo.h"
/* Define a dynamic array of int */
ARRAY_DEF(vector_int, int)
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
ALGO_DEF(vector_int, vector_int_t)
/* Define a dynamic array of bool */
ARRAY_DEF(vector_bool, bool)
#define M_OPL_vector_bool_t() ARRAY_OPLIST(vector_bool)
ALGO_DEF(vector_bool, vector_bool_t)
static bool id(bool x) { return x ; }
int main(void)
{
M_LET(myvector, tmp, vector_int_t) {
vector_int_fill_n(myvector, 3, 20);
vector_int_fill_n(tmp, 3, 33);
vector_int_splice(myvector, tmp);
vector_int_fill_n(tmp, 2, 10);
vector_int_splice(myvector, tmp);
printf("myvector contains:");
// In C11, the for loop can be simplified as :
// ALGO_FOR_EACH(myvector, vector_int_t, M_PRINT, " ");
for M_EACH(it, myvector, vector_int_t) {
printf(" %d", *it);
}
printf("\n");
}
M_LET(myvector, tmp, vector_bool_t) {
vector_bool_fill_n(myvector, 3, false);
vector_bool_fill_n(tmp, 3, true);
vector_bool_splice(myvector, tmp);
vector_bool_fill_n(tmp, 2, false);
vector_bool_splice(myvector, tmp);
printf("myvector contains:");
// In C11, the for loop can be simplified as :
// ALGO_FOR_EACH(myvector, vector_bool_t, M_PRINT, " ");
for M_EACH(it, myvector, vector_bool_t) {
printf(" %d", (int)*it);
}
printf("\n");
printf ("ALL: %d ANY: %d NONE: %d\n",
vector_bool_all_of_p(myvector, id),
vector_bool_any_of_p(myvector, id),
vector_bool_none_of_p(myvector, id) );
}
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
#include "m-dict.h"
#include "m-array.h"
#include "m-algo.h"
#include "m-string.h"
DICT_DEF2(dict, string_t, int)
#define M_OPL_dict_t() DICT_OPLIST(dict, STRING_OPLIST, M_DEFAULT_OPLIST)
ARRAY_DEF(vector_int, int)
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
#define start_with(pattern, item) \
string_start_with_str_p((item).key, (pattern))
#define get_value(out, item) ((out) = (item).value)
int main(void)
{
int s;
M_LET(keys, vector_int_t)
M_LET( (m, (STRING_CTE("foo"), 1), (STRING_CTE("bar"), 42), (STRING_CTE("bluez"), 7), (STRING_CTE("stop"), 789) ), tmp, dict_t) {
/* Extract all elements of 'm' that starts with 'b' */
ALGO_EXTRACT(tmp, dict_t, m, dict_t, start_with, "b");
/* Extract the values of theses elements */
ALGO_TRANSFORM(keys, vector_int_t, tmp, dict_t, get_value);
/* Sum theses values */
ALGO_REDUCE(s, keys, vector_int_t, sum);
printf("Sum of elements starting with 'b' is: %d\n", s);
}
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
#include "m-algo.h"
#include "m-array.h"
#include "m-string.h"
ARRAY_DEF(vector_int, int)
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
#define str_ui(s, i) string_printf(s, "%d", i)
#define str_cat(s1, s2) (string_cat_str(s1, "-"), string_cat(s1, s2))
int main(void)
{
M_LET(s, string_t)
M_LET( (v, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), vector_int_t) {
int sum, product;
ALGO_REDUCE(sum, v, vector_int_t, add);
ALGO_REDUCE(product, v, vector_int_t, mul);
/* Example of reduction with a transformation of the type from int to string_t */
ALGO_REDUCE( (s, string_t), v, vector_int_t, str_cat, str_ui);
printf ("sum: %d\n"
"product: %d\n"
"dash str: %s\n", sum, product, string_get_cstr(s));
}
return 0;
}
+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
+116
View File
@@ -0,0 +1,116 @@
#include "m-array.h"
#include "m-string.h"
/* This example show how to use complex structure with array
embedding another library */
/* This is a typical trivial library with its interface */
typedef struct lib_ext_struct {
int id;
// Other data may be added
} lib_ext_struct;
/* Method to create a duplicate of the object.
The new object is heap allocated */
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;
}
/* Method to delete any object */
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;
/* This method shall initialize the object data_node to
the init state.
It takes as first argument the object to initialize,
and since it takes by reference through a pointer,
it is compatible with 'API_2' convention for oplist */
static void data_node_init(data_node *obj)
{
obj->id = 0;
string_init(obj->type);
obj->properties = NULL;
}
/* This method shall initialize the object data_node to
the same state as the 'src' object (creating a copy, a duplicate) .
It takes as first argument the object to initialize,
and as second argument the source object.
It takes both object by reference through pointers,
as such it is compatible with 'API_6' convention for oplist */
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;
}
/* This method shall set the object data_node to
the same state as the 'src' object (creating a copy, a duplicate) .
THe object was already initialized.
It takes as first argument the object to initialize,
and as second argument the source object.
It takes both object by reference through pointers,
as such it is compatible with 'API_6' convention for oplist */
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;
}
/* This method shall clear the object data_node to
the cleared state (aka it shall free any resource).
It takes as first argument the object to initialize,
and since it takes by reference through a pointer,
it is compatible with 'API_2' convention for oplist */
static void data_node_clear(data_node *obj)
{
string_clear(obj->type);
if (obj->properties)
lib_ext_struct_Delete(obj->properties);
}
/* Create a dynamic array of 'data_node' named array_data_node_t
and gives the minimum oplist allowing M*LIB to handle
properly the object 'data_node' with the API convention
to respect for calling the methods.
*/
ARRAY_DEF(/* the array prefix */ array_data_node,
/* the object type */ data_node,
/* the object oplist */ (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))))
/* Define a global array with the created type */
array_data_node_t global_array;
int main(void)
{
/* Call the created method */
array_data_node_init(global_array);
/* Do stuff... */
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;
}
+27 -14
View File
@@ -5,11 +5,13 @@
#include "m-mutex.h"
/* Global variable to stop scheduling */
bool continue_threading_g;
atomic_bool continue_threading_g;
/**************************************************************/
/* Define a random big data structure */
/* Define a custom random big data structure
with its constructor and its destructor.
*/
#define BIGDATA_SIZE 1000
#define BIGDATA_NUM 10
@@ -28,6 +30,7 @@ static void bigdata_init(bigdata_t d)
{
memset(d->bigdata, 0, BIGDATA_SIZE);
d->ptr = malloc (BIGDATA_HEIGHT*BIGDATA_WIDTH);
if (!d->ptr) abort();
d->count = count_g++;
printf ("Init image %d\n", d->count);
}
@@ -38,19 +41,25 @@ static void bigdata_clear(bigdata_t d)
free(d->ptr);
}
/* Let's register the oplist of our custom type globally */
#define M_OPL_bigdata_t() (INIT(bigdata_init), CLEAR(bigdata_clear))
/**************************************************************/
/* Perform some random works on this big data */
/* Perform some random works on this custom type */
static void perform_acquisition(bigdata_t p)
{
printf ("Acquiring image %d\n", p->count);
// Let's say that rand is our acquisition function.
for(int i = 0 ; i < BIGDATA_SIZE;i++)
p->bigdata[i] = rand();
for(int i = 0 ; i < BIGDATA_WIDTH*BIGDATA_HEIGHT;i++)
p->ptr[i] = rand();
// Let's stop arbitrary after 20 acquisitions
if (p->count == 20)
continue_threading_g = false;
atomic_store(&continue_threading_g, false);
}
static void perform_computation2(bigdata_t p)
@@ -69,7 +78,7 @@ static void perform_computation3(bigdata_t p)
static void perform_computation4(bigdata_t p)
{
printf ("Computation 4 on image %d\n", p->count);
printf ("Reduction on image %d\n", p->count);
unsigned long s = 0;
for(int i = 0 ; i < BIGDATA_WIDTH*BIGDATA_HEIGHT;i++)
s += p->ptr[i];
@@ -81,11 +90,12 @@ static void perform_computation4(bigdata_t p)
/**************************************************************/
/* Let's define a shared pointer interface around the bigdata structure */
SHARED_PTR_DEF(shared_bigdata, bigdata_t, (INIT(bigdata_init), CLEAR(bigdata_clear)))
SHARED_PTR_DEF(shared_bigdata, bigdata_t)
/* Let's define the communication queue between the thread:
* - size of queue is 10,
* - it is a queue BUFFER_QUEUE,
* - it store shared pointer to our bigdata_t,
* - we change the semantics of _push / _pop into buffer as push a new item and pop it as a move (ideal semantics for shared_ptr interface).
*/
#define MY_QUEUE_SIZE 10
@@ -94,11 +104,11 @@ BUFFER_DEF(buffer_bigdata, shared_bigdata_t, MY_QUEUE_SIZE,
/* Let's build a Thread synchros tree:
*
* Thread1 perform the simulation acquisition of the data.
* Thread2 & Thread3 perform independet computation.
* Thread1 perform the simulated acquisition of the data.
* Thread2 & Thread3 perform independent computation.
* Thread4 finalizes the computation :
*
* Thread1 ---> Thread 2
* Thread1 ---> Thread 2 +
* +-> Thread 3 --> Thread 4
*/
buffer_bigdata_t buf_t1tot2;
@@ -123,7 +133,7 @@ static void clearbuf(void)
static void thread1 (void *arg)
{
(void) arg;
while (continue_threading_g) {
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
shared_bigdata_init_new (ptr);
perform_acquisition(*shared_bigdata_ref(ptr));
@@ -135,7 +145,7 @@ static void thread1 (void *arg)
static void thread2 (void *arg)
{
(void) arg;
while (continue_threading_g) {
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
buffer_bigdata_pop(&ptr, buf_t1tot2); // NOTE: Pop has been configured to init ptr
perform_computation2(*shared_bigdata_ref(ptr));
@@ -145,7 +155,7 @@ static void thread2 (void *arg)
static void thread3 (void *arg)
{
(void) arg;
while (continue_threading_g) {
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
buffer_bigdata_pop(&ptr, buf_t1tot3); // NOTE: Pop has been configured to init ptr
perform_computation3(*shared_bigdata_ref(ptr));
@@ -156,7 +166,7 @@ static void thread3 (void *arg)
static void thread4 (void *arg)
{
(void) arg;
while (continue_threading_g) {
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
buffer_bigdata_pop(&ptr, buf_t3tot4); // NOTE: Pop has been configured to init ptr
perform_computation4(*shared_bigdata_ref(ptr));
@@ -164,11 +174,12 @@ static void thread4 (void *arg)
}
}
/**************************************************************/
static void init(void)
{
initbuf();
continue_threading_g = true;
atomic_init(&continue_threading_g, true);
m_thread_create(t1, thread1, NULL);
m_thread_create(t2, thread2, NULL);
m_thread_create(t3, thread3, NULL);
@@ -184,6 +195,8 @@ static void clear(void)
clearbuf();
}
/**************************************************************/
int main(void)
{
init();
+2 -2
View File
@@ -15,7 +15,7 @@ int main(void) {
char *k = dict_m32_get(h, 10); // No 10 in h, so k == NULL
int is_missing = (k != NULL); // true
assert (is_missing);
dict_m32_remove(h, 5); // h is now empty
dict_m32_erase(h, 5); // h is now empty
dict_m32_it_t it; // iterate over all dictionnary
for (dict_m32_it (it, h) ; !dict_m32_end_p (it); dict_m32_next(it)) {
struct dict_m32_pair_s *item = dict_m32_ref(it);
@@ -33,7 +33,7 @@ int main(void) {
char *k = dict_m32_get(h, 10); // k == NULL
int is_missing = (k != NULL); // true
assert (is_missing);
dict_m32_remove(h, 5); // h is now empty
dict_m32_erase(h, 5); // h is now empty
for M_EACH(item, h, M32_OPLIST) { // traverse each item
item->value = 1; // Set its value to 1
}
+2 -2
View File
@@ -50,11 +50,11 @@ int main(void)
printf("Enter the mapped string to be deleted: ");
string_fgets (str, stdin, STRING_READ_PURE_LINE);
s = atoi(string_get_cstr(str));
dict_map_remove(mp, s);
dict_map_erase(mp, s);
break;
case 3:
printf("Size of Map: %lu\n", (unsigned long) dict_map_size(mp) );
printf("Size of Map: %zu\n", dict_map_size(mp) );
break;
case 4:
+3 -3
View File
@@ -5,14 +5,14 @@
/* program that reads text from standard-input,
and prints the total number of distinct words found in the text.
*/
DICT_SET_DEF2(dict_str, string_t, STRING_OPLIST)
DICT_SET_DEF(dict_str, string_t, STRING_OPLIST)
int main(void)
{
M_LET(word, STRING_OPLIST)
M_LET(wordcount, DICT_SET_OPLIST(dict_str, STRING_OPLIST)) {
while (string_fget_word(word, " \t\n,.!;:?", stdin))
dict_str_set_at(wordcount, word);
printf ("Words: %lu\n", dict_str_size(wordcount));
dict_str_push(wordcount, word);
printf ("Words: %zu\n", dict_str_size(wordcount));
}
}
+32
View File
@@ -0,0 +1,32 @@
#include <stdio.h>
#include "m-string.h"
#include "m-array.h"
#include "m-dict.h"
ARRAY_DEF(array_size, size_t)
#define M_OPL_array_size_t() ARRAY_OPLIST(array_size)
DICT_DEF2(map_pos, string_t, array_size_t)
#define M_OPL_map_pos_t() DICT_OPLIST(map_pos, STRING_OPLIST, ARRAY_OPLIST(array_size))
int main(int argc, const char *argv[])
{
if (argc == 2) {
M_LET(word, string_t)
M_LET(positions, map_pos_t) {
FILE *f = fopen(argv[1], "rt");
if (!f) return 1;
while (string_fget_word(word, " \t\n\r\"(),=", f)) {
array_size_t *array = map_pos_get_at(positions, word);
array_size_push_back(*array, (size_t)ftell(f) - string_size(word));
}
fclose(f);
for M_EACH(pair, positions, map_pos_t) {
string_fputs(stdout, pair->key);
for M_EACH(pos, pair->value, array_size_t)
printf(" %zu", *pos);
printf("\n");
}
}
}
}
+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}
]
+1 -1
View File
@@ -38,7 +38,7 @@ int main(void)
list_string_get_str (tmpstr, grow, false);
// print out
printf("Number of elements = %lu\n", (unsigned long) list_string_size(grow));
printf("Number of elements = %zu\n", list_string_size(grow));
printf("Final string = %s\n", string_get_cstr(tmpstr));
// Let's iterate over each element of the list
+246
View File
@@ -0,0 +1,246 @@
#include <stdio.h>
#include <stdint.h>
#include "m-array.h"
#include "m-dict.h"
#include "m-string.h"
#include "m-algo.h"
/* Based on:
* Easy Perfect Minimal Hashing
* By Steve Hanov. Released to the public domain.
* http://stevehanov.ca/blog/index.php?id=119
*
* Based on:
* Edward A. Fox, Lenwood S. Heath, Qi Fan Chen and Amjad M. Daoud,
* "Practical minimal perfect hash functions for large databases", CACM, 35(1):105-121
*
* also a good reference:
* Compress, Hash, and Displace algorithm by Djamal Belazzougui,
* Fabiano C. Botelho, and Martin Dietzfelbinger
*/
/* Define the needed data structure : */
// Define a dictionnary of constant string --> integer
DICT_DEF2(dict_mph, const char *, M_CSTR_OPLIST, uint32_t, M_DEFAULT_OPLIST)
#define M_OPL_dict_mph_t() DICT_OPLIST(dict_mph, M_CSTR_OPLIST, M_DEFAULT_OPLIST)
// Defin an array of integer
ARRAY_DEF(array_value, uint32_t) // Start from 1. 0 is 'None'
#define M_OPL_array_value_t() ARRAY_OPLIST(array_value)
// Defin an array of integer
ARRAY_DEF(array_seed, int32_t)
#define M_OPL_array_seed_t() ARRAY_OPLIST(array_seed)
ALGO_DEF(array_seed, array_seed_t)
// Define an array of constant string that are ordered by their size.
ARRAY_DEF(array_cstr, const char *, M_CSTR_OPLIST)
static inline int array_cstr_cmp(const array_cstr_t a, const array_cstr_t b) {
size_t sa = array_cstr_size(a);
size_t sb = array_cstr_size(b);
return (sa > sb) ? -1 : (sa < sb);
}
#define M_OPL_array_cstr_t() M_OPEXTEND(ARRAY_OPLIST(array_cstr, M_CSTR_OPLIST), CMP(array_cstr_cmp))
// Define an array of array of constant string, and some algo over this container
ARRAY_DEF(array_bucket, array_cstr_t)
#define M_OPL_array_bucket_t() ARRAY_OPLIST(array_bucket, M_OPL_array_cstr_t() )
ALGO_DEF(array_bucket, array_bucket_t)
// Define an array of string to save the input.
ARRAY_DEF(array_string, string_t)
#define M_OPL_array_string_t() ARRAY_OPLIST(array_string, M_OPL_string_t())
/* The algorithm may not converge on all inputs if the hash function
* doesn't randomly distribute the inputs:
* str1 != str2 ==> \exist seed \such_as hash(seed,str1) != hash(seed, str2)
* The first hash function below doesn't provide the needed property for
* inputs with two words 'a' and 'c'
* The second one is more robust but may still have this issue.
*/
static uint32_t hash(int32_t seed, const char str[])
{
#if 0
if (seed == 0) seed = 0x01000193;
while (*str) {
seed = ((seed * 0x01000193) ^ *str);
str++;
}
return seed;
#else
if (seed == 0) seed = 0x811C9DC5;
while (*str) {
seed = (seed ^ *str) * 16777619;
str++;
}
return seed ^ (seed >> 16);
#endif
}
// Step 1: place all key keys into buckets
static void
CreateMinimalPerfectHash1(array_seed_t seed, array_value_t value,
array_bucket_t bucket,
const dict_mph_t dict)
{
size_t size = dict_mph_size (dict);
array_value_resize(value, size);
array_seed_resize(seed, size);
array_bucket_resize(bucket, size);
for M_EACH(item, dict, dict_mph_t) {
size_t i = hash(0, item->key) % size;
array_cstr_push_back (*array_bucket_get (bucket, i), item->key);
}
}
// Step 2: Sort the buckets and process the ones with the most items first.
static void
CreateMinimalPerfectHash2(array_seed_t seed, array_value_t value,
array_bucket_t bucket,
const dict_mph_t dict)
{
size_t size = dict_mph_size (dict);
array_bucket_sort(bucket);
M_LET(slots, array_seed_t) {
for M_EACH(b, bucket, array_bucket_t) {
if (array_cstr_size(*b) <= 1)
break;
size_t d = 1;
/* Repeatedly try different values of d until we find a hash function
that places all items in the bucket into free slots.
This may loop forever on some inputs if the hash function is not good
enought. */
for(bool cont = true; cont ; ) {
cont = false;
array_seed_clean(slots);
for M_EACH(it, *b, array_cstr_t) {
size_t s = hash(d, *it) % size;
if (*array_value_get(value, s) != 0 || array_seed_contain_p(slots, s)){
d++;
cont = true;
break;
}
array_seed_push_back (slots, s);
}
}
// Fill in the seed & value with the computed value
array_seed_set_at(seed, hash(0, *array_cstr_get(*b, 0)) % size, d);
d = 0;
for M_EACH(it, *b, array_cstr_t) {
array_value_set_at(value,
*array_seed_get(slots, d),
*dict_mph_get(dict, *it));
d++;
}
}
}
}
/* Step 3:
* Only buckets with 1 item remain. Process them more quickly by directly
* placing them into a free slot. Use a negative value of d to indicate
* this.
*/
static void
CreateMinimalPerfectHash3(array_seed_t seed, array_value_t value,
array_bucket_t bucket,
const dict_mph_t dict)
{
size_t size = dict_mph_size (dict);
M_LET(freelist, array_seed_t) {
// Get a list of available slot
for(size_t i = 0; i < size; i++)
if (*array_value_get(value, i) == 0)
array_seed_push_back(freelist, i);
for M_EACH(b, bucket, array_bucket_t) {
if (array_cstr_size(*b) != 1)
continue;
int32_t slot;
array_seed_pop_back(&slot, freelist);
/*We subtract one to ensure it's negative even if the zeroeth slot was
used. */
array_seed_set_at (seed, hash(0, *array_cstr_get(*b, 0)) % size,
-slot-1);
array_value_set_at(value, slot, *dict_mph_get(dict, *array_cstr_get(*b, 0)));
}
}
}
static void
CreateMinimalPerfectHash(array_seed_t seed, array_value_t value,
const dict_mph_t dict)
{
M_LET(bucket, array_bucket_t) {
CreateMinimalPerfectHash1(seed, value, bucket, dict);
CreateMinimalPerfectHash2(seed, value, bucket, dict);
CreateMinimalPerfectHash3(seed, value, bucket, dict);
}
}
static void
dict_read_from_file(dict_mph_t dict, array_string_t arr, const char filename[])
{
FILE *f = fopen(filename, "rt");
if (!f) abort();
uint32_t line = 1;
M_LET(str, string_t) {
while (!feof(f) && !ferror(f)) {
string_fgets(str, f, STRING_READ_PURE_LINE);
string_strim(str);
if (!string_empty_p(str)) {
// Not clean as items of 'dict' points are linked to items of 'arr'
// and the containers are not aware of this link.
array_string_push_back (arr, str);
dict_mph_set_at(dict, string_get_cstr(*array_string_back(arr)), line);
line++;
}
}
}
fclose(f);
}
static uint32_t
perfect_hash_lockup(array_seed_t seed, array_value_t value,
const char key[])
{
int32_t s = *array_seed_get(seed, hash(0, key) % array_seed_size(seed));
if (s < 0)
return *array_value_get(value, -s-1);
else
return *array_value_get(value, hash(s, key) % array_value_size(value));
}
static void
test(array_seed_t seed, array_value_t value, dict_mph_t dict)
{
for M_EACH(item, dict, dict_mph_t) {
const char *key = item->key;
uint32_t v = perfect_hash_lockup(seed, value, key);
if (v != item->value) {
fprintf(stderr, "ERROR for %s: %u VS %u\n", key, v, item->value);
}
}
}
int main(int argc, const char *argv[])
{
M_LET(arr, array_string_t) M_LET(dict, dict_mph_t) M_LET(seed, array_seed_t)
M_LET(value, array_value_t) {
const char *filename = (argc > 1) ? argv[1] :"/usr/share/dict/words";
dict_read_from_file(dict, arr, filename);
CreateMinimalPerfectHash(seed, value, dict);
test(seed, value, dict);
}
return 0;
}
+2 -2
View File
@@ -47,8 +47,8 @@ static void program_init(program_t p, string_t text) {
int left;
array_int_pop_back(&left, leftstack);
int right = pc;
array_int_set_at2 (p->bracket_map, left, right);
array_int_set_at2 (p->bracket_map, right, left);
*array_int_get_at (p->bracket_map, left) = right;
*array_int_get_at (p->bracket_map, right) = left;
}
pc++;
+9 -2
View File
@@ -1,3 +1,5 @@
#if HAVE_GMP
#include <stdio.h>
#include <gmp.h>
@@ -50,14 +52,19 @@ int main(void)
// Dictionary iterator are pair (key,value)
if (my_integer_n_p(p->value)) {
printf ("It is a long long, value = %lld\n",
*my_integer_get_n(p->value));
*my_integer_cget_n(p->value));
}
if (my_integer_z_p(p->value)) {
printf ("It is a mpz_t, value =");
mpz_out_str(stdout, 10, *my_integer_get_z(p->value));
mpz_out_str(stdout, 10, *my_integer_cget_z(p->value));
printf("\n");
}
}
}
} /* All variables are automatically cleared beyond this point */
}
#else
// Nothing to do
int main(void) { return 0; }
#endif
+3 -1
View File
@@ -12,7 +12,9 @@ unsigned long long rand_seed;
#include "m-variant.h"
/* This example will show how to create JSON-like files.
This is a JSON **like** syntax, not exactly JSON format */
This is a JSON **like** syntax, not exactly JSON format.
For an example with JSON format, see https://github.com/P-p-H-d/mjsonlib
*/
/* First let's handle the OUT/IN functions of boolean */
static inline void boolean_out_str(FILE *f, bool b)
+90
View File
@@ -0,0 +1,90 @@
#include <stdio.h>
#include "m-tuple.h"
#include "m-array.h"
#include "m-string.h"
#include "m-list.h"
#include "m-buffer.h"
/* This example chains the structure in different ways in
order to show/test the good chaining of the structures */
TUPLE_DEF2(symbol, (type, int), (name, string_t))
#define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, STRING_OPLIST)
ARRAY_DEF(symbol_list, symbol_t)
#define M_OPL_symbol_list_t() ARRAY_OPLIST(symbol_list, M_OPL_symbol_t())
TUPLE_DEF2(record, (fields, symbol_list_t))
#define M_OPL_record_t() TUPLE_OPLIST(record, M_OPL_symbol_list_t())
TUPLE_DEF2(my_tuple, (name, string_t), (value, int))
#define M_OPL_my_tuple_t() TUPLE_OPLIST(my_tuple)
ARRAY_DEF(my_list_of_tuple, my_tuple_t)
#define M_OPL_my_list_of_tuple_t() ARRAY_OPLIST(my_list_of_tuple)
TUPLE_DEF2(my_tuple_of_tuple, (name, string_t), (inner_tuple, my_tuple_t))
#define M_OPL_my_tuple_of_tuple_t() TUPLE_OPLIST(my_tuple_of_tuple)
LIST_DEF(list_of_tuple_of_tuple, my_tuple_of_tuple_t)
#define M_OPL_list_of_tuple_of_tuple_t() LIST_OPLIST(list_of_tuple_of_tuple, M_OPL_my_tuple_of_tuple_t() )
TUPLE_DEF2(my_triple_tuple, (name, string_t), (inner_tuple, my_tuple_t), (list, list_of_tuple_of_tuple_t) )
#define M_OPL_my_triple_tuple_t() TUPLE_OPLIST(my_triple_tuple, STRING_OPLIST, M_OPL_my_tuple_t(), M_OPL_list_of_tuple_of_tuple_t())
BUFFER_DEF(trbuff, my_triple_tuple_t, 100, BUFFER_QUEUE)
#define M_OPL_trbuff_t() BUFFER_OPLIST(trbuff, M_OPL_my_triple_tuple_t())
TUPLE_DEF2(my_junction, (active, bool), (buffer, trbuff_t))
#define M_OPL_my_junction_t() TUPLE_OPLIST(my_junction, M_DEFAULT_OPLIST, M_OPL_trbuff_t())
TUPLE_DEF2(mlib_point, (x, int), (y, int))
#define M_OPL_mlib_point_t() \
TUPLE_OPLIST(mlib_point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
TUPLE_DEF2(mlib_points, (first_ref, mlib_point_ptr, M_PTR_OPLIST),
(second_ref, mlib_point_ptr, M_PTR_OPLIST),
(third, mlib_point_t))
#define M_OPL_mlib_points_t() \
TUPLE_OPLIST(mlib_points, M_PTR_OPLIST, M_PTR_OPLIST, M_OPL_mlib_point_t())
static void let2(void)
{
M_LET((p1, 4, 4), (p2, 5, 5), mlib_point_t) {
M_LET(pair, mlib_points_t) {
pair->first_ref = p1;
pair->second_ref = p2;
pair->third->x = pair->third->y = 6;
assert(pair->first_ref->x == 4);
p1->x = 10;
assert(pair->first_ref->x == 10);
assert(pair->third->x == 6);
}
}
}
static void let(void)
{
M_LET(s, symbol_t)
M_LET(l, symbol_list_t)
M_LET(r,record_t)
M_LET(tup, my_tuple_t)
M_LET(l2, my_list_of_tuple_t)
M_LET(tuptup, my_tuple_of_tuple_t)
M_LET(ltuptup, list_of_tuple_of_tuple_t)
M_LET(tuptuptup, my_triple_tuple_t)
M_LET(junc, my_junction_t)
{
printf("Init ok\n");
}
}
int main(void)
{
let();
let2();
}
+31
View File
@@ -0,0 +1,31 @@
#include <stdio.h>
#include "m-array.h"
#include "m-string.h"
// Let's create an array of string_t and register it globaly
ARRAY_DEF(vector_string, string_t)
#define M_OPL_vector_string_t() ARRAY_OPLIST(vector_string, STRING_OPLIST)
int main(void)
{
// Let's define a new string named str
M_LET(str, string_t)
// Let's define msg as a vector_array_t
M_LET( (msg, STRING_CTE("Hello"), STRING_CTE("C"), STRING_CTE("World!"), STRING_CTE("Let's"), STRING_CTE("discover"), STRING_CTE("M*LIB!")), vector_string_t) {
// Iterate over all words of the array msg
for M_EACH(word, msg, vector_string_t) {
// Cat the current word into the target string
string_cat(str, *word);
// Also add a space character
string_push_back(str, ' ');
}
// Let's define another string and set it to a formatted value.
M_LET( (count, "\nTo display this it uses \"vector\" of %zu strings!", vector_string_size(msg)), string_t) {
string_cat(str, count);
}
// Print the result
printf("%s\n", string_get_cstr(str) );
}
return 0;
}
+10 -8
View File
@@ -4,6 +4,15 @@
#include "m-rbtree.h"
// Let's define one utility function.
void *xmalloc(size_t n);
void *xmalloc(size_t n)
{
void *p = malloc (n);
if (!p) abort();
return p;
}
// Let's define a new type.
typedef struct {
int key;
@@ -12,7 +21,7 @@ typedef struct {
#define SIZE 256
void *xmalloc(size_t n);
// Let's define the prototypes of the methods of this new type.
void my_type_init(my_type_t x);
void my_type_clear(my_type_t x);
void my_type_init_set(my_type_t x, const my_type_t y);
@@ -24,13 +33,6 @@ void my_type_out_str(FILE *f, const my_type_t x);
bool my_type_in_str(my_type_t x, FILE *f);
void my_type_rand(my_type_t x);
void *xmalloc(size_t n)
{
void *p = malloc (n);
if (!p) abort();
return p;
}
// Let's define its constructor
void my_type_init(my_type_t x)
{
+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
+37
View File
@@ -0,0 +1,37 @@
/* Based on https://en.cppreference.com/w/cpp/algorithm/for_each
Need to be built in C11 mode */
#include <stdio.h>
#include "m-array.h"
#include "m-algo.h"
/* Define a dynamic array of int */
ARRAY_DEF(vector_int, int)
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
/* Define operator increment on int */
#define INC(n) (n)++
int main(void)
{
M_LET( (nums, 3, 4, 2, 8, 15, 267), vector_int_t) {
// Print the array before
printf ("before:");
ALGO_FOR_EACH(nums, vector_int_t, M_PRINT, " ");
printf ("\n");
// Increment each element of the array
ALGO_FOR_EACH(nums, vector_int_t, INC);
// Print the array after
printf ("after: ");
ALGO_FOR_EACH(nums, vector_int_t, M_PRINT, " ");
printf ("\n");
// Sum the elements of the array
int sum = 0;
ALGO_REDUCE(sum, nums, vector_int_t, add);
M_PRINT("sum = ", sum, "\n");
}
return 0;
}
+77
View File
@@ -0,0 +1,77 @@
#include "m-tuple.h"
#include "m-array.h"
#include "m-algo.h"
#include "m-funcobj.h"
#include "m-serial-json.h"
#include "m-string.h"
/* Define the employee */
TUPLE_DEF2(employee,
(name, string_t),
(age, int),
(idnum, int))
#define M_OPL_employee_t() TUPLE_OPLIST(employee, STRING_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
/* Define an array of employee and some algorithms on it */
ARRAY_DEF(vector_employee, employee_t)
#define M_OPL_vector_employee_t() ARRAY_OPLIST(vector_employee, M_OPL_employee_t())
ALGO_DEF(vector_employee, vector_employee_t)
/* Define the function object to select the sort order */
FUNC_OBJ_INS_DEF(compare_by,
vector_employee_cmp_obj,
(a, b), {
if (string_equal_str_p(self->sort_field, "name"))
return string_cmp(a->name, b->name);
else if (string_equal_str_p(self->sort_field, "age"))
return a->age < b->age ? -1 : a->age > b->age;
else
return a->idnum < b->idnum ? -1 : a->idnum > b->idnum;
},
(sort_field, string_t) )
#define M_OPL_compare_by_t() FUNC_OBJ_INS_OPLIST(compare_by, STRING_OPLIST)
// Let's read an array of person from the given JSON file
static void read(vector_employee_t base, const char filename[])
{
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 JSON serializer with this file
M_LET( (in,f), m_serial_json_read_t)
ret = vector_employee_in_serial(base, in);
// Let's handle errors
if (ret != M_SERIAL_OK_DONE) {
fprintf(stderr, "ERROR: Cannot read JSON data from file '%s'.\nParsing stops at:\n", filename);
while (!feof(f)) { fputc(fgetc(f), stdout); }
fclose(f);
exit(2);
}
fclose(f);
}
int main(void)
{
M_LET(emps, vector_employee_t) {
/* Code to populate database from JSON */
read(emps, "ex11-algo02.json");
// Sort the database by employee ID number
M_LET( (cmp, STRING_CTE("idnum")), compare_by_t )
vector_employee_sort_fo(emps, compare_by_as_interface(cmp));
printf("Employees sorted by idnum are: ");
vector_employee_out_str(stdout, emps);
M_LET( (cmp, STRING_CTE("name")), compare_by_t )
vector_employee_sort_fo(emps, compare_by_as_interface(cmp));
printf("\nEmployees sorted by name are: ");
vector_employee_out_str(stdout, emps);
printf("\n");
}
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
[
{ "name": "John Doe", "age": 32, "idnum": 14528},
{ "name": "Clint Smith", "age": 31, "idnum": 14529},
{ "name": "Linda Cameron", "age": 20, "idnum": 14527},
{ "name": "Cameron Carrey", "age": 25, "idnum": 14526},
{ "name": "Marty Brown", "age": 26, "idnum": 14525},
{ "name": "Donald Cow", "age": 45, "idnum": 14520}
]
+936 -372
View File
File diff suppressed because it is too large. Load diff
+424 -114
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic ARRAY module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,33 +22,52 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_ARRAY_H
#define __M_ARRAY_H
#ifndef MSTARLIB_ARRAY_H
#define MSTARLIB_ARRAY_H
#include "m-core.h"
/* Define an array of a given type.
/* 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__) \
((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))))
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))))
/* Define the oplist of an array of type.
/* 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_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
(__VA_ARGS__ )))
ARRAYI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
/********************************************************************************/
/********************************** INTERNAL ************************************/
/********************************************************************************/
#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
#define ARRAYI_OPLIST2(name, oplist) \
(INIT(M_C(name, _init)) \
/* Validation of the given oplist */
#define ARRAYI_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(ARRAYI_OPLIST_P3, ARRAYI_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define ARRAYI_OPLIST_FAILURE(name, oplist) \
M_LIB_ERROR(ARGUMENT_OF_ARRAY_OPLIST_IS_NOT_AN_OPLIST, name, oplist)
/* 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 _it_remove)? */
#define ARRAYI_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,INIT_MOVE(M_C(name, _init_move)) \
@@ -56,6 +75,7 @@
,SWAP(M_C(name, _swap)) \
,TYPE(M_C(name,_t)) \
,SUBTYPE(M_C(name, _type_t)) \
,TEST_EMPTY(M_C(name,_empty_p)) \
,IT_TYPE(M_C(name,_it_t)) \
,IT_FIRST(M_C(name,_it)) \
,IT_LAST(M_C(name,_it_last)) \
@@ -68,15 +88,30 @@
,IT_PREVIOUS(M_C(name,_previous)) \
,IT_REF(M_C(name,_ref)) \
,IT_CREF(M_C(name,_cref)) \
,IT_REMOVE(M_C(name,_remove)) \
,IT_INSERT(M_C(name,_insert)) \
,IT_REMOVE(M_C(name,_remove)) \
,CLEAN(M_C(name,_clean)) \
,KEY_TYPE(size_t) \
,VALUE_TYPE(M_C(name, _type_t)) \
,KEY_OPLIST(M_DEFAULT_OPLIST) \
,VALUE_OPLIST(oplist) \
,SET_KEY(M_C(name, _set_at)) \
,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)) \
,POP_MOVE(M_C(name,_pop_move)) \
,OPLIST(oplist) \
,M_IF_METHOD(CMP, oplist)(SORT(M_C(name, _special_sort)),) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
,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),) \
@@ -84,36 +119,59 @@
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* 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.
#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) \
/* Validate the oplist before going further */
#define ARRAYI_DEF_P2(name, type, oplist, array_t, it_t) \
M_IF_OPLIST(oplist)(ARRAYI_DEF_P3, ARRAYI_DEF_FAILURE)(name, type, oplist, array_t, it_t)
/* Stop processing with a compilation failure */
#define ARRAYI_DEF_FAILURE(name, type, oplist, array_t, it_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ARRAY_DEF): the given argument is not a valid oplist: " #oplist)
/* 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_P3(name, type, oplist, array_t, it_t) \
\
typedef struct M_C(name, _s) { \
size_t size, alloc; \
type *ptr; \
size_t size; /* Number of elements in 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); \
\
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]; \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(array_t v) \
{ \
assert (v != NULL); \
v->size = v->alloc = 0; \
v->ptr = NULL; \
/* Initialy, the array is empty with nothing allocated */ \
v->size = 0; \
v->alloc = 0; \
v->ptr = NULL; \
ARRAYI_CONTRACT(v); \
} \
\
@@ -122,7 +180,7 @@
{ \
ARRAYI_CONTRACT(v); \
for(size_t i = 0; i < v->size; i++) \
M_GET_CLEAR oplist(v->ptr[i]); \
M_CALL_CLEAR(oplist, v->ptr[i]); \
v->size = 0; \
ARRAYI_CONTRACT(v); \
} \
@@ -132,7 +190,7 @@
{ \
ARRAYI_CONTRACT(v); \
M_C(name, _clean)(v); \
M_GET_FREE oplist(v->ptr); \
M_CALL_FREE(oplist, v->ptr); \
v->alloc = 0; \
v->ptr = NULL; \
} \
@@ -145,7 +203,7 @@
if (M_UNLIKELY (d == s)) return; \
if (s->size > d->alloc) { \
const size_t alloc = s->size; \
type *ptr = M_GET_REALLOC oplist (type, d->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
@@ -155,11 +213,11 @@
} \
size_t i, step1 = M_MIN(s->size, d->size); \
for(i = 0; i < step1; i++) \
M_GET_SET oplist (d->ptr[i], s->ptr[i]); \
M_CALL_SET(oplist, d->ptr[i], s->ptr[i]); \
for( ; i < s->size; i++) \
M_GET_INIT_SET oplist (d->ptr[i], s->ptr[i]); \
M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]); \
for( ; i < d->size; i++) \
M_GET_CLEAR oplist (d->ptr[i]); \
M_CALL_CLEAR(oplist, d->ptr[i]); \
d->size = s->size; \
ARRAYI_CONTRACT(d); \
} \
@@ -180,7 +238,7 @@
d->size = s->size; \
d->alloc = s->alloc; \
d->ptr = s->ptr; \
s->size = s->alloc = 0; \
s->alloc = 0; \
s->ptr = NULL; \
ARRAYI_CONTRACT(d); \
} \
@@ -194,14 +252,14 @@
} \
\
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); \
M_GET_SET oplist(v->ptr[i], x); \
M_CALL_SET(oplist, v->ptr[i], x); \
} \
\
static inline const type * \
static inline type const * \
M_C(name, _back)(array_t v) \
{ \
ARRAYI_CONTRACT(v); \
@@ -215,13 +273,13 @@
ARRAYI_CONTRACT(v); \
if (M_UNLIKELY (v->size >= v->alloc)) { \
assert(v->size == v->alloc); \
size_t alloc = M_GET_INC_ALLOC oplist (v->alloc); \
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
if (M_UNLIKELY (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return NULL; \
} \
assert (alloc > v->size); \
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return NULL; \
@@ -233,6 +291,7 @@
type *ret = &v->ptr[v->size]; \
v->size++; \
ARRAYI_CONTRACT(v); \
M_ASSUME(ret != NULL); \
return ret; \
} \
\
@@ -240,19 +299,31 @@
M_C(name, _push_back)(array_t v, type const x) \
{ \
type *data = M_C(name, _push_raw)(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_GET_INIT_SET oplist(*data, x); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_CALL_INIT_SET(oplist, *data, x); \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline type * \
M_C(name, _push_new)(array_t v) \
{ \
type *data = M_C(name, _push_raw)(v); \
if (M_UNLIKELY (data == NULL) ) \
return NULL; \
M_GET_INIT oplist(*data); \
M_CALL_INIT(oplist, *data); \
return data; \
} \
, /* No INIT */ ) \
\
static inline void \
M_C(name, _push_move)(array_t v, type *x) \
{ \
assert (x != NULL); \
type *data = M_C(name, _push_raw)(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_DO_INIT_MOVE (oplist, *data, *x); \
} \
\
static inline void \
@@ -262,13 +333,13 @@
assert (key <= v->size); \
if (M_UNLIKELY (v->size >= v->alloc) ) { \
assert(v->size == v->alloc); \
size_t alloc = M_GET_INC_ALLOC oplist (v->alloc); \
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
if (M_UNLIKELY (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
} \
assert (alloc > v->size); \
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
@@ -279,10 +350,11 @@
assert(v->ptr != NULL); \
memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \
v->size++; \
M_GET_INIT_SET oplist (v->ptr[key], x); \
M_CALL_INIT_SET(oplist, v->ptr[key], x); \
ARRAYI_CONTRACT(v); \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _resize)(array_t v, size_t size) \
{ \
@@ -290,13 +362,13 @@
if (v->size > size) { \
/* Decrease size of array */ \
for(size_t i = size ; i < v->size; i++) \
M_GET_CLEAR oplist(v->ptr[i]); \
M_CALL_CLEAR(oplist, v->ptr[i]); \
v->size = size; \
} else if (v->size < size) { \
/* Increase size of array */ \
if (size > v->alloc) { \
size_t alloc = size ; \
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
@@ -305,11 +377,12 @@
v->alloc = alloc; \
} \
for(size_t i = v->size ; i < size; i++) \
M_GET_INIT oplist(v->ptr[i]); \
M_CALL_INIT(oplist, v->ptr[i]); \
v->size = size; \
} \
ARRAYI_CONTRACT(v); \
} \
, /* No INIT */ ) \
\
static inline void \
M_C(name, _reserve)(array_t v, size_t alloc) \
@@ -320,11 +393,11 @@
alloc = v->size; \
} \
if (M_UNLIKELY (alloc == 0)) { \
M_GET_FREE oplist (v->ptr); \
M_CALL_FREE(oplist, v->ptr); \
v->size = v->alloc = 0; \
v->ptr = NULL; \
} else { \
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
@@ -335,49 +408,76 @@
ARRAYI_CONTRACT(v); \
} \
\
static inline void \
M_C(name, _set_at2)(array_t v, size_t idx, type x) \
M_IF_METHOD(INIT, oplist)( \
static inline type * \
M_C(name, _get_at)(array_t v, size_t idx) \
{ \
ARRAYI_CONTRACT(v); \
const size_t size = idx + 1; \
/* NOTE: set at and resize if needed */ \
/* resize if needed */ \
if (v->size <= size) { \
/* Increase size of array */ \
if (M_UNLIKELY (size > v->alloc) ) { \
size_t alloc = M_GET_INC_ALLOC oplist (size) ; \
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
/* In case of overflow of size_t */ \
if (M_UNLIKELY (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
return NULL; \
} \
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
return NULL; \
} \
v->ptr = ptr; \
v->alloc = alloc; \
} \
for(size_t i = v->size ; i < size; i++) \
M_GET_INIT oplist(v->ptr[i]); \
M_CALL_INIT(oplist, v->ptr[i]); \
v->size = size; \
} \
assert (idx < v->size); \
M_GET_SET oplist(v->ptr[idx], x); \
ARRAYI_CONTRACT(v); \
return &v->ptr[idx]; \
} \
, /* No INIT */) \
\
static inline void \
M_C(name, _pop_back)(type *dest, array_t v) \
{ \
ARRAYI_CONTRACT(v); \
assert (v->size > 0 && v->ptr != NULL); \
v->size--; \
if (dest) \
if (dest) { \
M_DO_MOVE (oplist, *dest, v->ptr[v->size]); \
else \
M_GET_CLEAR oplist (v->ptr[v->size]); \
} else { \
M_CALL_CLEAR(oplist, v->ptr[v->size]); \
} \
ARRAYI_CONTRACT(v); \
} \
\
static inline void \
M_C(name, _pop_move)(type *dest, array_t v) \
{ \
ARRAYI_CONTRACT(v); \
assert (v->size > 0 && v->ptr != NULL); \
assert (dest != NULL); \
v->size--; \
M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
ARRAYI_CONTRACT(v); \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _pop_until)(array_t v, it_t pos) \
{ \
ARRAYI_CONTRACT(v); \
assert (v == pos->array); \
M_ASSUME (pos->index <= v->size); \
M_C(name, _resize)(v, pos->index); \
} \
, /* No INIT */ ) \
\
static inline bool \
M_C(name, _empty_p)(const array_t v) \
{ \
@@ -404,28 +504,47 @@
{ \
ARRAYI_CONTRACT(v); \
assert (v->size > 0 && v->ptr != NULL); \
assert (i < v->size); \
if (dest) \
M_DO_MOVE (oplist, *dest, v->ptr[i]); \
else \
M_GET_CLEAR oplist (v->ptr[i]); \
M_CALL_CLEAR(oplist, v->ptr[i]); \
memmove(&v->ptr[i], &v->ptr[i+1], sizeof(type)*(v->size - i) ); \
v->size--; \
ARRAYI_CONTRACT(v); \
} \
\
static inline bool \
M_C(name, _erase)(array_t a, size_t i) \
{ \
ARRAYI_CONTRACT(a); \
if (i >= a->size) return false; \
M_C(name, _pop_at)(NULL, a, i); \
return true; \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _insert_v)(array_t v, size_t i, size_t num) \
{ \
ARRAYI_CONTRACT(v); \
assert(i <= v->size); \
size_t size = v->size + 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_GET_INC_ALLOC oplist (size) ; \
if (M_UNLIKELY (alloc <= v->alloc)) { \
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
if (M_UNLIKELY (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
} \
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
@@ -435,10 +554,11 @@
} \
memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) ); \
for(size_t k = i ; k < i+num; k++) \
M_GET_INIT oplist(v->ptr[k]); \
M_CALL_INIT(oplist, v->ptr[k]); \
v->size = size; \
ARRAYI_CONTRACT(v); \
} \
, /* No INIT */) \
\
static inline void \
M_C(name, _remove_v)(array_t v, size_t i, size_t j) \
@@ -446,7 +566,7 @@
ARRAYI_CONTRACT(v); \
assert(i < v->size && j <= v->size && i < j && v->ptr != NULL); \
for(size_t k = i ; k < j; k++) \
M_GET_CLEAR oplist(v->ptr[k]); \
M_CALL_CLEAR(oplist, v->ptr[k]); \
memmove(&v->ptr[i], &v->ptr[j], sizeof(type)*(v->size - j) ); \
v->size -= (j-i); \
ARRAYI_CONTRACT(v); \
@@ -476,16 +596,9 @@
ARRAYI_CONTRACT(v); \
assert(i < v->size && j < v->size && v->ptr != NULL); \
type tmp; \
M_IF_METHOD(INIT_MOVE, oplist) ( \
M_GET_INIT_MOVE oplist (tmp, v->ptr[i]); \
M_GET_INIT_MOVE oplist (v->ptr[i], v->ptr[j]); \
M_GET_INIT_MOVE oplist (v->ptr[j], tmp); \
, \
M_GET_INIT_SET oplist (tmp, v->ptr[i]); \
M_GET_SET oplist(v->ptr[i], v->ptr[j]); \
M_GET_SET oplist(v->ptr[j], tmp); \
M_GET_CLEAR oplist(tmp); \
) /* IF INIT_MOVE method */ \
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); \
} \
\
@@ -497,7 +610,7 @@
return &v->ptr[i]; \
} \
\
static inline const type * \
static inline type const * \
M_C(name, _cget)(const array_t v, size_t i) \
{ \
ARRAYI_CONTRACT(v); \
@@ -505,8 +618,16 @@
return M_CONST_CAST(type, &v->ptr[i]); \
} \
\
static inline type const * \
M_C(name, _front)(const array_t v) \
{ \
ARRAYI_CONTRACT(v); \
assert (v->size > 0); \
return M_C(name, _cget)(v, 0); \
} \
\
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); \
@@ -515,7 +636,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); \
@@ -524,7 +645,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); \
@@ -533,7 +654,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; \
@@ -542,14 +663,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 \
@@ -558,22 +679,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 \
@@ -582,28 +703,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 const type * \
M_C(name, _cref)(const array_it_t it) \
static inline type const * \
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); \
@@ -613,7 +734,7 @@
M_IF_METHOD(CMP, oplist) \
( \
static inline void M_C(name, _special_sort)(array_t l, \
int (*func_type) (const type *a, const type *b)) \
int (*func_type) (type const *a, type const *b)) \
{ \
/* Using qsort is more compact but slower than a full templated \
version which can be twice faster */ \
@@ -622,27 +743,117 @@
func_void = (int (*)(const void*, const void*))func_type; \
qsort (l->ptr, l->size, sizeof(type), func_void); \
} \
,) /* IF CMP oplist */ \
\
M_IF_METHOD(SWAP, oplist)( \
static inline void \
M_C(name, _special_stable_sort_noalloc) (type tab[], size_t size, type tmp[]) \
{ \
size_t th = 4; \
M_IF_DEBUG(type *org_tab = tab;) \
assert (size > 1); \
/* Let's select the threshold of the pass 1 to be sure \
the final result is in tab.*/ \
if (m_core_clz64(size-1) & 1) \
th += th; \
\
/* Pass 1: insertion sort (stable) */ \
for(size_t k = 0 ; k < size; ) { \
size_t max = size - k < 2*th ? size - k : th; \
M_ASSUME(max >= th); \
for(size_t i = 1; i < max; i++) { \
size_t j = i; \
while (j > 0 && M_CALL_CMP(oplist, tab[k+j-1], tab[k+j]) > 0) { \
M_CALL_SWAP(oplist, tab[k+j-1], tab[k+j]); \
j = j - 1; \
} \
} \
k += max; \
} \
\
/* N Pass of merge */ \
while (th < size) { \
type *dest = tmp; \
/* Pass n: Merge */ \
for(size_t k = 0 ; k < size; ) { \
type *el1 = &tab[k]; \
type *el2 = &tab[k+th]; \
size_t n1 = th; \
size_t n2 = size-k <= 3*th ? size-k-th : th; \
assert (size-k > th); \
assert (0 < n1 && n1 <= size); \
assert (0 < n2 && n2 <= size); \
k += n1+n2; \
for (;;) { \
if (M_CALL_CMP(oplist, *el1, *el2) <= 0) { \
M_CALL_SET(oplist, *dest, *el1); \
dest++; \
el1++; \
if (-- n1 == 0) { \
if (n2 > 0) { \
memcpy (dest, el2, n2 * sizeof (type)); \
dest += n2; \
} \
break; \
} \
} else { \
M_CALL_SET(oplist, *dest, *el2); \
dest++; \
el2++; \
if (-- n2 == 0) { \
if (n1 > 0) { \
memcpy (dest, el1, n1 * sizeof (type)); \
dest += n1; \
} \
break; \
} \
} \
} \
} \
/* Swap t & tab */ \
M_SWAP(type *, tab, tmp); \
/* Increase th for next pass */ \
th += th; \
} \
assert (org_tab == tab); \
} \
\
static inline void \
M_C(name, _special_stable_sort)(array_t l) \
{ \
if (M_UNLIKELY (l->size < 2)) \
return; \
type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \
if (temp == NULL) { \
M_MEMORY_FULL(sizeof (type) * l->size); \
return ; \
} \
M_C(name, _special_stable_sort_noalloc)(l->ptr, l->size, temp); \
M_CALL_FREE(oplist, temp); \
} \
,) /* IF SWAP method */ \
\
,) /* IF CMP oplist */ \
\
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, array_t array, \
M_C(name, _get_str)(string_t str, array_t const array, \
bool append) \
{ \
STRING_CONTRACT(str); \
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)){ \
const type *item = M_C(name, _cref)(it); \
M_GET_GET_STR oplist (str, *item, true); \
type const *item = M_C(name, _cref)(it); \
M_CALL_GET_STR(oplist, str, *item, true); \
if (!M_C(name, _last_p)(it)) \
string_push_back (str, M_GET_SEPARATOR oplist); \
} \
string_push_back (str, ']'); \
STRING_CONTRACT(str); \
STRINGI_CONTRACT(str); \
} \
, /* no GET_STR */ ) \
\
@@ -654,8 +865,8 @@
assert (file != NULL); \
fputc ('[', file); \
for (size_t i = 0; i < array->size; i++) { \
const type *item = M_C(name, _cget)(array, i); \
M_GET_OUT_STR oplist (file, *item); \
type const *item = M_C(name, _cget)(array, i); \
M_CALL_OUT_STR(oplist, file, *item); \
if (i != array->size-1) \
fputc (M_GET_SEPARATOR oplist, file); \
} \
@@ -663,7 +874,39 @@
} \
, /* no OUT_STR */ ) \
\
M_IF_METHOD(IN_STR, oplist)( \
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
static inline bool \
M_C(name, _parse_str)(array_t array, const char str[], const char**endp) \
{ \
ARRAYI_CONTRACT(array); \
assert (str != NULL); \
M_C(name,_clean)(array); \
bool success = false; \
int c = *str++; \
if (M_UNLIKELY (c != '[')) goto exit; \
c = *str++; \
if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \
if (M_UNLIKELY (c == 0)) goto exit; \
str--; \
type item; \
M_CALL_INIT(oplist, item); \
do { \
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
do { c = *str++; } while (isspace(c)); \
if (b == false || c == 0) { goto exit_clear; } \
M_C(name, _push_back)(array, item); \
} while (c == M_GET_SEPARATOR oplist); \
ARRAYI_CONTRACT(array); \
success = (c == ']'); \
exit_clear: \
M_CALL_CLEAR(oplist, item); \
exit: \
if (endp) *endp = str; \
return success; \
} \
, /* no PARSE_STR & INIT */ ) \
\
M_IF_METHOD2(IN_STR, INIT, oplist)( \
static inline bool \
M_C(name, _in_str)(array_t array, FILE *file) \
{ \
@@ -677,18 +920,64 @@
if (M_UNLIKELY (c == EOF)) return false; \
ungetc(c, file); \
type item; \
M_GET_INIT oplist (item); \
M_CALL_INIT(oplist, item); \
do { \
bool b = M_GET_IN_STR oplist (item, file); \
c = fgetc(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_back)(array, item); \
} while (c == M_GET_SEPARATOR oplist); \
M_GET_CLEAR oplist (item); \
M_CALL_CLEAR(oplist, item); \
ARRAYI_CONTRACT(array); \
return c == ']'; \
} \
, /* no IN_STR */ ) \
, /* 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 \
@@ -700,9 +989,9 @@
if (array1->size != array2->size) return false; \
size_t i; \
for(i = 0; i < array1->size; i++) { \
const type *item1 = M_C(name, _cget)(array1, i); \
const type *item2 = M_C(name, _cget)(array2, i); \
bool b = M_GET_EQUAL oplist (*item1, *item2); \
type const *item1 = M_C(name, _cget)(array1, i); \
type const *item2 = M_C(name, _cget)(array2, i); \
bool b = M_CALL_EQUAL(oplist, *item1, *item2); \
if (!b) return false; \
} \
return i == array1->size; \
@@ -716,11 +1005,32 @@
ARRAYI_CONTRACT(array); \
M_HASH_DECL(hash); \
for(size_t i = 0 ; i < array->size; i++) { \
size_t hi = M_GET_HASH oplist (array->ptr[i]); \
size_t hi = M_CALL_HASH(oplist, array->ptr[i]); \
M_HASH_UP(hash, hi); \
} \
return M_HASH_FINAL (hash); \
} \
, /* no HASH */ ) \
\
static inline void \
M_C(name, _splice)(array_t a1, array_t a2) \
{ \
ARRAYI_CONTRACT(a1); \
ARRAYI_CONTRACT(a2); \
if (M_LIKELY (a2->size > 0)) { \
size_t newSize = a1->size + a2->size; \
if (newSize > a1->alloc) { \
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \
if (M_UNLIKELY (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * newSize); \
} \
a1->ptr = ptr; \
a1->alloc = newSize; \
} \
memcpy(&a1->ptr[a1->size], &a2->ptr[0], a2->size * sizeof (type)); \
a2->size = 0; \
a1->size = newSize; \
} \
} \
#endif
+100 -52
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin stdatomic wrapper for C++ compatibility
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,8 +22,8 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_ATOMIC_H
#define __M_ATOMIC_H
#ifndef MSTARLIB_ATOMIC_H
#define MSTARLIB_ATOMIC_H
/* NOTE: Due to the C++ not having recognized stdatomic.h officialy,
it is hard to use this header directly with a C++ compiler like
@@ -89,16 +89,41 @@ using std::atomic_flag_test_and_set_explicit;
using std::atomic_flag_clear;
using std::atomic_flag_clear_explicit;
using std::memory_order_relaxed;
using std::memory_order_consume;
using std::memory_order_acquire;
using std::memory_order_release;
using std::memory_order_acq_rel;
using std::memory_order_seq_cst;
#define _Atomic(T) std::atomic< T >
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|| ( defined(__GNUC__) && !defined(__clang__) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|| (defined(__clang__) && __clang_major__ >= 4)
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|| ( defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|| (defined(__clang__) && __clang_major__ >= 4) \
|| (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1800)
/* CLANG 3.5 has issues with GCC's stdatomic.h
and doesn't provide its own stdatomic.h */
/* C11 has mandatory working stdatomic
STDATOMIC doesn't work with C++ except for clang
GCC < 4.9 doesn't provide a compliant stdatomic.h
CLANG 3.5 has issues with GCC's stdatomic.h
and doesn't provide its own stdatomic.h
ICC < 18 doesn't provide a compliant 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.
@@ -110,21 +135,24 @@ using std::atomic_flag_clear_explicit;
*/
#include "m-mutex.h"
/* The structure is quite large:
__val : value of the atomic type,
__zero : zero value of the atomic type (constant),
__previous: temporary value used within the mutex lock,
__lock : the mutex lock.
/* _Atomic qualifier for a type (emulation).
The structure is quite large:
_val : value of the atomic type,
_zero : zero value of the atomic type (constant),
_previous: temporary value used within the mutex lock,
_lock : the mutex lock.
Support up to sizeof (long long) type.
*/
#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 memory order is defined, only the strongest constraint is used */
/* Define the supported memory order.
Even if memory order is defined, only the strongest constraint is used */
typedef enum {
memory_order_relaxed,
memory_order_consume,
@@ -152,7 +180,7 @@ typedef _Atomic(size_t) atomic_size_t;
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
/* Define the minimum size supported by the architecture
for atomic read or write.
for an atomic read or write.
This can help a lot since it avoids locking for atomic_load and
atomic_store.
*/
@@ -168,74 +196,94 @@ typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
#if (defined (INTMAX_C) && defined (UINTMAX_C) && !defined(__cplusplus)) || \
defined (_STDINT_H) || defined (_STDINT_H_) || defined (_STDINT) || \
defined (_SYS_STDINT_H_)
/* Define additional atomic types */
typedef _Atomic(intmax_t) atomic_intmax_t;
typedef _Atomic(uintmax_t) atomic_uintmax_t;
#endif
/* Unlock the mutex and return the given value */
/* (INTERNAL) Unlock the mutex and return the given value */
static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
{
m_mutex_unlock (*lock);
return val;
}
/* This is the heart of the wrapper:
/* (INTERNAL) This is the heart of the wrapper:
lock the atomic value, read it and returns the value.
In order to avoid any compiler extension, we need to transform the
atomic type into 'long long' then convert it back to its value.
This is because __previous can't be read after the lock, and we can't
This is because _previous can't be read after the lock, and we can't
generate temporary variable within a macro.
The trick is computing _val - _zero within the lock, then
returns retvalue + _zero after the lock.
returns retvalue + _zero after the release of the lock.
*/
#define atomic_fetch_op(ptr, val, op) \
(m_mutex_lock((ptr)->__lock), \
(ptr)->__previous = (ptr)->__val, \
(ptr)->__val op (val), \
atomic_fetch_unlock(&(ptr)->__lock, (ptr)->__previous-(ptr)->__zero)+(ptr)->__zero)
(m_mutex_lock((ptr)->_lock), \
(ptr)->_previous = (ptr)->_val, \
(ptr)->_val op (val), \
atomic_fetch_unlock(&(ptr)->_lock, (ptr)->_previous-(ptr)->_zero)+(ptr)->_zero)
/* Perform an atomic add (EMULATION) */
#define atomic_fetch_add(ptr, val) atomic_fetch_op(ptr, val, +=)
/* Perform an atomic sub (EMULATION) */
#define atomic_fetch_sub(ptr, val) atomic_fetch_op(ptr, val, -=)
/* Perform an atomic or (EMULATION) */
#define atomic_fetch_or(ptr, val) atomic_fetch_op(ptr, val, |=)
/* Perform an atomic xor (EMULATION) */
#define atomic_fetch_xor(ptr, val) atomic_fetch_op(ptr, val, ^=)
/* Perform an atomic and (EMULATION) */
#define atomic_fetch_and(ptr, val) atomic_fetch_op(ptr, val, &=)
/* Perform an atomic exchange (EMULATION) */
#define atomic_exchange(ptr, val) atomic_fetch_op(ptr, val, =)
#define ATOMIC_VAR_INIT(val) { val, 0, 0, 0, M_MUTEXI_INIT_VALUE }
/* Initialize an atomic GLOBAL variable */
#define ATOMIC_VAR_INIT(val) { val, 0, 0, M_MUTEXI_INIT_VALUE }
/* Initialize an atomic variable */
#define atomic_init(ptr, val) \
(m_mutex_init((ptr)->__lock), (ptr)->__val = val, (ptr)->__zero = 0)
(m_mutex_init((ptr)->_lock), (ptr)->_val = val, (ptr)->_zero = 0)
/* (INTERNAL) Load an atomic variable within a lock
(needed for variable greater than CPU atomic size) */
#define atomic_load_lock(ptr) \
(m_mutex_lock((ptr)->__lock), \
(ptr)->__previous = (ptr)->__val, \
atomic_fetch_unlock(&(ptr)->__lock, (ptr)->__previous-(ptr)->__zero)+(ptr)->__zero)
(m_mutex_lock((ptr)->_lock), \
(ptr)->_previous = (ptr)->_val, \
atomic_fetch_unlock(&(ptr)->_lock, (ptr)->_previous-(ptr)->_zero)+(ptr)->_zero)
/* (INTERNAL) Store an atomic variable within a lock
(needed for variable greater than CPU atomic size) */
#define atomic_store_lock(ptr, val) \
(m_mutex_lock((ptr)->__lock), \
(ptr)->__val = (val), \
m_mutex_unlock((ptr)->__lock))
(m_mutex_lock((ptr)->_lock), \
(ptr)->_val = (val), \
m_mutex_unlock((ptr)->_lock))
/* Atomic load of a variable (EMULATION)
If the atomic type size is not greater than the CPU atomic size,
we can perform a direct read of the variable (much faster) */
#define atomic_load(ptr) \
( sizeof ((ptr)->__val) <= ATOMICI_MIN_RW_SIZE \
? (ptr)->__val \
( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE \
? (ptr)->_val \
: atomic_load_lock(ptr))
/* Atomic store of a variable (EMULATION)
If the atomic type size is not greater than the CPU atomic size,
we can perform a direct write of the variable (much faster) */
#define atomic_store(ptr, val) do { \
if ( sizeof ((ptr)->__val) <= ATOMICI_MIN_RW_SIZE) { \
(ptr)->__val = (val); \
if ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE) { \
(ptr)->_val = (val); \
} else { \
long long _offset = (val) - (ptr)->__zero; \
atomic_store_lock(ptr, (ptr)->__zero + _offset); \
long long _offset = (val) - (ptr)->_zero; \
atomic_store_lock(ptr, (ptr)->_zero + _offset); \
} \
} while (0)
#define atomic_compare_exchange_strong(ptr, exp, val) \
(m_mutex_lock((ptr)->__lock), \
atomic_fetch_unlock(&(ptr)->__lock, \
(ptr)->__val == *(exp) \
? ((ptr)->__val = (val), true) \
: (*(exp) = (ptr)->__val, false)))
/* Perform a CAS (Compare and swap) operation (EMULATION) */
#define atomic_compare_exchange_strong(ptr, exp, val) \
(m_mutex_lock((ptr)->_lock), \
atomic_fetch_unlock(&(ptr)->_lock, \
(ptr)->_val == *(exp) \
? ((ptr)->_val = (val), true) \
: (*(exp) = (ptr)->_val, false)))
#define atomic_fetch_add_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, +=)
@@ -250,11 +298,11 @@ static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
#define atomic_thread_fence(mem) (void) 0
#define atomic_signal_fence(mem) (void) 0
#define atomic_is_lock_free(ptr) false
#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem) atomic_compare_exchange_strong(ptr, exp, val)
#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem) atomic_compare_exchange_strong(ptr, exp, val)
#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val)
#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val)
#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val)
#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val)
/* TODO: Missing atomic_flag. Problem: it needs to be lock free! */
/* TODO: Missing atomic_flag. Problem: it is supposed to be lock free! */
#endif
+100 -44
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - BITSET module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,38 +22,29 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_BITSET_H
#define __M_BITSET_H
#ifndef MSTARLIB_BITSET_H
#define MSTARLIB_BITSET_H
#include <stdint.h>
#include "m-core.h"
/********************************** INTERNAL ************************************/
// Define a limb of a bitset
typedef unsigned long bitset_limb;
// 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_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
#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 2 ? 4 : (n) * 2)
// 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)); \
@@ -65,12 +56,28 @@ 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)
{
assert (t != NULL);
assert (M_POWEROF2_P(BITSET_LIMB_BIT));
t->size = t->alloc = 0;
t->size = 0;
t->alloc = 0;
t->ptr = NULL;
BITSETI_CONTRACT(t);
}
@@ -145,11 +152,11 @@ bitset_set_at(bitset_t v, size_t i, bool x)
{
BITSETI_CONTRACT(v);
assert (i < v->size && v->ptr != NULL);
size_t offset = i / BITSET_LIMB_BIT;
size_t index = i % BITSET_LIMB_BIT;
// This is a branchless version as x can only be 0 or 1.
v->ptr[offset] &= ~(((bitset_limb)1)<<index);
v->ptr[offset] |= ((bitset_limb)x)<<index;
const size_t offset = i / BITSET_LIMB_BIT;
const size_t index = i % BITSET_LIMB_BIT;
// This is a branchless version as x can only be 0 or 1 with only one variable shift.
const bitset_limb mask = ((bitset_limb)1)<<index;
v->ptr[offset] = (v->ptr[offset] & ~mask) | (mask & (-(bitset_limb)x));
BITSETI_CONTRACT (v);
}
@@ -183,6 +190,7 @@ bitset_push_back (bitset_t v, bool x)
return;
}
// Clear allocated memory
// FIXME: Why?
for(size_t a = v->alloc; a < needAlloc; a++)
ptr[a] = 0;
v->ptr = ptr;
@@ -272,6 +280,14 @@ bitset_pop_back(bool *dest, bitset_t v)
BITSETI_CONTRACT (v);
}
static inline bool
bitset_front(bitset_t v)
{
BITSETI_CONTRACT (v);
assert(v->size > 0);
return bitset_get(v, 0);
}
static inline bool
bitset_back(bitset_t v)
{
@@ -407,7 +423,7 @@ bitset_equal_p (const bitset_t set1, const bitset_t set2)
/* Compare the last limb if needed */
const size_t index = set1->size % BITSET_LIMB_BIT;
if (index > 0) {
const size_t mask = (((bitset_limb)1) << index) - 1;
const bitset_limb mask = (((bitset_limb)1) << index) - 1;
if ((set1->ptr[limbSize] & mask) != (set2->ptr[limbSize] & mask))
return false;
}
@@ -523,22 +539,32 @@ bitset_in_str(bitset_t set, FILE *file)
}
static inline bool
bitset_set_str(bitset_t set, const char str[])
bitset_parse_str(bitset_t set, const char str[], const char **endptr)
{
BITSETI_CONTRACT (set);
assert(str != NULL);
bool success = false;
bitset_clean(set);
char c = *str++;
if (c != '[') return false;
if (c != '[') goto exit;
c = *str++;
do {
if (c != '0' && c != '1') return false;
if (c != '0' && c != '1') goto exit;
const bool b = (c == '1');
bitset_push_back (set, b);
c = *str++;
} while (c != ']' && c != 0);
BITSETI_CONTRACT (set);
return c == ']';
success = (c == ']');
exit:
if (endptr) *endptr = str;
return success;
}
static inline bool
bitset_set_str(bitset_t dest, const char str[])
{
return bitset_parse_str(dest, str, NULL);
}
static inline void
@@ -552,7 +578,7 @@ bitset_and(bitset_t dest, const bitset_t src)
for(size_t i = 0 ; i < n; i++)
dest->ptr[i] &= src->ptr[i];
if (m) {
size_t mask = (((bitset_limb)1) << m) - 1;
bitset_limb mask = (((bitset_limb)1) << m) - 1;
dest->ptr[n] = (dest->ptr[n] & src->ptr[n]) & mask;
}
dest->size = s;
@@ -570,7 +596,7 @@ bitset_or(bitset_t dest, const bitset_t src)
for(size_t i = 0 ; i < n; i++)
dest->ptr[i] |= src->ptr[i];
if (m) {
size_t mask = (((bitset_limb)1) << m) - 1;
bitset_limb mask = (((bitset_limb)1) << m) - 1;
dest->ptr[n] = (dest->ptr[n] | src->ptr[n]) & mask;
}
dest->size = s;
@@ -588,7 +614,7 @@ bitset_xor(bitset_t dest, const bitset_t src)
for(size_t i = 0 ; i < n; i++)
dest->ptr[i] ^= src->ptr[i];
if (m) {
size_t mask = (((bitset_limb)1) << m) - 1;
bitset_limb mask = (((bitset_limb)1) << m) - 1;
dest->ptr[n] = (dest->ptr[n] ^ src->ptr[n]) & mask;
}
dest->size = s;
@@ -605,7 +631,7 @@ bitset_not(bitset_t dest)
for(size_t i = 0 ; i < n; i++)
dest->ptr[i] = ~ (dest->ptr[i]);
if (m) {
size_t mask = (((bitset_limb)1) << m) - 1;
bitset_limb mask = (((bitset_limb)1) << m) - 1;
dest->ptr[n] = (~ dest->ptr[n]) & mask;
}
dest->size = s;
@@ -613,25 +639,51 @@ bitset_not(bitset_t dest)
}
static inline size_t
bitset_hash(const bitset_t dest)
bitset_hash(const bitset_t set)
{
BITSETI_CONTRACT(dest);
size_t s = dest->size;
BITSETI_CONTRACT(set);
size_t s = set->size;
size_t n = s / BITSET_LIMB_BIT;
size_t m = s % BITSET_LIMB_BIT;
M_HASH_DECL(hash);
for(size_t i = 0 ; i < n; i++)
M_HASH_UP(hash, dest->ptr[i]);
M_HASH_UP(hash, set->ptr[i]);
if (m) {
size_t mask = (((bitset_limb)1) << m) - 1;
M_HASH_UP(hash, (dest->ptr[n] & mask));
bitset_limb mask = (((bitset_limb)1) << m) - 1;
M_HASH_UP(hash, (set->ptr[n] & mask));
}
return M_HASH_FINAL (hash);
}
static inline size_t
bitset_clz(const bitset_t set)
{
BITSETI_CONTRACT(set);
size_t s = set->size;
assert (s > 0);
size_t n = (s -1) / BITSET_LIMB_BIT;
size_t m = s % BITSET_LIMB_BIT;
bitset_limb limb = set->ptr[n];
if (m) {
bitset_limb mask = (((bitset_limb)1) << m) - 1;
limb &= mask;
} else {
m = BITSET_LIMB_BIT;
}
s = 0;
while (limb == 0 && n > 0) {
s += m;
limb = set->ptr[--n];
m = BITSET_LIMB_BIT;
}
s += m_core_clz64(limb) - (BITSET_LIMB_BIT - m);
return s;
}
#define BITSET_OPLIST \
(INIT(bitset_init) \
,INIT_SET(bitset_init_set) \
,INIT_WITH(API_1(M_INIT_VAI)) \
,SET(bitset_set) \
,CLEAR(bitset_clear) \
,INIT_MOVE(bitset_init_move) \
@@ -639,6 +691,8 @@ bitset_hash(const bitset_t dest)
,SWAP(bitset_swap) \
,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) \
@@ -656,10 +710,12 @@ bitset_hash(const bitset_t dest)
,HASH(bitset_hash) \
,GET_STR(bitset_get_str) \
,OUT_STR(bitset_out_str) \
,PARSE_STR(bitset_parse_str) \
,IN_STR(bitset_in_str) \
,EQUAL(bitset_equal_p) \
)
/* Register the OPLIST as a global one */
#define M_OPL_bitset_t() BITSET_OPLIST
// TODO: set_at2, insert_v, remove_v
@@ -667,9 +723,9 @@ bitset_hash(const bitset_t dest)
#endif
// NOTE: Define this function only if m-string has been included
#ifndef __M_BITSET_STRING_H
#ifdef __M_STRING_H
#define __M_BITSET_STRING_H
#ifndef MSTARLIB_BITSET_STRING_H
#ifdef MSTARLIB_STRING_H
#define MSTARLIB_BITSET_STRING_H
static inline void
bitset_get_str(string_t str, const bitset_t set, bool append)
+1383
View File
File diff suppressed because it is too large. Load diff
+719 -154
View File
File diff suppressed because it is too large. Load diff
+826
View File
@@ -0,0 +1,826 @@
/*
* M*LIB - Concurrent memory pool allocator
*
* Copyright (c) 2017-2020, 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
+810
View File
@@ -0,0 +1,810 @@
/*
* M*LIB - Basic Protected Concurrent module over container.
*
* Copyright (c) 2017-2020, 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_H
#define MSTARLIB_CONCURRENT_H
#include "m-core.h"
#include "m-mutex.h"
#include "m-atomic.h"
/* 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_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_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,
so that all arguments are evaluated before further expansion */
#define CONCURRENTI_OPLIST_P1(arg) CONCURRENTI_OPLIST_P2 arg
/* Validation of the given oplist */
#define CONCURRENTI_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(CONCURRENTI_OPLIST_P3, CONCURRENTI_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define CONCURRENTI_OPLIST_FAILURE(name, oplist) \
M_LIB_ERROR(ARGUMENT_OF_CONCURRENT_OPLIST_IS_NOT_AN_OPLIST, name, oplist)
/* 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_OPLIST_P3(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)),) \
,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_C(name, _clear)),) \
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_C(name, _init_move)),) \
,M_IF_METHOD(MOVE, oplist)(MOVE(M_C(name, _move)),) \
,M_IF_METHOD(SWAP,oplist)(SWAP(M_C(name, _swap)),) \
,TYPE(M_C(name,_t)) \
,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),) \
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_OPLIST(M_GET_KEY_OPLIST oplist),) \
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_OPLIST(M_GET_VALUE_OPLIST oplist), ) \
,M_IF_METHOD(SET_KEY, oplist)(SET_KEY(M_C(name, _set_at)),) \
,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_C(name, _erase)),) \
,M_IF_METHOD(PUSH, oplist)(PUSH(M_C(name,_push)),) \
,M_IF_METHOD(POP, oplist)(POP(M_C(name,_pop)),) \
,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_C(name,_push_move)),) \
,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_C(name,_pop_move)),) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
,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),) \
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
,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) != NULL); \
} while (0)
/* Deferred evaluation for the concurrent definition,
so that all arguments are evaluated before further expansion */
#define CONCURRENTI_DEF_P1(arg) CONCURRENTI_DEF_P2 arg
/* Validate the value oplist before going further */
#define CONCURRENTI_DEF_P2(name, type, oplist, concurrent_t) \
M_IF_OPLIST(oplist)(CONCURRENTI_DEF_P3, CONCURRENTI_DEF_FAILURE)(name, type, oplist, concurrent_t)
/* Stop processing with a compilation failure */
#define CONCURRENTI_DEF_FAILURE(name, type, oplist, concurrent_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
/* 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_P3(name, type, oplist, concurrent_t) \
\
typedef struct M_C(name, _s) { \
struct M_C(name, _s) *self; \
m_mutex_t lock; \
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); \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
/* Define the lock strategy (global & shared lock) */ \
static inline void \
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); \
} \
,) \
\
M_IF_METHOD(INIT_SET, oplist)( \
static inline void \
M_C(name, _init_set)(concurrent_t out, concurrent_t const src) \
{ \
CONCURRENTI_CONTRACT(src); \
assert (out != src); \
M_C(name, _internal_init)(out); \
M_C(name, _read_lock)(src); \
M_CALL_INIT_SET(oplist, out->data, src->data); \
M_C(name, _read_unlock)(src); \
CONCURRENTI_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(SET, oplist)( \
static inline void \
M_C(name, _set)(concurrent_t out, concurrent_t const src) \
{ \
CONCURRENTI_CONTRACT(out); \
if (out == src) return; \
if (out < src) { \
M_C(name, _write_lock)(out); \
M_C(name, _read_lock)(src); \
} else { \
M_C(name, _read_lock)(src); \
M_C(name, _write_lock)(out); \
} \
M_CALL_SET(oplist, out->data, src->data); \
if (out < src) { \
M_C(name, _read_lock)(src); \
M_C(name, _write_unlock)(out); \
} else { \
M_C(name, _write_unlock)(out); \
M_C(name, _read_unlock)(src); \
} \
CONCURRENTI_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(CLEAR, oplist)( \
static inline void \
M_C(name, _clear)(concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
/* No need to lock */ \
M_CALL_CLEAR(oplist, out->data); \
M_C(name, _internal_clear)(out); \
} \
,) \
\
M_IF_METHOD(INIT_MOVE, oplist)( \
static inline void \
M_C(name, _init_move)(concurrent_t out, concurrent_t src) \
{ \
CONCURRENTI_CONTRACT(src); \
assert (out != src); \
/* No need to lock 'src' ? */ \
M_C(name, _internal_init)(out); \
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
M_C(name, _internal_clear)(src); \
CONCURRENTI_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(MOVE, oplist)( \
static inline void \
M_C(name, _move)(concurrent_t out, concurrent_t src) \
{ \
CONCURRENTI_CONTRACT(out); \
CONCURRENTI_CONTRACT(src); \
/* No need to lock 'src' ? */ \
M_C(name, _write_lock)(out); \
M_CALL_MOVE(oplist, out->data, src->data); \
M_C(name, _write_unlock)(out); \
M_C(name, _internal_clear)(src); \
CONCURRENTI_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(SWAP, oplist)( \
static inline void \
M_C(name, _swap)(concurrent_t out, concurrent_t src) \
{ \
CONCURRENTI_CONTRACT(out); \
CONCURRENTI_CONTRACT(src); \
if (out < src) { \
M_C(name, _write_lock)(out); \
M_C(name, _write_lock)(src); \
} else { \
M_C(name, _write_lock)(src); \
M_C(name, _write_lock)(out); \
} \
M_CALL_SWAP(oplist, out->data, src->data); \
if (out < src) { \
M_C(name, _write_unlock)(src); \
M_C(name, _write_unlock)(out); \
} else { \
M_C(name, _write_unlock)(out); \
M_C(name, _write_unlock)(src); \
} \
} \
,) \
\
M_IF_METHOD(CLEAN, oplist)( \
static inline void \
M_C(name, _clean)(concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
M_CALL_CLEAN(oplist, out->data); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(TEST_EMPTY, oplist)( \
static inline bool \
M_C(name, _empty_p)(concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _read_lock)(out); \
bool b = M_CALL_TEST_EMPTY(oplist, out->data); \
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)( \
static inline void \
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_C(name, _write_lock)(out); \
M_CALL_SET_KEY(oplist, out->data, key, data); \
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, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
CONCURRENTI_CONTRACT(out); \
assert (out_data != NULL); \
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_C(name, _read_unlock)(out); \
return p != NULL; \
} \
,) \
\
M_IF_METHOD(GET_SET_KEY, oplist)( \
static inline void \
M_C(name, _get_at_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
CONCURRENTI_CONTRACT(out); \
assert (out_data != NULL); \
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_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(ERASE_KEY, oplist)( \
static inline bool \
M_C(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
M_C(name, _write_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(PUSH, oplist)( \
static inline void \
M_C(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
M_CALL_PUSH(oplist, out->data, data); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(POP, oplist)( \
static inline void \
M_C(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
M_CALL_POP(oplist, p, out->data); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(PUSH_MOVE, oplist)( \
static inline void \
M_C(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
M_CALL_PUSH_MOVE(oplist, out->data, data); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(POP_MOVE, oplist)( \
static inline void \
M_C(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
M_CALL_POP_MOVE(oplist, p, out->data); \
M_C(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, concurrent_t const out, bool a) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _read_lock)(out); \
M_CALL_GET_STR(oplist, str, out->data, a); \
M_C(name, _read_unlock)(out); \
} \
,) \
\
M_IF_METHOD(OUT_STR, oplist)( \
static inline void \
M_C(name, _out_str)(FILE *f, concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _read_lock)(out); \
M_CALL_OUT_STR(oplist, f, out->data); \
M_C(name, _read_unlock)(out); \
} \
,) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
static inline bool \
M_C(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
M_C(name, _write_signal)(out); \
M_C(name, _write_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(IN_STR, oplist)( \
static inline bool \
M_C(name, _in_str)(concurrent_t out, FILE *f) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _write_lock)(out); \
bool b = M_CALL_IN_STR(oplist, out->data, f); \
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)( \
static inline bool \
M_C(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \
{ \
CONCURRENTI_CONTRACT(out1); \
CONCURRENTI_CONTRACT(out2); \
if (out1 == out2) return true; \
if (out1 < out2) { \
M_C(name, _read_lock)(out1); \
M_C(name, _read_lock)(out2); \
} else { \
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_C(name, _read_unlock)(out2); \
M_C(name, _read_unlock)(out1); \
} else { \
M_C(name, _read_unlock)(out1); \
M_C(name, _read_unlock)(out2); \
} \
return b; \
} \
,) \
\
M_IF_METHOD(GET_KEY, oplist)( \
static inline bool \
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_C(name, _read_lock)(out); \
while (true) { \
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); \
ret = true; \
break; \
} \
if (blocking == false) break; \
M_C(name, _read_wait)(out); \
} \
M_C(name, _read_unlock)(out); \
return ret; \
} \
,) \
\
M_IF_METHOD2(POP, TEST_EMPTY, oplist)( \
static inline bool \
M_C(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
{ \
CONCURRENTI_CONTRACT(out); \
assert (p != NULL); \
bool ret = false; \
M_C(name, _write_lock)(out); \
while (true) { \
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
M_CALL_POP(oplist, p, out->data); \
ret = true; \
break; \
} \
if (blocking == false) break; \
M_C(name, _write_wait)(out); \
} \
M_C(name, _write_unlock)(out); \
return ret; \
} \
,) \
\
M_IF_METHOD2(POP_MOVE, TEST_EMPTY, oplist)( \
static inline bool \
M_C(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
{ \
CONCURRENTI_CONTRACT(out); \
assert (p != NULL); \
bool ret = false; \
M_C(name, _write_lock)(out); \
while (true) { \
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
M_CALL_POP_MOVE(oplist, p, out->data); \
ret = true; \
break; \
} \
if (blocking == false) break; \
M_C(name, _write_wait)(out); \
} \
M_C(name, _write_unlock)(out); \
return ret; \
} \
,) \
\
M_IF_METHOD(HASH, oplist)( \
static inline size_t \
M_C(name, _hash)(concurrent_t const out) \
{ \
CONCURRENTI_CONTRACT(out); \
M_C(name, _read_lock)(out); \
size_t h = M_CALL_HASH(oplist, out->data); \
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
/* Validate the value oplist before going further */
#define CONCURRENTI_RP_DEF_P2(name, type, oplist, concurrent_t) \
M_IF_OPLIST(oplist)(CONCURRENTI_RP_DEF_P3, CONCURRENTI_RP_DEF_FAILURE)(name, type, oplist, concurrent_t)
/* Stop processing with a compilation failure */
#define CONCURRENTI_RP_DEF_FAILURE(name, type, oplist, concurrent_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_RP_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
/* 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_P3(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
+1434 -269
View File
File diff suppressed because it is too large. Load diff
+362 -102
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - DEQUE module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,30 +22,34 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_DEQUE_H
#define __M_DEQUE_H
#ifndef MSTARLIB_DEQUE_H
#define MSTARLIB_DEQUE_H
#include "m-i-list.h"
/* Define a deque of a given type.
/* 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_DEFAULT_OPLIST, 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 type.
/* 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
#endif
/* Define the internal contract of a deque */
#define DEQUEI_CONTRACT(d) do { \
assert ((d) != NULL); \
assert ((d)->default_size >= DEQUEUI_DEFAULT_SIZE); \
@@ -59,23 +63,63 @@
(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) \
/* Validate the oplist before going further */
#define DEQUEI_DEF_P2(name, type, oplist, deque_t, it_t, node_t) \
M_IF_OPLIST(oplist)(DEQUEI_DEF_P3, DEQUEI_DEF_FAILURE)(name, type, oplist, deque_t, it_t, node_t)
/* Stop processing with a compilation failure */
#define DEQUEI_DEF_FAILURE(name, type, oplist, deque_t, it_t, node_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DEQUE_DEF): the given argument is not a valid oplist: " #oplist)
/* Internal deque definition
- name: prefix to be used
- type: type of the elements of the deque
- 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_P3(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[]; \
type data[M_MIN_FLEX_ARRAY_SIZE]; \
} node_t; \
\
\
/* 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; \
@@ -83,6 +127,9 @@
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); \
\
typedef struct M_C(name, _it_s) { \
node_t *node; \
@@ -92,24 +139,32 @@
\
typedef type M_C(name, _type_t); \
\
static inline deque_node_t* \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline M_C(name, _node_t)* \
M_C(name, _int_new_node)(deque_t d) \
{ \
const size_t def = d->default_size; \
if (M_UNLIKELY (def >SIZE_MAX / sizeof (type) - sizeof(deque_node_t))) { \
M_MEMORY_FULL(sizeof(deque_node_t)+def * sizeof(type)); \
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; \
} \
deque_node_t*n = (deque_node_t*) (void*) \
M_GET_REALLOC oplist (char, NULL, \
sizeof(deque_node_t)+def * sizeof(type) ); \
/* Alloc a new node */ \
M_C(name, _node_t)*n = (M_C(name, _node_t)*) (void*) \
M_CALL_REALLOC(oplist, char, NULL, \
sizeof(M_C(name, _node_t)) + def * sizeof(type) ); \
if (n==NULL) { \
M_MEMORY_FULL(sizeof(deque_node_t)+def * sizeof(type)); \
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
return NULL; \
} \
/* Initialize the node */ \
n->size = def; \
deque_node_list_init_field(n); \
d->default_size = M_GET_INC_ALLOC oplist (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); \
return n; \
} \
\
@@ -119,9 +174,9 @@
M_C(name, _node_list_init)(d->list); \
d->default_size = DEQUEUI_DEFAULT_SIZE; \
d->count = 0; \
deque_node_t *n = M_C(name, _int_new_node)(d); \
M_C(name, _node_t) *n = M_C(name, _int_new_node)(d); \
if (n == NULL) return; \
deque_node_list_push_back(d->list, n); \
M_C(name, _node_list_push_back)(d->list, n); \
d->front->node = n; \
d->front->index = DEQUEUI_DEFAULT_SIZE/2; \
d->back->node = n; \
@@ -133,18 +188,17 @@
M_C(name, _clean)(deque_t d) \
{ \
DEQUEI_CONTRACT(d); \
M_C(name, _node_list_it_t) it; \
deque_node_t *min_node = NULL; \
for(M_C(name, _node_list_it)(it, d->list) ; \
!M_C(name, _node_list_end_p)(it) ; \
M_C(name, _node_list_next)(it) ){ \
deque_node_t *n = M_C(name, _node_list_ref)(it); \
size_t min = n == d->front->node ? d->back->index + 1 : 0; \
size_t max = n == d->back->node ? d->back->index + 1: n->size; \
M_C(name, _node_t) *min_node = NULL; \
for(M_C(name, _node_t) *n = d->front->node; \
n != NULL ; \
n = (n == d->back->node) ? NULL : \
M_C(name, _node_list_next_obj)(d->list, n) ){ \
size_t min = n == d->front->node ? d->front->index : 0; \
size_t max = n == d->back->node ? d->back->index : n->size; \
for(size_t i = min; i < max; i++) { \
M_GET_CLEAR oplist (n->data[i]); \
M_CALL_CLEAR(oplist, n->data[i]); \
} \
min_node = min_node == NULL || min_node->size > n->size ? n : min_node; \
min_node = (min_node == NULL || min_node->size > n->size) ? n : min_node; \
} \
assert (min_node != NULL); \
d->front->node = min_node; \
@@ -160,9 +214,9 @@
{ \
DEQUEI_CONTRACT(d); \
M_C(name, _clean)(d); \
/* We registered the delete operator to clear all objects */ \
/* We have registered the delete operator to clear all objects */ \
M_C(name, _node_list_clear)(d->list); \
/* It is safer to clean some variables*/ \
/* It is safer to clean some variables */ \
d->front->node = NULL; \
d->back->node = NULL; \
d->count = 0; \
@@ -172,11 +226,13 @@
M_C(name, _push_back_raw)(deque_t d) \
{ \
DEQUEI_CONTRACT(d); \
deque_node_t *n = d->back->node; \
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); \
@@ -197,25 +253,37 @@
{ \
type *p = M_C(name, _push_back_raw)(d); \
if (M_LIKELY(p != NULL)) { \
M_GET_INIT_SET oplist (*p, x); \
M_CALL_INIT_SET(oplist, *p, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline type * \
M_C(name, _push_back_new)(deque_t d) \
{ \
type *p = M_C(name, _push_back_raw)(d); \
if (M_LIKELY(p != NULL)) { \
M_GET_INIT oplist (*p); \
M_CALL_INIT(oplist, *p); \
} \
return p; \
} \
, ) \
\
static inline void \
M_C(name, _push_back_move)(deque_t d, type *x) \
{ \
assert (x != NULL); \
type *p = M_C(name, _push_back_raw)(d); \
if (M_UNLIKELY(p == NULL)) \
return; \
M_DO_INIT_MOVE (oplist, *p, *x); \
} \
\
static inline type* \
M_C(name, _push_front_raw)(deque_t d) \
{ \
DEQUEI_CONTRACT(d); \
deque_node_t *n = d->front->node; \
M_C(name, _node_t) *n = d->front->node; \
size_t index = d->front->index; \
index --; \
/* If overflow */ \
@@ -241,18 +309,30 @@
{ \
type *p = M_C(name, _push_front_raw)(d); \
if (M_LIKELY(p != NULL)) { \
M_GET_INIT_SET oplist (*p, x); \
M_CALL_INIT_SET(oplist, *p, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline type * \
M_C(name, _push_front_new)(deque_t d) \
{ \
type *p = M_C(name, _push_front_raw)(d); \
if (M_LIKELY(p != NULL)) { \
M_GET_INIT oplist (*p); \
M_CALL_INIT(oplist, *p); \
} \
return p; \
} \
,) \
\
static inline void \
M_C(name, _push_front_move)(deque_t d, type *x) \
{ \
assert (x != NULL); \
type *p = M_C(name, _push_front_raw)(d); \
if (M_UNLIKELY(p == NULL)) \
return; \
M_DO_INIT_MOVE (oplist, *p, *x); \
} \
\
static inline void \
@@ -260,61 +340,101 @@
{ \
DEQUEI_CONTRACT(d); \
assert(d->count > 0); \
deque_node_t *n = d->back->node; \
M_C(name, _node_t) *n = d->back->node; \
size_t index = d->back->index; \
index --; \
if (M_UNLIKELY (n->size <= index)) { \
n = deque_node_list_previous_obj(d->list, n); \
/* If there is a next node, \
pop the back node and push it back to the front. This \
reduce the used memory if the deque is used as a FIFO queue.*/ \
M_C(name, _node_t) *next = M_C(name, _node_list_next_obj)(d->list, n); \
if (next != NULL) { \
next = M_C(name, _node_list_pop_back)(d->list); \
assert (next != n); \
M_C(name, _node_list_push_front)(d->list, next); \
} \
n = M_C(name, _node_list_previous_obj)(d->list, n); \
assert (n != NULL); \
d->back->node = n; \
index = n->size-1; \
} \
if (ptr != NULL) \
M_IF_METHOD(MOVE, oplist) ( \
M_GET_MOVE oplist(*ptr, n->data[index]); else \
M_CALL_MOVE(oplist, *ptr, n->data[index]); else \
, \
M_GET_SET oplist(*ptr, n->data[index]); \
M_CALL_SET(oplist, *ptr, n->data[index]); \
) \
M_GET_CLEAR oplist (n->data[index]); \
M_CALL_CLEAR(oplist, n->data[index]); \
d->count --; \
d->back->index = index; \
DEQUEI_CONTRACT(d); \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _pop_back_move)(type *ptr, deque_t d) \
{ \
assert(ptr != NULL); \
/* Note: Lazy implementation. Can be improved if needed */ \
M_CALL_INIT(oplist, *ptr); \
M_C(name, _pop_back)(ptr, d); \
} \
, ) \
\
static inline void \
M_C(name, _pop_front)(type *ptr, deque_t d) \
{ \
DEQUEI_CONTRACT(d); \
assert(d->count > 0); \
deque_node_t *n = d->front->node; \
M_C(name, _node_t) *n = d->front->node; \
size_t index = d->front->index; \
if (M_UNLIKELY (n->size <= index)) { \
n = deque_node_list_next_obj(d->list, n); \
/* If there is a previous node, \
pop the front node and push it back to the back. This \
reduce the used memory if the deque is used as a FIFO queue.*/ \
M_C(name,_node_t) *prev = M_C(name, _node_list_previous_obj)(d->list, n); \
if (prev != NULL) { \
prev = M_C(name, _node_list_pop_front)(d->list); \
assert (prev != n); \
M_C(name, _node_list_push_back)(d->list, prev); \
} \
n = M_C(name, _node_list_next_obj)(d->list, n); \
assert (n != NULL); \
d->front->node = n; \
index = 0; \
} \
if (ptr != NULL) \
M_IF_METHOD(MOVE, oplist) ( \
M_GET_MOVE oplist(*ptr, n->data[index]); else \
M_CALL_MOVE(oplist, *ptr, n->data[index]); else \
, \
M_GET_SET oplist(*ptr, n->data[index]); \
M_CALL_SET(oplist, *ptr, n->data[index]); \
) \
M_GET_CLEAR oplist (n->data[index]); \
M_CALL_CLEAR(oplist, n->data[index]); \
index++; \
d->count --; \
d->front->index = index; \
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _pop_front_move)(type *ptr, deque_t d) \
{ \
assert(ptr != NULL); \
/* Note: Lazy implementation */ \
M_CALL_INIT(oplist, *ptr); \
M_C(name, _pop_front)(ptr, d); \
} \
,) \
\
static inline type * \
M_C(name, _back)(const deque_t d) \
{ \
DEQUEI_CONTRACT(d); \
assert (d->count > 0); \
size_t i = d->back->index; \
deque_node_t *n = d->back->node; \
M_C(name, _node_t) *n = d->back->node; \
if (M_UNLIKELY (i == 0)) { \
n = deque_node_list_previous_obj(d->list, n); \
n = M_C(name, _node_list_previous_obj)(d->list, n); \
assert (n != NULL); \
i = n->size; \
} \
@@ -327,9 +447,9 @@
DEQUEI_CONTRACT(d); \
assert (d->count > 0); \
size_t i = d->front->index; \
deque_node_t *n = d->front->node; \
M_C(name, _node_t) *n = d->front->node; \
if (M_UNLIKELY (n->size <= i)) { \
n = deque_node_list_next_obj(d->list, n); \
n = M_C(name, _node_list_next_obj)(d->list, n); \
assert (n != NULL); \
i = 0; \
} \
@@ -342,6 +462,40 @@
DEQUEI_CONTRACT(d); \
return d->count; \
} \
\
static inline size_t \
M_C(name, _capacity_back)(const deque_t v) \
{ \
DEQUEI_CONTRACT(v); \
size_t s = 0; \
for(M_C(name, _node_t) *n = M_C(name, _node_list_back)(v->list); \
n != NULL ; \
n = (n == v->back->node) ? NULL : \
M_C(name, _node_list_previous_obj)(v->list, n) ){ \
s += (n == v->back->node ? v->back->index : n->size); \
} \
return s; \
} \
\
static inline size_t \
M_C(name, _capacity_front)(const deque_t v) \
{ \
DEQUEI_CONTRACT(v); \
size_t s = 0; \
for(M_C(name, _node_t) *n = M_C(name, _node_list_front)(v->list); \
n != NULL ; \
n = (n == v->front->node) ? NULL : \
M_C(name, _node_list_next_obj)(v->list, n) ){ \
s += n->size - (n == v->front->node ? v->front->index : 0); \
} \
return s; \
} \
\
static inline size_t \
M_C(name, _capacity)(const deque_t v) \
{ \
return M_C(name, _capacity_back)(v)+M_C(name, _capacity_front)(v); \
} \
\
static inline bool \
M_C(name, _empty_p)(const deque_t d) \
@@ -369,7 +523,7 @@
it->index = d->back->index - 1; \
it->deque = d; \
if (M_UNLIKELY (it->index >= it->node->size)) { \
it->node = deque_node_list_previous_obj(d->list, it->node); \
it->node = M_C(name, _node_list_previous_obj)(d->list, it->node); \
assert (it->node != NULL); \
it->index = it->node->size-1; \
} \
@@ -409,10 +563,10 @@
M_C(name, _next)(it_t it) \
{ \
assert (it != NULL); \
deque_node_t *n = it->node; \
M_C(name, _node_t) *n = it->node; \
it->index ++; \
if (M_UNLIKELY (it->index >= n->size)) { \
n = deque_node_list_next_obj(it->deque->list, n); \
n = M_C(name, _node_list_next_obj)(it->deque->list, n); \
if (M_UNLIKELY (n == NULL)) { \
/* Point to 'end' (can't undo it) */ \
it->node = it->deque->back->node; \
@@ -428,10 +582,10 @@
M_C(name, _previous)(it_t it) \
{ \
assert (it != NULL); \
deque_node_t *n = it->node; \
M_C(name, _node_t) *n = it->node; \
it->index --; \
if (M_UNLIKELY (it->index >= n->size)) { \
n = deque_node_list_previous_obj(it->deque->list, n); \
n = M_C(name, _node_list_previous_obj)(it->deque->list, n); \
if (M_UNLIKELY (n == NULL)) { \
/* Point to 'end' (can't undo it) */ \
it->node = it->deque->back->node; \
@@ -471,7 +625,7 @@
return &it->node->data[it->index]; \
} \
\
static inline const type * \
static inline type const * \
M_C(name, _cref)(it_t it) \
{ \
assert (it != NULL); \
@@ -487,10 +641,10 @@
M_C(name, _node_list_init)(d->list); \
d->default_size = DEQUEUI_DEFAULT_SIZE + src->count; \
d->count = src->count; \
deque_node_t *n = M_C(name, _int_new_node)(d); \
M_C(name, _node_t) *n = M_C(name, _int_new_node)(d); \
if (n == NULL) return; \
d->default_size /= 2; \
deque_node_list_push_back(d->list, n); \
M_C(name, _node_list_push_back)(d->list, n); \
d->front->node = n; \
d->front->index = DEQUEUI_DEFAULT_SIZE/2; \
d->back->node = n; \
@@ -498,8 +652,8 @@
it_t it; \
size_t i = DEQUEUI_DEFAULT_SIZE/2; \
for(M_C(name, _it)(it, src); !M_C(name, _end_p)(it) ; M_C(name, _next)(it)) { \
const type *obj = M_C(name, _cref)(it); \
M_GET_INIT_SET oplist (n->data[i], *obj); \
type const *obj = M_C(name, _cref)(it); \
M_CALL_INIT_SET(oplist, n->data[i], *obj); \
i++; \
assert (i <= d->back->index); \
} \
@@ -507,7 +661,7 @@
} \
\
static inline void \
M_C(name, _set)(deque_t d, deque_t src) \
M_C(name, _set)(deque_t d, deque_t const src) \
{ \
if (M_UNLIKELY (src == d)) \
return; \
@@ -521,7 +675,13 @@
{ \
DEQUEI_CONTRACT(src); \
assert (d!= NULL); \
memcpy(d, src, sizeof(deque_t)); \
M_C(name,_node_list_init_move)(d->list, src->list); \
d->front->node = src->front->node; \
d->front->index = src->front->index; \
d->back->node = src->back->node; \
d->back->index = src->back->index; \
d->default_size = src->default_size; \
d->count = src->count; \
memset(src, 0, sizeof(deque_t)); \
DEQUEI_CONTRACT(d); \
} \
@@ -541,7 +701,7 @@
{ \
DEQUEI_CONTRACT(d); \
DEQUEI_CONTRACT(e); \
deque_node_list_swap (d->list, e->list); \
M_C(name, _node_list_swap) (d->list, e->list); \
M_SWAP(node_t *, d->front->node, e->front->node); \
M_SWAP(node_t *, d->back->node, e->back->node); \
M_SWAP(size_t, d->front->index, e->front->index); \
@@ -559,12 +719,11 @@
assert (key < d->count); \
const size_t index0 = d->front->index; \
size_t count = 0; \
M_C(name, _node_list_it_t) it; \
/* This loop is in log(N) since the size increase exponentially.*/ \
for(M_C(name, _node_list_it)(it, d->list) ; \
!M_C(name, _node_list_end_p)(it) ; \
M_C(name, _node_list_next)(it) ){ \
deque_node_t *n = M_C(name, _node_list_ref)(it); \
for(M_C(name, _node_t) *n = d->front->node; \
n != NULL ; \
n = (n == d->back->node) ? NULL : \
M_C(name, _node_list_next_obj)(d->list, n) ){ \
if (index0 + key < count + n->size) { \
return &n->data[index0 + key - count]; \
} \
@@ -574,7 +733,7 @@
return NULL; \
} \
\
static inline const type * \
static inline type const * \
M_C(name, _cget)(deque_t d, size_t key) \
{ \
return M_CONST_CAST(type, M_C(name, _get)(d, key)); \
@@ -586,7 +745,7 @@
DEQUEI_CONTRACT(d); \
assert (key < d->count); \
type *p = M_C(name, _get)(d, key); \
M_GET_SET oplist (*p, x); \
M_CALL_SET(oplist, *p, x); \
} \
\
M_IF_METHOD(EQUAL, oplist)( \
@@ -602,9 +761,9 @@
for(M_C(name, _it)(it1, d1), M_C(name,_it)(it2, d2); \
!M_C(name, _end_p)(it1) ; \
M_C(name, _next)(it1), M_C(name, _next)(it2)) { \
const type *obj1 = M_C(name, _cref)(it1); \
const type *obj2 = M_C(name, _cref)(it2); \
if (M_GET_EQUAL oplist (*obj1, *obj2) == false) \
type const *obj1 = M_C(name, _cref)(it1); \
type const *obj2 = M_C(name, _cref)(it2); \
if (M_CALL_EQUAL(oplist, *obj1, *obj2) == false) \
return false; \
} \
assert (M_C(name, _end_p)(it2)); \
@@ -620,8 +779,8 @@
M_HASH_DECL(hash); \
it_t it; \
for(M_C(name, _it)(it, d); !M_C(name, _end_p)(it); M_C(name, _next)(it)) { \
const type *obj = M_C(name, _cref)(it); \
M_HASH_UP (hash, M_GET_HASH oplist (*obj)); \
type const *obj = M_C(name, _cref)(it); \
M_HASH_UP (hash, M_CALL_HASH(oplist, *obj)); \
} \
return M_HASH_FINAL(hash); \
} \
@@ -636,29 +795,29 @@
assert (j < d->count); \
type *obj1 = M_C(name, _get)(d, i); \
type *obj2 = M_C(name, _get)(d, j); \
M_GET_SWAP oplist (*obj1, *obj2); \
M_CALL_SWAP(oplist, *obj1, *obj2); \
DEQUEI_CONTRACT(d); \
} \
, /* NO SWAP: TODO */) \
, /* NO SWAP */) \
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, deque_t deque, bool append) \
M_C(name, _get_str)(string_t str, deque_t const deque, bool append) \
{ \
STRING_CONTRACT(str); \
STRINGI_CONTRACT(str); \
DEQUEI_CONTRACT(deque); \
(append ? string_cat_str : string_set_str) (str, "["); \
it_t it; \
for (M_C(name, _it)(it, deque) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
const type *item = M_C(name, _cref)(it); \
M_GET_GET_STR oplist (str, *item, true); \
type const *item = M_C(name, _cref)(it); \
M_CALL_GET_STR(oplist, str, *item, true); \
if (!M_C(name, _last_p)(it)) \
string_push_back (str, M_GET_SEPARATOR oplist); \
} \
string_push_back (str, ']'); \
STRING_CONTRACT(str); \
STRINGI_CONTRACT(str); \
} \
, /* no GET_STR */ ) \
\
@@ -669,17 +828,52 @@
DEQUEI_CONTRACT(deque); \
assert (file != NULL); \
fputc ('[', file); \
for (size_t i = 0; i < deque->size; i++) { \
const type *item = M_C(name, _cget)(deque, i); \
M_GET_OUT_STR oplist (file, *item); \
if (i != deque->size-1) \
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); \
M_CALL_OUT_STR(oplist, file, *item); \
if (!M_C(name, _last_p)(it)) \
fputc (M_GET_SEPARATOR oplist, file); \
} \
fputc (']', file); \
} \
, /* no OUT_STR */ ) \
\
M_IF_METHOD(IN_STR, oplist)( \
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
static inline bool \
M_C(name, _parse_str)(deque_t deque, const char str[], const char **endp) \
{ \
DEQUEI_CONTRACT(deque); \
assert (str != NULL); \
M_C(name,_clean)(deque); \
bool success = false; \
int c = *str++; \
if (M_UNLIKELY (c != '[')) goto exit; \
c = *str++; \
if (M_UNLIKELY (c == ']')) {success = true; goto exit;} \
if (M_UNLIKELY (c == 0)) goto exit; \
str--; \
type item; \
M_CALL_INIT(oplist, item); \
do { \
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
do { c = *str++; } while (isspace(c)); \
if (b == false || c == 0) { goto exit_clear; } \
M_C(name, _push_back)(deque, item); \
} while (c == M_GET_SEPARATOR oplist); \
DEQUEI_CONTRACT(deque); \
success = (c == ']'); \
exit_clear: \
M_CALL_CLEAR(oplist, item); \
exit: \
if (endp) *endp = str; \
return success; \
} \
, /* no PARSE_STR */ ) \
\
M_IF_METHOD2(IN_STR, INIT, oplist)( \
static inline bool \
M_C(name, _in_str)(deque_t deque, FILE *file) \
{ \
@@ -693,25 +887,86 @@
if (M_UNLIKELY (c == EOF)) return false; \
ungetc(c, file); \
type item; \
M_GET_INIT oplist (item); \
M_CALL_INIT(oplist, item); \
do { \
bool b = M_GET_IN_STR oplist (item, file); \
c = fgetc(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_back)(deque, item); \
} while (c == M_GET_SEPARATOR oplist); \
M_GET_CLEAR oplist (item); \
M_CALL_CLEAR(oplist, item); \
DEQUEI_CONTRACT(deque); \
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
/* Validation of the given oplist */
#define DEQUEI_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(DEQUEI_OPLIST_P3, DEQUEI_OPLIST_FAILURE)(name, oplist)
#define DEQUEI_OPLIST2(name, oplist) \
/* Prepare a clean compilation failure */
#define DEQUEI_OPLIST_FAILURE(name, oplist) \
M_LIB_ERROR(ARGUMENT_OF_DEQUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)
/* OPLIST definition of a deque */
#define DEQUEI_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,INIT_MOVE(M_C(name, _init_move)) \
@@ -719,6 +974,7 @@
,SWAP(M_C(name, _swap)) \
,TYPE(M_C(name,_t)) \
,SUBTYPE(M_C(name, _type_t)) \
,TEST_EMPTY(M_C(name,_empty_p)) \
,IT_TYPE(M_C(name,_it_t)) \
,IT_FIRST(M_C(name,_it)) \
,IT_LAST(M_C(name,_it_last)) \
@@ -732,12 +988,16 @@
,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) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
,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),) \
+911 -243
View File
File diff suppressed because it is too large. Load diff
+345
View File
@@ -0,0 +1,345 @@
/*
* M*LIB - Function Object module
*
* Copyright (c) 2017-2020, 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_FUNCOBJ_H
#define MSTARLIB_FUNCOBJ_H
#include "m-core.h"
/* Define a function object interface of name 'name'
* with a function like retcode, type of param1, type of param 2, ...
* USAGE:
* FUNC_OBJ_ITF_DEF(name, retcode type, type of param1, type of param 2, ...)
*/
#define FUNC_OBJ_ITF_DEF(name, ...) \
M_IF_NARGS_EQ1(__VA_ARGS__)(FUNCOBJI_ITF_NO_PARAM_DEF, FUNCOBJI_ITF_PARAM_DEF)(name, __VA_ARGS__)
/* Define a function object instance of name 'name' based on the interface 'base_name'
* The function is defined as per :
* - the prototype of the inherited interface
* - the parameters of the function are named as per the list param_list
* - the core of the function shall be defined in callback_core
* - optionals member attributes of the function object can be defined after the core
* (just like for tuple & variant: (name, type [, oplist])
*
* In the core of the function, parameters are accessible just like a normal like
* and a special variable 'self' which refers to the function object itself
* can be used to access member attributes.
*
* There shall be **exactly** the same number of parameters in 'param_list' than
* the number of parameters of the interface 'base_name'
*
* USAGE/EXAMPLE:
* FUNC_OBJ_INS_DEF(name, base_name, (param1, ...), { return param1 * self->member1 }, (member1, int), ...)
*/
#define FUNC_OBJ_INS_DEF(name, base_name, param_list, ...) \
M_IF_NARGS_EQ1(__VA_ARGS__)(FUNCOBJI_INS_NO_ATTR_DEF, FUNCOBJI_INS_ATTR_DEF)(name, base_name, param_list, __VA_ARGS__)
/* OPLIST of the instanced function object
* USAGE:
* FUNC_OBJ_INS_OPLIST(name, oplist of the attr1, ...)
*/
#define FUNC_OBJ_INS_OPLIST(...) \
M_IF_NARGS_EQ1(__VA_ARGS__)(FUNCOBJI_INS_NO_ATTR_OPLIST, FUNCOBJI_INS_ATTR_OPLIST_P1)( __VA_ARGS__)
/********************************************************************************/
/********************************** INTERNAL ************************************/
/********************************************************************************/
/* To be used by M_IF_FUNCOBJ macro.
NOTE: It is reversed (0 instead of 1) so that it can be used in M_IF reliabely.
*/
#define M_FUNCOBJ_IS_NOT_DEFINED 0
/* Design Constraints:
* callback SHALL be the first member of the structures in all the definitions.
*
* Structure definitions are specialized in function of the presence or not
* of parameters and/or attributes
* FIXME: How to factorize reasonnably well between the definitions?
*/
/* Specialization of the OPLIST in function if there is at least one member or not */
#define FUNCOBJI_INS_NO_ATTR_OPLIST(name) \
(NAME(name), TYPE(M_C(name, _t)), \
CLEAR(M_C(name, _clear)), \
INIT(M_C(name,_init)) \
)
/* Validate the oplist before going further */
#define FUNCOBJI_INS_ATTR_OPLIST_P1(name, ...) \
M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__))(FUNCOBJI_INS_ATTR_OPLIST_P3, FUNCOBJI_INS_ATTR_OPLIST_FAILURE)(name, __VA_ARGS__)
/* Prepare a clean compilation failure */
#define FUNCOBJI_INS_ATTR_OPLIST_FAILURE(name, ...) \
M_LIB_ERROR(ONE_ARGUMENT_OF_FUNC_OBJ_INS_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__)
/* Define at least the oplist */
#define FUNCOBJI_INS_ATTR_OPLIST_P3(name, ...) \
(NAME(name), TYPE(M_C(name, _t)), \
INIT_WITH(M_C(name, _init_with)), \
CLEAR(M_C(name, _clear)), \
M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_C(name,_init)),), \
)
/* Specialization of the definition a function object interface of name 'name'
* with a function like retcode () that doesn't have any input parameters.
*/
#define FUNCOBJI_ITF_NO_PARAM_DEF(name, retcode) \
typedef retcode M_C(name, _retcode_t); \
\
struct M_C(name, _s); \
\
/* No parameters to the callback */ \
typedef retcode(*M_C(name, _callback_t))(struct M_C(name, _s) *); \
\
typedef struct M_C(name, _s) { \
M_C(name, _callback_t) callback; \
} M_C(name, _t)[1]; \
\
static inline retcode \
M_C(name, _call)(M_C(name, _t) funcobj) \
{ \
M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \
funcobj->callback(funcobj); \
}
/* Specialization of the definition a function object interface of name 'name'
* with a function like retcode, type of param1, type of param 2, ...
* with mandatory input parameters.
*/
#define FUNCOBJI_ITF_PARAM_DEF(name, retcode, ...) \
typedef retcode M_C(name, _retcode_t); \
\
/* Define types for all paremeters */ \
M_MAP3(FUNCOBJI_BASE_TYPE, name, __VA_ARGS__) \
\
struct M_C(name, _s); \
\
typedef retcode(*M_C(name, _callback_t))(struct M_C(name, _s) *, __VA_ARGS__); \
\
typedef struct M_C(name, _s) { \
M_C(name, _callback_t) callback; \
} M_C(name, _t)[1]; \
\
static inline retcode \
M_C(name, _call)(M_C(name, _t) funcobj \
M_MAP3(FUNCOBJI_BASE_ARGLIST, name, __VA_ARGS__) ) \
{ \
M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \
funcobj->callback(funcobj M_MAP3(FUNCOBJI_BASE_ARGCALL, name, __VA_ARGS__) ); \
}
/* Specialization of the definition a function object instance of name 'name'
* with no member attribute.
*/
#define FUNCOBJI_INS_NO_ATTR_DEF(name, base_name, param_list, callback_core) \
typedef struct M_C(name, _s) { \
M_C(base_name, _callback_t) callback; \
} M_C(name, _t)[1]; \
\
static inline M_C(base_name, _retcode_t) \
M_C(name, _callback)(M_C(base_name, _t) _self \
M_IF_EMPTY(M_OPFLAT param_list)( \
/* No param */, \
M_MAP3(FUNCOBJI_INS_ARGLIST, base_name, M_OPFLAT param_list)\
) \
) \
{ \
struct M_C(name, _s) *self = (struct M_C(name, _s) *)_self; \
(void) self; /* maybe unused */ \
callback_core; \
} \
\
static inline void \
M_C(name, _init_with)(M_C(name, _t) obj) \
{ \
obj->callback = M_C(name, _callback); \
} \
\
static inline void \
M_C(name, _clear)(M_C(name, _t) obj) \
{ \
(void) obj; /* nothing to do */ \
} \
\
static inline struct M_C(base_name, _s) * \
M_C(name, _as_interface)(M_C(name, _t) obj) \
{ \
return (struct M_C(base_name, _s) *) obj; \
} \
\
static inline void \
M_C(name, _init)(M_C(name, _t) obj) \
{ \
obj->callback = M_C(name, _callback); \
} \
/* Specialization of the definition a function object instance of name 'name'
* with mandatory member attribute.
* First inject oplist in member attributes.
*/
#define FUNCOBJI_INS_ATTR_DEF(name, base_name, param_list, callback_core, ...) \
FUNCOBJI_INS_ATTR_DEF_P2(name, base_name, param_list, callback_core, FUNCOBJI_INJECT_GLOBAL(__VA_ARGS__) )
/* Inject the oplist within the list of arguments */
#define FUNCOBJI_INJECT_GLOBAL(...) \
M_MAP_C(FUNCOBJI_INJECT_OPLIST_A, __VA_ARGS__)
/* Transform (x, type) into (x, type, oplist) if there is global registered oplist
or (x, type, M_DEFAULT_OPLIST) if there is no global one,
or keep (x, type, oplist) if oplist was already present */
#define FUNCOBJI_INJECT_OPLIST_A( duo_or_trio ) \
FUNCOBJI_INJECT_OPLIST_B duo_or_trio
#define FUNCOBJI_INJECT_OPLIST_B( f, ... ) \
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
// Test if all third argument of all arguments is an oplist
#define FUNCOBJI_IF_ALL_OPLIST(...) \
M_IF(M_REDUCE(FUNCOBJI_IS_OPLIST_P, M_AND, __VA_ARGS__))
// Test if the third argument is an oplist
#define FUNCOBJI_IS_OPLIST_P(a) \
M_OPLIST_P(M_RET_ARG3 a)
/* Validate the oplist before going further */
#define FUNCOBJI_INS_ATTR_DEF_P2(name, base_name, param_list, callback_core, ...) \
FUNCOBJI_IF_ALL_OPLIST(__VA_ARGS__)(FUNCOBJI_INS_ATTR_DEF_P3, FUNCOBJI_INS_ATTR_DEF_FAILURE)(name, base_name, param_list, callback_core, __VA_ARGS__)
/* Stop processing with a compilation failure */
#define FUNCOBJI_INS_ATTR_DEF_FAILURE(name, base_name, param_list, callback_core, ...) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(FUNC_OBJ_INS_DEF): at least one of the given argument is not a valid oplist: " #__VA_ARGS__)
/* Expand the Function Object with members */
#define FUNCOBJI_INS_ATTR_DEF_P3(name, base_name, param_list, callback_core, ...) \
typedef struct M_C(name, _s) { \
/* Callback is the mandatory first argument */ \
M_C(base_name, _callback_t) callback; \
/* All the member attribute of the Function Object */ \
M_MAP(FUNCOBJI_INS_ATTR_STRUCT, __VA_ARGS__) \
} M_C(name, _t)[1]; \
\
FUNCOBJI_CONTROL_ALL_OPLIST(name, __VA_ARGS__) \
\
static inline M_C(base_name, _retcode_t) \
M_C(name, _callback)(M_C(base_name, _t) _self \
M_IF_EMPTY(M_OPFLAT param_list)( \
/* No param */, \
M_MAP3(FUNCOBJI_INS_ARGLIST, base_name, M_OPFLAT param_list)\
) \
) \
{ \
struct M_C(name, _s) *self = (struct M_C(name, _s) *)_self; \
(void) self; /* maybe unused */ \
callback_core; \
} \
\
static inline void \
M_C(name, _init_with)(M_C(name, _t) obj M_MAP(FUNCOBJI_INS_ATTR_LIST, __VA_ARGS__)) \
{ \
obj->callback = M_C(name, _callback); \
M_MAP(FUNCOBJI_INS_ATTR_INIT_SET, __VA_ARGS__); \
} \
\
static inline void \
M_C(name, _clear)(M_C(name, _t) obj) \
{ \
M_MAP(FUNCOBJI_INS_ATTR_CLEAR, __VA_ARGS__); \
} \
\
static inline struct M_C(base_name, _s) * \
M_C(name, _as_interface)(M_C(name, _t) obj) \
{ \
return (struct M_C(base_name, _s) *) obj; \
} \
\
M_IF(FUNCOBJI_TEST_METHOD_P(INIT, __VA_ARGS)) \
( \
static inline void \
M_C(name, _init)(M_C(name, _t) obj) \
{ \
obj->callback = M_C(name, _callback); \
M_MAP(FUNCOBJI_INS_ATTR_INIT, __VA_ARGS__); \
} \
, /* END OF INIT METHOD */ ) \
/* Define a numbered type of a parameter of the callback*/
#define FUNCOBJI_BASE_TYPE(name, num, type) \
typedef type M_C4(name, _param_, num, _t);
/* Define a list of the type of arguments for a function definition */
#define FUNCOBJI_BASE_ARGLIST(name, num, type) \
M_DEFERRED_COMMA type M_C(param_, num)
/* Define a list of arguments for a function call */
#define FUNCOBJI_BASE_ARGCALL(name, num, type) \
M_DEFERRED_COMMA M_C(param_, num)
/* Helper macros */
/* arg = (name, type [, oplist]) */
#define FUNCOBJI_INS_ATTR_STRUCT(arg) \
M_RET_ARG2 arg M_RET_ARG1 arg;
#define FUNCOBJI_INS_ATTR_LIST(arg) \
M_DEFERRED_COMMA M_RET_ARG2 arg const M_RET_ARG1 arg
#define FUNCOBJI_INS_ATTR_INIT(arg) \
M_CALL_INIT(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg);
#define FUNCOBJI_INS_ATTR_INIT_SET(arg) \
M_CALL_INIT_SET(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg, M_RET_ARG1 arg);
#define FUNCOBJI_INS_ATTR_CLEAR(arg) \
M_CALL_CLEAR(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg);
/* Define the list of arguments of the instance of the callback */
#define FUNCOBJI_INS_ARGLIST(name, num, param) \
M_DEFERRED_COMMA M_C4(name, _param_, num, _t) param
/* Macros for testing for a method presence in all the attributes */
#define FUNCOBJI_TEST_METHOD2_P(method, op) \
M_TEST_METHOD_P(method, op)
#define FUNCOBJI_TEST_METHOD1_P(method, arg) \
M_APPLY(FUNCOBJI_TEST_METHOD2_P, method, M_RET_ARG3 arg)
#define FUNCOBJI_TEST_METHOD_P(method, ...) \
M_IF(M_REDUCE2(FUNCOBJI_TEST_METHOD1_P, M_AND, method, __VA_ARGS__))
/* Macro for checking compatible type and oplist for all the attributes */
#define FUNCOBJI_CONTROL_ALL_OPLIST(name, ...) \
M_MAP2(FUNCOBJI_CONTROL_OPLIST, name, __VA_ARGS__)
#define FUNCOBJI_CONTROL_OPLIST(name, a) \
M_CHECK_COMPATIBLE_OPLIST(name, M_RET_ARG1 a, M_RET_ARG2 a, M_RET_ARG3 a)
#endif
+149 -52
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - GENINT module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,8 +22,8 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_GENINT_H
#define __M_GENINT_H
#ifndef MSTARLIB_GENINT_H
#define MSTARLIB_GENINT_H
#include "m-core.h"
#include "m-atomic.h"
@@ -31,43 +31,82 @@
/* GENINT is a container of integers with the following properties:
- it stores integer from [0..N) (N is fixed).
- an integer can have only one occurence in the container
(makeing the container a kind of permutation of [0..N) )
- you can atomically push in / pop out integer from this container.
- an integer can have only one occurrence in the container.
- you can atomically push in / pop out integer from this container
provided that they were not already in the container.
- there are no order (like FIFO or stack)
This can be used to map integers to index of ressources in a table.
This can be used to map integers to index of resources in a table.
At most we can support N = GENINT_LIMBSIZE * GENINT_LIMBSIZE >= 4096
due to the master trick. For the typical usage of this container
(mapping hardware or software limited resources), this should be
enough.
*/
typedef unsigned long genint_limb_t;
// TO DO: We may want a specialization with constant N value.
// Define the limb size
typedef unsigned long long genint_limb_t;
typedef struct genint_s {
size_t n;
atomic_ulong *data;
unsigned int n; // size of the container
unsigned int max; // number of allocated limb - 1
genint_limb_t mask0; // mask of the last limb
genint_limb_t mask_master; // mask of master
atomic_ullong master; // master bitfield (which informs if a limb is full or not)
atomic_ullong *data; // the bitfield which informs if an integer
} genint_t[1];
// Define the max supported value
#define GENINT_MAX_ALLOC (GENINT_LIMBSIZE * (GENINT_LIMBSIZE - GENINT_ABA_CPT))
// Define the size of a limb in bits.
#define GENINT_LIMBSIZE (sizeof(genint_limb_t) * CHAR_BIT)
#define GENINT_CONTRACT(s) do { \
assert (s != NULL); \
assert (s->n > 0); \
assert (s->data != NULL); \
#define GENINT_CONTRACT(s) do { \
assert (s != NULL); \
assert (s->n > 0 && s->n <= GENINT_MAX_ALLOC); \
assert ((s->max+1) * GENINT_LIMBSIZE >= s->n); \
assert (s->data != NULL); \
} while (0)
#define GENINT_ONE ((genint_limb_t)1)
#define GENINT_ONE ((genint_limb_t)1)
static inline void genint_init(genint_t s, size_t n)
// Value returned in case of error.
#define GENINT_ERROR (-1U)
// 32 bits of master are kept for handling the ABA problems.
#define GENINT_ABA_CPT 32
#define GENINT_ABA_CPT_T uint32_t
// Set the bit i of master
#define GENINT_MASTER_SET(master, i) \
((((master)& (~((GENINT_ONE<< GENINT_ABA_CPT)-1))) | (GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|((GENINT_ABA_CPT_T)((master) + 1)))
// Reset the bit i of master
#define GENINT_MASTER_RESET(master, i) \
(((master) & (~((GENINT_ONE<< GENINT_ABA_CPT)-1)) & ~(GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|((GENINT_ABA_CPT_T)((master) + 1)))
static inline void genint_init(genint_t s, unsigned int n)
{
assert (s != NULL && n > 0);
assert (s != NULL && n > 0 && n <= GENINT_MAX_ALLOC);
const size_t alloc = (n + GENINT_LIMBSIZE - 1) / GENINT_LIMBSIZE;
atomic_ulong *ptr = M_MEMORY_REALLOC (atomic_ulong, NULL, alloc);
s->n = n;
s->data = ptr;
const unsigned int index = n % GENINT_LIMBSIZE;
atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, alloc);
if (M_UNLIKELY (ptr == NULL)) {
M_MEMORY_FULL(alloc);
return;
}
for(size_t i = 0; i < alloc; i++)
atomic_init(&s->data[i], 0UL);
s->n = n;
s->data = ptr;
s->max = alloc-1;
s->mask0 = (index == 0) ? -GENINT_ONE : ~((GENINT_ONE<<(GENINT_LIMBSIZE-index))-1);
s->mask_master = (((GENINT_ONE << alloc) - 1) << (GENINT_LIMBSIZE-alloc)) >> GENINT_ABA_CPT;
atomic_init (&s->master, (genint_limb_t)0);
for(unsigned int i = 0; i < alloc; i++)
atomic_init(&s->data[i], (genint_limb_t)0);
GENINT_CONTRACT(s);
}
@@ -78,47 +117,105 @@ static inline void genint_clear(genint_t s)
s->data = NULL;
}
static inline size_t genint_pop(genint_t s)
static inline size_t genint_size(genint_t s)
{
GENINT_CONTRACT(s);
const size_t max = (s->n + GENINT_LIMBSIZE - 1) / GENINT_LIMBSIZE;
const size_t index = s->n % GENINT_LIMBSIZE;
const genint_limb_t mask0 = (index == 0) ? -GENINT_ONE : (GENINT_ONE<<index)-1;
// O(n) approach, but with a really low constant factor
for(size_t i = 0; i < max; i++)
{
const size_t mask = (i+1 == max) ? mask0 : -GENINT_ONE;
unsigned int bit;
genint_limb_t next, org = atomic_load(&s->data[i]);
do {
if (org == mask)
goto next_element;
// At least one bit is free in the limb
bit = GENINT_LIMBSIZE - m_core_clz(org);
assert ((org & (GENINT_ONE<<bit)) == 0);
// Set & reserve the integer as being used.
next = org | (GENINT_ONE << bit);
} while (!atomic_compare_exchange_weak (&s->data[i], &org, next));
// Return the new number
GENINT_CONTRACT(s);
return i * GENINT_LIMBSIZE + bit;
next_element: (void)1;
}
GENINT_CONTRACT(s);
return -(size_t)1; // No more ressource available
return s->n;
}
static inline void genint_push(genint_t s, size_t n)
// For a typical case, the amortized cost is one CAS per pop.
static inline unsigned int genint_pop(genint_t s)
{
GENINT_CONTRACT(s);
// First read master to see which limb is not full.
genint_limb_t master = atomic_load(&s->master);
// While master is not full
while ((master >> GENINT_ABA_CPT) != s->mask_master) {
// Let's get the index i of the first not full limb according to master.
unsigned int i = m_core_clz64(~master);
assert (i < GENINT_LIMBSIZE);
// Let's compute the mask of this limb representing the limb as being full
genint_limb_t mask = s->mask0;
mask = (i == s->max) ? mask : -GENINT_ONE;
unsigned int bit;
// Let's load this limb,
genint_limb_t next, org = atomic_load(&s->data[i]);
do {
// If it is now full, we have been preempted by another.
if (M_UNLIKELY (org == mask))
goto next_element;
assert (org != -GENINT_ONE);
// At least one bit is free in the limb. Find one.
bit = GENINT_LIMBSIZE - 1 - m_core_clz64(~org);
assert (bit < GENINT_LIMBSIZE);
assert ((org & (GENINT_ONE<<bit)) == 0);
assert (i * GENINT_LIMBSIZE + GENINT_LIMBSIZE - 1 - bit < s->n);
// Set the integer as being used.
next = org | (GENINT_ONE << bit);
// Try to reserve the integer
} while (!atomic_compare_exchange_weak (&s->data[i], &org, next));
// We have reserved the integer.
// If the limb is now full, try to update master
if (M_UNLIKELY(next == mask)) {
while (true) {
genint_limb_t newMaster;
if (next == mask) {
newMaster = GENINT_MASTER_SET(master, i);
} else {
newMaster = GENINT_MASTER_RESET(master, i);
}
if (atomic_compare_exchange_weak (&s->master, &master, newMaster))
break;
// Fail to update. Reload limb to check if it is still full.
next = atomic_load(&s->data[i]);
}
}
// Return the new number
GENINT_CONTRACT(s);
return i * GENINT_LIMBSIZE + GENINT_LIMBSIZE - 1 - bit;
next_element:
// Reload master
master = atomic_load(&s->master);
}
GENINT_CONTRACT(s);
return GENINT_ERROR; // No more resource available
}
// For a typical case, the amortized cost is one CAS per pop.
static inline void genint_push(genint_t s, unsigned int n)
{
GENINT_CONTRACT(s);
assert (n < s->n);
size_t i = n / GENINT_LIMBSIZE;
size_t bit = n % GENINT_LIMBSIZE;
const unsigned int i = n / GENINT_LIMBSIZE;
const unsigned int bit = GENINT_LIMBSIZE - 1 - (n % GENINT_LIMBSIZE);
genint_limb_t master = atomic_load(&s->master);
// Load the limb
genint_limb_t next, org = atomic_load(&s->data[i]);
do {
assert ((org & (GENINT_ONE<<bit)) != 0);
assert ((org & (GENINT_ONE << bit)) != 0);
// Reset it
next = org & (~(GENINT_ONE << bit));
// Try to unreserve it.
} while (!atomic_compare_exchange_weak (&s->data[i], &org, next));
// if the limb was marked as full by master
genint_limb_t mask = s->mask0;
mask = (i == s->max) ? mask : -GENINT_ONE;
if (M_UNLIKELY (next != mask)) {
// Let's compute the mask of this limb representing the limb as being full
// Let's try to update master to say that this limb is not full
while (true) {
genint_limb_t newMaster;
if (next == mask) {
newMaster = GENINT_MASTER_SET(master, i);
} else {
newMaster = GENINT_MASTER_RESET(master, i);
}
if (atomic_compare_exchange_weak (&s->master, &master, newMaster))
break;
// Fail to update. Reload limb to check if it is still full.
next = atomic_load(&s->data[i]);
}
}
GENINT_CONTRACT(s);
}
+197 -62
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Intrusive List module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,16 +22,17 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_I_LIST_H
#define __M_I_LIST_H
#ifndef MSTARLIB_I_LIST_H
#define MSTARLIB_I_LIST_H
#include "m-core.h"
#include "m-list.h" // For LISTI_ITBASE_DEF
/* Interface to add to a structure to allow intrusive support.
/* Interface to add to a structure to enable intrusive doubly-linked support.
name: name of the intrusive list.
type: name of the type of the structure (aka. struct test_s) - not used currently
USAGE:
struct tmp_str_s {
typedef struct tmp_str_s {
...
ILIST_INTERFACE(tmpstr, struct tmp_str_s);
...
@@ -40,20 +41,21 @@
#define ILIST_INTERFACE(name, type) \
struct ilist_head_s name
/* Define a list of a given type.
/* Define a doubly-linked intrusive list of a given type.
The type needs to have ILIST_INTERFACE().
USAGE:
LIST_DEF(name, type [, oplist_of_the_type]) */
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 ilist of type.
/* Define the oplist of a doubly-linked instrusive list of type.
USAGE:
LIST_OPLIST(name [, oplist_of_the_type]) */
ILIST_OPLIST(name [, oplist_of_the_type]) */
#define ILIST_OPLIST(...) \
ILISTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
ILISTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_DEFAULT_OPLIST), \
(__VA_ARGS__ )))
@@ -65,15 +67,27 @@ typedef struct ilist_head_s {
struct ilist_head_s *prev;
} ilist_head_t;
/* Indirection call to allow expanding all arguments (TBC) */
#define ILISTI_OPLIST(arg) ILISTI_OPLIST2 arg
/* Indirection call to allow expanding all arguments */
#define ILISTI_OPLIST_P1(arg) ILISTI_OPLIST_P2 arg
/* Validation of the given oplist */
#define ILISTI_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(ILISTI_OPLIST_P3, ILISTI_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define ILISTI_OPLIST_FAILURE(name, oplist) \
M_LIB_ERROR(ARGUMENT_OF_ILIST_OPLIST_IS_NOT_AN_OPLIST, name, oplist)
/* Define the oplist of an ilist of type */
#define ILISTI_OPLIST2(name,oplist) \
#define ILISTI_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)), \
CLEAR(M_C(name, _clear)), \
INIT_MOVE(M_C(name, _init_move)), \
MOVE(M_C(name, _move)), \
TYPE(M_C(name,_t)), \
CLEAN(M_C(name,_clean)), \
SUBTYPE(M_C(name,_type_t)), \
TEST_EMPTY(M_C(name,_empty_p)), \
IT_TYPE(M_C(name,_it_t)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name,_it_set)), \
@@ -89,35 +103,54 @@ typedef struct ilist_head_s {
IT_REMOVE(M_C(name,_remove)), \
M_IF_METHOD(NEW, oplist)(IT_INSERT(M_C(name,_insert)),), \
OPLIST(oplist), \
PUSH(M_C(name,_push_back)), \
POP(M_C(name,_pop_back)) \
SPLICE_BACK(M_C(name,_splice_back)) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
)
/* Contract respected by all intrusive lists */
#define ILISTI_CONTRACT(name, list) do { \
assert (list != NULL); \
assert(list->name.next->prev == &list->name); \
assert(list->name.prev->next == &list->name); \
assert(list != NULL); \
assert(list->name.prev != NULL); \
assert(list->name.next != NULL); \
assert(list->name.next->prev == &list->name); \
assert(list->name.prev->next == &list->name); \
} while (0)
#define ILISTI_NODE_CONTRACT(node) do { \
assert((node) != NULL); \
assert((node)->prev != NULL); \
assert((node)->next != NULL); \
assert((node)->next->prev == node); \
assert((node)->prev->next == node); \
} 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 list.
/* Validate the oplist before going further */
#define ILISTI_DEF_P2(name, type, oplist, list_t, it_t) \
M_IF_OPLIST(oplist)(ILISTI_DEF_P3, ILISTI_DEF_FAILURE)(name, type, oplist, list_t, it_t)
/* Stop processing with a compilation failure */
#define ILISTI_DEF_FAILURE(name, type, oplist, list_t, it_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ILIST_DEF): the given argument is not a valid oplist: " #oplist)
/* Definition of the type and function for an intrusive doubly-linked list.
USAGE:
name: name of the intrusive list
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_P3(name, type, oplist, list_t, it_t) \
\
typedef struct M_C(name, _s) { \
struct ilist_head_s name; \
} list_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); \
\
@@ -126,8 +159,10 @@ 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]; \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void M_C(name, _init)(list_t list) \
{ \
assert (list != NULL); \
@@ -141,14 +176,15 @@ typedef struct ilist_head_s {
ILISTI_CONTRACT(name, list); \
for(struct ilist_head_s *it = list->name.next, *next ; \
it != &list->name; it = next) { \
/* Cannot check node contract as previous node may be deleted */ \
type *obj = M_TYPE_FROM_FIELD(type, it, \
struct ilist_head_s, name); \
/* Read next now before the object is destroyed */ \
next = it->next; \
assert (next != NULL); \
M_GET_CLEAR oplist (*obj); \
M_CALL_CLEAR(oplist, *obj); \
/* Delete also the object if a DELETE operand is registered */ \
M_IF_METHOD(DEL, oplist)(M_GET_DEL oplist (obj), (void) 0); \
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
} \
/* Nothing remains in the list anymore */ \
list->name.next = &list->name; \
@@ -165,6 +201,29 @@ typedef struct ilist_head_s {
{ \
ILISTI_CONTRACT(name, list); \
return list->name.next == &list->name; \
} \
\
\
static inline void M_C(name, _init_move)(list_t list, list_t ref) \
{ \
ILISTI_CONTRACT(name, ref); \
assert (list != ref); \
M_C(name,_init)(list); \
if (!M_C(name,_empty_p)(ref)) { \
list->name.next = ref->name.next; \
list->name.prev = ref->name.prev; \
list->name.next->prev = &list->name; \
list->name.prev->next = &list->name; \
} \
ref->name.next = NULL; \
ref->name.prev = NULL; \
ILISTI_CONTRACT(name, list); \
} \
\
static inline void M_C(name, _move)(list_t list, list_t ref) \
{ \
M_C(name, _clear)(list); \
M_C(name, _init_move)(list, ref); \
} \
\
static inline size_t M_C(name, _size)(const list_t list) \
@@ -172,8 +231,10 @@ typedef struct ilist_head_s {
ILISTI_CONTRACT(name, list); \
size_t s = 0; \
for(const struct ilist_head_s *it = list->name.next ; \
it != &list->name; it = it->next) \
it != &list->name; it = it->next) { \
ILISTI_NODE_CONTRACT(it); \
s++; \
} \
return s; \
} \
\
@@ -249,85 +310,94 @@ typedef struct ilist_head_s {
} \
\
static inline type * \
M_C(name, _next_obj)(const list_t list, const type *obj) \
M_C(name, _next_obj)(const list_t list, type const *obj) \
{ \
ILISTI_CONTRACT(name, list); \
assert (obj != NULL); \
ILISTI_NODE_CONTRACT(&obj->name); \
return obj->name.next == &list->name ? NULL : \
M_TYPE_FROM_FIELD(type, obj->name.next, \
struct ilist_head_s, name); \
} \
\
static inline type * \
M_C(name, _previous_obj)(const list_t list, const type *obj) \
M_C(name, _previous_obj)(const list_t list, type const *obj) \
{ \
ILISTI_CONTRACT(name, list); \
assert (obj != NULL); \
ILISTI_NODE_CONTRACT(&obj->name); \
return obj->name.prev == &list->name ? NULL : \
M_TYPE_FROM_FIELD(type, obj->name.prev, \
struct ilist_head_s, name); \
} \
\
static inline void \
M_C(name, _it)(list_it_t it, list_t list) \
M_C(name, _it)(it_t it, const list_t list) \
{ \
ILISTI_CONTRACT(name, list); \
assert (it != NULL); \
it->head = &list->name; \
it->head = list->name.next->prev; \
it->current = list->name.next; \
it->next = list->name.next->next; \
it->previous = &list->name; \
it->previous = it->head; \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
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; \
it->current = cit->current; \
it->next = cit->next; \
it->previous = cit->previous; \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
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); \
return it->current == it->head; \
} \
\
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); \
return it->next == it->head || it->current == it->head; \
} \
\
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! */ \
/* Note: Can't set it->previous to it->current. \
it->current may have been unlinked from the list */ \
it->current = it->next; \
@@ -335,12 +405,14 @@ typedef struct ilist_head_s {
it->next = it->current->next; \
it->previous = it->current->prev; \
assert (it->next != NULL && it->previous != NULL); \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
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! */ \
/* Note: Can't set it->next to it->current. \
it->current may have been unlinked from the list */ \
it->current = it->previous; \
@@ -348,10 +420,11 @@ typedef struct ilist_head_s {
it->next = it->current->next; \
it->previous = it->current->prev; \
assert (it->next != NULL && it->previous != NULL); \
ILISTI_NODE_CONTRACT(it->current); \
} \
\
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 */ \
@@ -359,9 +432,10 @@ 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); \
/* check if 'it' was not deleted */ \
assert (it->current->next == it->next); \
assert (it->current->prev == it->previous); \
@@ -370,37 +444,39 @@ typedef struct ilist_head_s {
struct ilist_head_s, name); \
} \
\
static inline const type * \
M_C(name, _cref)(const list_it_t it) \
static inline type const * \
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); \
(void)list; /* list param is not used */ \
type *obj = M_TYPE_FROM_FIELD(type, it->current, \
struct ilist_head_s, name); \
M_C(name, _unlink)(obj); \
M_GET_CLEAR oplist (obj); \
M_IF_METHOD(DEL, oplist)(M_GET_DEL oplist (obj), (void) 0); \
M_CALL_CLEAR(oplist, obj); \
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
M_C(name, _next)(it); \
} \
\
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); \
type *p = M_GET_NEW oplist (type); \
ILISTI_NODE_CONTRACT(it->current); \
type *p = M_CALL_NEW(oplist, type); \
if (M_UNLIKELY (p == NULL)) { \
M_MEMORY_FULL (sizeof (type)); \
return ; \
} \
M_GET_INIT_SET oplist (*p, x); \
M_CALL_INIT_SET(oplist, *p, x); \
type *obj = M_C(name, _ref)(it); \
M_C(name, _push_after)(obj, p); \
(void) list; \
@@ -448,30 +524,89 @@ 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); \
ILISTI_NODE_CONTRACT(it->current); \
assert (it != NULL); \
(void) ov; \
type *obj = M_C(name, _ref)(it); \
M_C(name, _unlink)(obj); \
M_C(name, _push_back)(nv, obj); \
M_C(name, _next)(it); \
ILISTI_CONTRACT(name, nv); \
ILISTI_CONTRACT(name, ov); \
} \
\
static inline void \
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); \
assert (npos != NULL && opos != NULL); \
assert (!M_C(name, _end_p)(opos)); \
/* npos may be end */ \
(void) olist, (void) nlist; \
type *obj = M_C(name, _ref)(opos); \
struct ilist_head_s *ref = npos->current; \
/* Remove object */ \
M_C(name, _unlink)(obj); \
/* Push 'obj' after 'ref' */ \
struct ilist_head_s *next = ref->next; \
ref->next = &obj->name; \
obj->name.next = next; \
obj->name.prev = ref; \
next->prev = &obj->name; \
/* Move iterator in old list */ \
M_C(name, _next)(opos); \
/* Set npos iterator to new position of object */ \
npos->previous = ref; \
npos->current = &obj->name; \
npos->next = next; \
ILISTI_CONTRACT(name, nlist); \
ILISTI_CONTRACT(name, olist); \
} \
\
static inline void \
M_C(name, _swap)(list_t d, list_t e) \
{ \
ILISTI_CONTRACT(name, d); \
ILISTI_CONTRACT(name, e); \
M_SWAP(struct ilist_head_s *, d->name.next, e->name.next); \
M_SWAP(struct ilist_head_s *, d->name.prev, e->name.prev); \
M_SWAP(struct ilist_head_s *, d->name.next->prev, e->name.next->prev); \
M_SWAP(struct ilist_head_s *, d->name.prev->next, e->name.prev->next); \
struct ilist_head_s *d_item = d->name.next; \
struct ilist_head_s *e_item = e->name.next; \
d->name.next = e_item == &e->name ? &d->name : e_item; \
e->name.next = d_item == &d->name ? &e->name : d_item; \
d_item = d->name.prev; \
e_item = e->name.prev; \
d->name.prev = e_item == &e->name ? &d->name : e_item; \
e->name.prev = d_item == &d->name ? &e->name : d_item; \
d->name.next->prev = &d->name; \
d->name.prev->next = &d->name; \
e->name.next->prev = &e->name; \
e->name.prev->next = &e->name; \
ILISTI_CONTRACT(name, d); \
ILISTI_CONTRACT(name, e); \
} \
\
static inline void \
M_C(name, _reverse)(list_t list) \
{ \
ILISTI_CONTRACT(name, list); \
struct ilist_head_s *next, *it; \
for(it = list->name.next ; it != &list->name; it = next) { \
next = it->next; \
it->next = it->prev; \
it->prev = next; \
} \
next = it->next; \
it->next = it->prev; \
it->prev = next; \
ILISTI_CONTRACT(name, list); \
} \
\
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
#endif
+52 -28
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - INTRUSIVE SHARED PTR Module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,8 +22,8 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_I_SHARED_PTR__H
#define __M_I_SHARED_PTR__H
#ifndef MSTARLIB_I_SHARED_PTR_H
#define MSTARLIB_I_SHARED_PTR_H
#include "m-core.h"
#include "m-atomic.h"
@@ -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_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
(__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.
@@ -42,25 +42,36 @@
#define ISHARED_PTR_INTERFACE(name, type) \
atomic_int M_C(name, _cpt)
/* Define the intrusive shared pointer type and its static inline functions.
USAGE: ISHARED_PTR_DEF(name[, oplist]) */
USAGE: ISHARED_PTR_DEF(name, type, [, 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) ( \
/* Validation of the given oplist */
#define ISHAREDI_PTR_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(ISHAREDI_PTR_OPLIST_P3, ISHAREDI_PTR_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define ISHAREDI_PTR_OPLIST_FAILURE(name, oplist) \
M_LIB_ERROR(ARGUMENT_OF_ISHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)
// Define the oplist
#define ISHAREDI_PTR_OPLIST_P3(name, oplist) ( \
INIT(M_INIT_DEFAULT), \
INIT_SET(M_C(name, _init_set2) M_IPTR), \
SET(M_C(name, _set) M_IPTR), \
CLEAR(M_C(name, _clear)), \
CLEAN(M_C(name, _clean) M_IPTR), \
TYPE(M_C(name, _type_t)), \
TYPE(M_C(name, _t)), \
OPLIST(oplist), \
SUBTYPE(M_C(name, _type_t)) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
@@ -68,14 +79,25 @@
,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) \
/* Validate the oplist before going further */
#define ISHAREDI_PTR_DEF_P2(name, type, oplist) \
M_IF_OPLIST(oplist)(ISHAREDI_PTR_DEF_P3, ISHAREDI_PTR_DEF_FAILURE)(name, type, oplist)
/* Stop processing with a compilation failure */
#define ISHAREDI_PTR_DEF_FAILURE(name, type, oplist) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ISHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist)
#define ISHAREDI_PTR_DEF_P3(name, type, oplist) \
\
typedef type *M_C(name,_t); \
typedef type M_C(name, _type_t); \
\
static inline type * \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline M_C(name,_t) \
M_C(name, _init)(type *ptr) \
{ \
if (M_LIKELY (ptr != NULL)) \
@@ -83,8 +105,8 @@
return ptr; \
} \
\
static inline type * \
M_C(name, _init_set)(type *shared) \
static inline M_C(name,_t) \
M_C(name, _init_set)(M_C(name,_t) shared) \
{ \
if (M_LIKELY (shared != NULL)) { \
int n = atomic_fetch_add(&(shared->M_C(name, _cpt)), 1); \
@@ -94,45 +116,47 @@
} \
\
static inline void \
M_C(name, _init_set2)(type ** ptr, type *shared) \
M_C(name, _init_set2)(M_C(name,_t) *ptr, M_C(name,_t) shared) \
{ \
assert (ptr != NULL); \
*ptr = M_C(name, _init_set)(shared); \
} \
\
static inline type * \
M_C(name, _init_new)(void) \
M_IF_METHOD2(INIT, NEW, oplist)( \
static inline M_C(name,_t) \
M_C(name, _init_new)(void) \
{ \
type *ptr = M_GET_NEW oplist (type); \
type *ptr = M_CALL_NEW(oplist, type); \
if (ptr == NULL) { \
M_MEMORY_FULL(sizeof(type)); \
return NULL; \
} \
M_GET_INIT oplist (*ptr); \
M_CALL_INIT(oplist, *ptr); \
atomic_init (&ptr->M_C(name, _cpt), 1); \
return ptr; \
} \
, /* End of INIT/NEW */) \
\
static inline void \
M_C(name, _clear)(type *shared) \
M_C(name, _clear)(M_C(name,_t) shared) \
{ \
if (shared != NULL) { \
if (atomic_fetch_sub(&(shared->M_C(name, _cpt)), 1) == 1) { \
M_GET_CLEAR oplist (*shared); \
M_IF_DISABLED_METHOD(DEL, oplist)(, M_GET_DEL oplist (shared);) \
M_CALL_CLEAR(oplist, *shared); \
M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \
} \
} \
} \
\
static inline void \
M_C(name, _clean)(type **shared) \
M_C(name, _clean)(M_C(name,_t) *shared) \
{ \
M_C(name, _clear)(*shared); \
*shared = NULL; \
} \
\
static inline void \
M_C(name, _set)(type ** ptr, type *shared) \
M_C(name, _set)(M_C(name,_t) *ptr, M_C(name,_t)shared) \
{ \
assert (ptr != NULL); \
if (M_LIKELY (*ptr != shared)) { \
+969 -144
View File
File diff suppressed because it is too large. Load diff
+16 -12
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - MEMPOOL module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,14 +22,14 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_MEMPOOL_H
#define __M_MEMPOOL_H
#ifndef MSTARLIB_MEMPOOL_H
#define MSTARLIB_MEMPOOL_H
#include "m-core.h"
/* Fast Fixed Size thread unsafe allocator.
/* Fast Fixed Size thread unsafe allocator based on memory region.
USAGE:
MEMPOOL_DEF(uint, unsigned int)
MEMPOOL_DEF(memppol_uint, unsigned int)
...
memppol_uint_t m;
mempool_uint_init(m);
@@ -50,7 +50,7 @@
typedef struct M_C(name,_segment_s) { \
unsigned int count; \
struct M_C(name,_segment_s) *next; \
M_C(name,_union_t) tab[MEMPOOLI_MAX_PER_SEGMENT(type)]; \
M_C(name,_union_t) tab[MEMPOOL_MAX_PER_SEGMENT(type)]; \
} M_C(name,_segment_t); \
\
typedef struct M_C(name, _s) { \
@@ -99,7 +99,7 @@
M_C(name,_segment_t) *segment = mem->current_segment; \
assert(segment != NULL); \
unsigned int count = segment->count; \
if (M_UNLIKELY (count >= MEMPOOLI_MAX_PER_SEGMENT(type))) { \
if (M_UNLIKELY (count >= MEMPOOL_MAX_PER_SEGMENT(type))) { \
M_C(name,_segment_t) *new_segment = M_MEMORY_ALLOC (M_C(name,_segment_t)); \
if (M_UNLIKELY (new_segment == NULL)) { \
M_MEMORY_FULL(sizeof (M_C(name,_segment_t))); \
@@ -129,17 +129,21 @@
MEMPOOLI_CONTRACT(mem, type); \
} \
/* User shall be able to cutomize the size of the segment and/or
the minimun number of elements.
*/
#ifndef MEMPOOL_MAX_PER_SEGMENT
#define MEMPOOL_MAX_PER_SEGMENT(type) \
M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U)
#endif
/********************************** INTERNAL ************************************/
// TODO: user shall be able to cutomize the size of the segment and/or
// the minimun number of elements... i.e. all the formula!
#define MEMPOOLI_MAX_PER_SEGMENT(type) \
M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U)
#define MEMPOOLI_CONTRACT(mempool, type) do { \
assert((mempool) != NULL); \
assert((mempool)->current_segment != NULL); \
assert((mempool)->current_segment->count <= MEMPOOLI_MAX_PER_SEGMENT(type)); \
assert((mempool)->current_segment->count <= MEMPOOL_MAX_PER_SEGMENT(type)); \
} while (0)
#endif
+223 -53
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin Mutex & Thread wrapper
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,12 +22,8 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_MUTEX_H
#define __M_MUTEX_H
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
#ifndef MSTARLIB_MUTEX_H
#define MSTARLIB_MUTEX_H
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
&& !defined(__STDC_NO_THREADS__)
@@ -35,227 +31,374 @@
/****************************** C11 version ********************************/
#include <threads.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
/* Define a mutex type based on C11 definition */
typedef mtx_t m_mutex_t[1];
/* Define a condition variable type based on C11 definition */
typedef cnd_t m_cond_t[1];
/* Define a thread type based on C11 definition */
typedef thrd_t m_thread_t[1];
/* Initialize the mutex (constructor) */
static inline void m_mutex_init(m_mutex_t m)
{
int rc = mtx_init(m, mtx_plain);
M_ASSERT_INIT (rc == 0);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (rc == 0, "mutex");
}
/* Clear the mutex (destructor) */
static inline void m_mutex_clear(m_mutex_t m)
{
mtx_destroy(m);
}
/* Lock the mutex */
static inline void m_mutex_lock(m_mutex_t m)
{
mtx_lock(m);
}
/* Unlock the mutex */
static inline void m_mutex_unlock(m_mutex_t m)
{
mtx_unlock(m);
}
/* Initialize the condition variable (constructor) */
static inline void m_cond_init(m_cond_t c)
{
int rc = cnd_init(c);
M_ASSERT_INIT (rc == 0);
// Abort program in case of initialization failure
// There is really nothing else to do if the object cannot be constructed
M_ASSERT_INIT (rc == 0, "conditional variable");
}
/* Clear the condition variable (destructor) */
static inline void m_cond_clear(m_cond_t c)
{
cnd_destroy(c);
}
static inline void m_cond_clear(m_cond_t c)
/* Signal the condition variable to at least one waiting thread */
static inline void m_cond_signal(m_cond_t c)
{
cnd_signal(c);
}
/* Signal the condition variable to all waiting threads */
static inline void m_cond_broadcast(m_cond_t c)
{
cnd_broadcast(c);
}
/* Wait for signaling the condition variable by another thread */
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
{
cnd_wait(c, m);
}
/* Create the thread (constructor) and start it */
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
{
int rc = thrd_create(t, ((int)(*)(void*))func, arg);
M_ASSERT_INIT (rc == thrd_success);
int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
// Abort program in case of initialization failure
M_ASSERT_INIT (rc == thrd_success, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
static inline void m_thread_join(m_thread_t t)
{
int rc = thrd_join(t, NULL);
int rc = thrd_join(*t, NULL);
assert (rc == thrd_success);
// Avoid warning about variable unused.
(void) rc;
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
static inline void m_thread_yield(void)
{
thrd_yield();
}
/* Sleep the thread for at least usec microseconds.
Return true if the sleep was successful */
static inline bool m_thread_sleep(unsigned long long usec)
{
struct timespec tv;
tv.tv_sec = usec / 1000000ULL;
tv.tv_nsec = (usec % 1000000ULL) * 1000UL;
int retval = thrd_sleep(&tv, NULL);
return retval == 0;
}
// Internal type, not exported.
typedef once_flag m_oncei_t[1];
// Initial value for m_oncei_t
#define M_ONCEI_INIT_VALUE { ONCE_FLAG_INIT }
// Call the function exactly once
static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
{
call_once(o,func);
}
// Attribute to use to allocate a global variable to a thread.
#define M_THREAD_ATTR _Thread_local
#elif defined(WIN32) || defined(_WIN32)
// MSYS2 doesn't define _WIN32 by default.
#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
/****************************** WIN32 version ******************************/
/* CriticalSection & ConditionVariable are available from Windows Vista */
#ifndef WINVER
#define WINVER _WIN32_WINNT_VISTA
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
typedef HANDLE m_mutex_t[1];
typedef HANDLE m_cond_t[1];
/* Define a thread type based on WINDOWS definition */
typedef HANDLE m_thread_t[1];
/* Define a mutex type based on WINDOWS definition */
typedef CRITICAL_SECTION m_mutex_t[1];
/* Define a condition variable type based on WINDOWS definition */
typedef CONDITION_VARIABLE m_cond_t[1];
/* Initialize a mutex (Constructor)*/
static inline void m_mutex_init(m_mutex_t m)
{
*m = CreateMutex(NULL, FALSE, NULL);
M_ASSERT_INIT(*m != NULL);
InitializeCriticalSection(m);
}
/* Clear a mutex (destructor) */
static inline void m_mutex_clear(m_mutex_t m)
{
CloseHandle(*m);
DeleteCriticalSection(m);
}
/* Lock a mutex */
static inline void m_mutex_lock(m_mutex_t m)
{
DWORD dwWaitResult = WaitForSingleObject(*m, INFINITE);
assert (dwWaitResult == WAIT_OBJECT_0);
EnterCriticalSection(m);
}
/* Unlock a mutex */
static inline void m_mutex_unlock(m_mutex_t m)
{
ReleaseMutex(*m);
}
// Internal function, not exported
#define M_MUTEXI_INIT_VALUE { NULL }
static inline void m_mutexi_lazy_lock (m_mutex_t m)
{
HANDLE *addr = m;
if (*addr == NULL) {
/* Try to create one, affect it atomicaly and otherwise clears it */
HANDLE h = CreateMutex(NULL, FALSE, NULL);
if (InterlockedCompareExchangePointer((PVOID*)addr, (PVOID)h, NULL) != NULL)
CloseHandle(h);
/* FIXME: there is no way to clean the mutex at program exit... */
}
DWORD dwWaitResult = WaitForSingleObject(*addr, INFINITE);
assert (dwWaitResult == WAIT_OBJECT_0);
LeaveCriticalSection(m);
}
/* Initialize a condition variable (constructor) */
static inline void m_cond_init(m_cond_t c)
{
*c = CreateEvent (NULL, FALSE, FALSE, NULL);
M_ASSERT_INIT (*c != NULL);
InitializeConditionVariable(c);
}
/* Clear a condition variable (destructor) */
static inline void m_cond_clear(m_cond_t c)
{
CloseHandle(*c);
(void) c; // There is no destructor for this object.
}
/* Signal a condition variable to at least one waiting thread */
static inline void m_cond_signal(m_cond_t c)
{
PulseEvent(*c);
WakeConditionVariable(c);
}
/* Signal a condition variable to all waiting threads */
static inline void m_cond_broadcast(m_cond_t c)
{
WakeAllConditionVariable(c);
}
/* Wait for a condition variable */
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
{
DWORD dwWaitResult = SignalObjectAndWait(*m, *c, INFINITE,FALSE);
assert (dwWaitResult == WAIT_OBJECT_0);
dwWaitResult = WaitForSingleObject(*m, INFINITE);
assert (dwWaitResult == WAIT_OBJECT_0);
SleepConditionVariableCS(c, m, INFINITE);
}
/* Create a thread (constructor) and start it */
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");
}
/* Wait for the thread to terminate and destroy it (destructor) */
static inline void m_thread_join(m_thread_t t)
{
DWORD dwWaitResult = WaitForSingleObject(*t, INFINITE);
(void) dwWaitResult;
assert (dwWaitResult == WAIT_OBJECT_0);
CloseHandle(*t);
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
static inline void m_thread_yield(void)
{
Sleep(0);
}
/* Sleep the thread for at least usec microseconds
Return true if the sleep was successful */
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, "timer");
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
CloseHandle(hd);
return dwWaitResult == WAIT_OBJECT_0;
}
// Internal type, not exported.
typedef INIT_ONCE m_oncei_t[1];
#define M_ONCEI_INIT_VALUE { INIT_ONCE_STATIC_INIT }
static inline BOOL CALLBACK m_oncei_callback( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
{
void (*func)(void);
(void) InitOnce;
(void) lpContext;
func = (void (*)(void))(uintptr_t) Parameter;
(*func)();
return TRUE;
}
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)
// Attribute to use to allocate a global variable to a thread (MSVC def).
# define M_THREAD_ATTR __declspec( thread )
#else
// Attribute to use to allocate a global variable to a thread (GCC def).
# define M_THREAD_ATTR __thread
#endif
#else
/**************************** PTHREAD version ******************************/
#include <pthread.h>
#ifdef _POSIX_PRIORITY_SCHEDULING
#include <sched.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
/* Define a mutex type based on PTHREAD definition */
typedef pthread_mutex_t m_mutex_t[1];
/* Define a condition variable type based on PTHREAD definition */
typedef pthread_cond_t m_cond_t[1];
/* Define a thread type based on PTHREAD definition */
typedef pthread_t m_thread_t[1];
/* Initialize the mutex (constructor) */
static inline void m_mutex_init(m_mutex_t m)
{
int _rc = pthread_mutex_init(m, NULL);
M_ASSERT_INIT (_rc == 0);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (_rc == 0, "mutex");
}
/* Clear the mutex (destructor) */
static inline void m_mutex_clear(m_mutex_t m)
{
pthread_mutex_destroy(m);
}
/* Lock the mutex */
static inline void m_mutex_lock(m_mutex_t m)
{
pthread_mutex_lock(m);
}
/* Unlock the mutex */
static inline void m_mutex_unlock(m_mutex_t m)
{
pthread_mutex_unlock(m);
}
/* Lazy lock initialization */
#define M_MUTEXI_INIT_VALUE { PTHREAD_MUTEX_INITIALIZER }
/* Internal function compatible with lazy lock */
static inline void m_mutexi_lazy_lock(m_mutex_t m)
{
pthread_mutex_lock(m);
}
/* Initialize the condition variable (constructor) */
static inline void m_cond_init(m_cond_t c)
{
int _rc = pthread_cond_init(c, NULL);
M_ASSERT_INIT (_rc == 0);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (_rc == 0, "conditional variable");
}
/* Clear the condition variable (destructor) */
static inline void m_cond_clear(m_cond_t c)
{
pthread_cond_destroy(c);
}
/* Signal a condition variable to at least a waiting thread */
static inline void m_cond_signal(m_cond_t c)
{
pthread_cond_signal(c);
}
/* Signal a condition variable to all waiting threads */
static inline void m_cond_broadcast(m_cond_t c)
{
pthread_cond_broadcast(c);
}
/* Waiting for a condition variable */
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
{
pthread_cond_wait(c, m);
}
/* Create a thread (constructor) and start it */
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
int _rc = pthread_create(t, NULL, (void*(*)(void*))func, arg);
M_ASSERT_INIT (_rc == 0);
int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (_rc == 0, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
static inline void m_thread_join(m_thread_t t)
{
int _rc = pthread_join(*t, NULL);
@@ -263,6 +406,28 @@ static inline void m_thread_join(m_thread_t t)
assert (_rc == 0);
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
static inline void m_thread_yield(void)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield();
#endif
}
/* Sleep for at least usec microseconds
Return true if the sleep was successful */
static inline bool m_thread_sleep(unsigned long long usec)
{
struct timeval tv;
/* We don't want to use usleep or nanosleep so that
we remain compatible with strict C99 build */
tv.tv_sec = usec / 1000000ULL;
tv.tv_usec = usec % 1000000ULL;
int retval = select(1, NULL, NULL, NULL, &tv);
return retval == 0;
}
// Internal type, not exported.
typedef pthread_once_t m_oncei_t[1];
#define M_ONCEI_INIT_VALUE { PTHREAD_ONCE_INIT }
@@ -271,8 +436,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);
+200 -26
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic priority queue module
*
* Copyright (c) 2017, Patrick Pelissier
* Copyright (c) 2017-2020, 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:
@@ -22,37 +22,50 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __M_PRIOQUEUE_H
#define __M_PRIOQUEUE_H
#ifndef MSTARLIB_PRIOQUEUE_H
#define MSTARLIB_PRIOQUEUE_H
#include "m-core.h"
#include "m-array.h"
/* Priority queue based on binary heap implementation */
/* Define an prioqueue of a given type.
/* 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) ), \
(name, __VA_ARGS__, M_C(name,_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 an prioqueue of type.
/* 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_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
(__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_OPLIST2(name, oplist) \
/* Validation of the given oplist */
#define PRIOQUEUEI_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(PRIOQUEUEI_OPLIST_P3, PRIOQUEUEI_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define PRIOQUEUEI_OPLIST_FAILURE(name, oplist) \
M_LIB_ERROR(ARGUMENT_OF_PRIOQUEUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)
/* Define oplist of a priority queue */
#define PRIOQUEUEI_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,INIT_MOVE(M_C(name, _init_move)) \
@@ -64,9 +77,31 @@
,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) \
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define PRIOQUEUEI_DEF_P1(arg) PRIOQUEUEI_DEF_P2 arg
/* Validate the oplist before going further */
#define PRIOQUEUEI_DEF_P2(name, type, oplist, prioqueue_t, it_t) \
M_IF_OPLIST(oplist)(PRIOQUEUEI_DEF_P3, PRIOQUEUEI_DEF_FAILURE)(name, type, oplist, prioqueue_t, it_t)
/* Stop processing with a compilation failure */
#define PRIOQUEUEI_DEF_FAILURE(name, type, oplist, prioqueue_t, it_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(PRIOQUEUE_DEF): the given argument is not a valid oplist: " #oplist)
/* 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_P3(name, type, oplist, prioqueue_t, it_t) \
ARRAY_DEF(M_C(name, _array), type, oplist) \
\
typedef type M_C(name, _type_t); \
@@ -74,6 +109,12 @@
typedef struct M_C(name, _s) { \
M_C(name, _array_t) array; \
} prioqueue_t[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
\
typedef M_C(name, _array_it_t) it_t; \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(prioqueue_t p) \
@@ -82,13 +123,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); \
} \
@@ -145,20 +186,20 @@
} \
\
static inline int \
M_C(name, _i_cmp)(prioqueue_t p, size_t i, size_t j) \
M_C(name, _i_cmp)(const prioqueue_t p, size_t i, size_t j) \
{ \
return M_GET_CMP oplist (*M_C(name, _array_get)(p->array, i), \
*M_C(name, _array_get)(p->array, j)); \
return M_CALL_CMP(oplist, *M_C(name, _array_cget)(p->array, i), \
*M_C(name, _array_cget)(p->array, j)); \
} \
\
static inline bool \
M_C(name, _empty_p)(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)(prioqueue_t p) \
M_C(name, _size)(prioqueue_t const p) \
{ \
return M_C(name, _array_size)(p->array); \
} \
@@ -178,10 +219,10 @@
} \
} \
\
static inline type * \
M_C(name, _front)(prioqueue_t p) \
static inline type const * \
M_C(name, _front)(prioqueue_t const p) \
{ \
return M_C(name, _array_get)(p->array, 0); \
return M_C(name, _array_cget)(p->array, 0); \
} \
\
static inline void \
@@ -205,6 +246,139 @@
i = child; \
} \
} \
\
static inline void \
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, prioqueue_t const v) \
{ \
M_C(name, _array_it_last)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_end)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it_end)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_set)(it_t it, const it_t org) \
{ \
M_C(name, _array_it_set)(it, org); \
} \
\
static inline bool \
M_C(name, _end_p)(const it_t it) \
{ \
return M_C(name, _array_end_p)(it); \
} \
\
static inline bool \
M_C(name, _last_p)(const it_t it) \
{ \
return M_C(name, _array_last_p)(it); \
} \
\
static inline bool \
M_C(name, _it_equal_p)(const it_t it1, \
const it_t it2) \
{ \
return M_C(name, _array_it_equal_p)(it1, it2); \
} \
\
static inline void \
M_C(name, _next)(it_t it) \
{ \
M_C(name, _array_next)(it); \
} \
\
static inline void \
M_C(name, _previous)(it_t it) \
{ \
M_C(name, _array_previous)(it); \
} \
\
static inline type const * \
M_C(name, _cref)(const it_t it) \
{ \
return M_C(name, _array_cref)(it); \
} \
\
M_IF_METHOD(EQUAL, oplist) \
( \
/* EQUAL & CMP may be uncorrelated */ \
static inline bool \
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++) { \
if (M_CALL_EQUAL(oplist, *M_C(name, _array_cget)(p->array, i), x)) \
break; \
} \
/* If x is not found, then stop */ \
if (i >= size) \
return false; \
/* Swap the found item and the last element */ \
size--; \
M_C(name, _array_swap_at) (p->array, i, size); \
M_C(name, _array_pop_back)(NULL, p->array); \
/* Move back the last swapped element to its right position in the heap */ \
while (true) { \
size_t child = M_C(name, _i_lchild)(i); \
if (child >= size) break; \
size_t otherChild = M_C(name, _i_rchild)(i); \
if (otherChild < size \
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
child = otherChild; \
} \
if (M_C(name, _i_cmp)(p, i, child) <= 0) break; \
M_C(name, _array_swap_at) (p->array, i, child); \
i = child; \
} \
return true; \
} \
\
static inline void \
M_C(name, _update_further)(prioqueue_t p, type xold, type xnew) \
{ \
/* This update assumes that the new position is further in the heap */ \
assert (M_CALL_CMP(oplist, xold, xnew) > 0); \
/* First pass: search for an item EQUAL to x */ \
size_t size = M_C(name, _array_size)(p->array); \
size_t i = 0; \
for(i = 0; i < size; i++) { \
if (M_CALL_EQUAL(oplist, *M_C(name, _array_cget)(p->array, i), xold)) \
break; \
} \
/* We shall have found the item */ \
assert (i < size); \
/* Set the found item to the new element */ \
M_C(name, _array_set_at) (p->array, i, xnew); \
/* Move back the updated element to its right position in the heap */ \
while (true) { \
size_t child = M_C(name, _i_lchild)(i); \
if (child >= size) break; \
size_t otherChild = M_C(name, _i_rchild)(i); \
if (otherChild < size \
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
child = otherChild; \
} \
if (M_C(name, _i_cmp)(p, i, child) <= 0) break; \
M_C(name, _array_swap_at) (p->array, i, child); \
i = child; \
} \
} \
\
, /* No EQUAL */ ) \
// TODO: set all & remove all function
#endif
+414 -251
View File
File diff suppressed because it is too large. Load diff
Loaded 100 of 147 files, more files were not shown because too many files have changed in this diff. Show more