412 Commits
Author SHA1 Message Date
Patrick Pelissier 46759b5f2c Rename _push_raw method to _push_back_raw method (Cleaner semantic) 2025-07-26 10:35:48 +02:00
Patrick Pelissier 8c8a1e2822 Fix bug reported by florommel on dict's reserve 2025-07-18 20:11:13 +02:00
Patrick Pelissier ad646f5e04 Update links 2025-07-01 22:17:49 +02:00
Patrick Pelissier 9ecc031fd7 Fix doc 2025-06-10 22:18:24 +02:00
Patrick Pelissier ea3c2c5019 Update doc 2025-06-10 22:15:34 +02:00
Patrick Pelissier a9dc2eef12 Update doc 2025-05-31 00:11:49 +02:00
Patrick Pelissier 39a0a4f672 Fix format 2025-05-29 15:06:36 +02:00
Patrick Pelissier 4ffa4a15ac Rename example 2025-05-29 15:01:21 +02:00
Patrick Pelissier 2d5c9e71ae Add new example 2025-05-29 14:58:25 +02:00
Patrick Pelissier e802e22b7b Port to new API 2025-05-29 14:58:09 +02:00
Patrick Pelissier 3fbe8ec0b6 Update _transform, _reduce and _map_reduce to be more flexible
Rename _map_reduce to _transform_reduce
2025-05-29 10:41:20 +02:00
Patrick Pelissier 34085aa637 WIP to allow recursive variant 2025-05-29 10:40:19 +02:00
Patrick Pelissier 77033e2020 Move to V0.8.1 2025-05-29 10:39:42 +02:00
Patrick Pelissier 5c57ad1f51 Add example on tree 2025-05-24 17:40:29 +02:00
Patrick Pelissier 27621db95d Add method _left, right, _up and _down for tree to get directly an iterator to the requested element 2025-05-24 17:38:57 +02:00
Patrick Pelissier eab3f701a8 Fix displayed size in case of overflow computation 2025-05-24 10:18:15 +02:00
Patrick Pelissier cbc5f17cf0 Update Makefile and dependencies 2025-05-11 11:22:49 +02:00
PpHd 02a39fce3f Merge pull request #134 from P-p-H-d/feature/new-memory-model
New memory model
2025-05-08 22:14:17 +02:00
Patrick Pelissier 7c4f94a674 Remove obsolete m-mempool.h header 2025-05-07 22:09:56 +02:00
Patrick Pelissier 8c70a1f96c Remove obsolete m-concurrent.h header 2025-05-07 22:01:38 +02:00
Patrick Pelissier 25dcdeed69 Remove obsolete m-i-shared.h header 2025-05-07 21:57:53 +02:00
Patrick Pelissier 7e8cb07a6c Port to shared-ptr interface 2025-05-07 21:50:26 +02:00
Patrick Pelissier 2296f203fd Remove obsolete m-shared.h header 2025-05-07 21:34:18 +02:00
Patrick Pelissier 790ae33334 Remove obsolete m-c-mempool.h header 2025-05-07 21:28:13 +02:00
Patrick Pelissier d795476157 Update version 2025-05-07 21:02:55 +02:00
Patrick Pelissier 6a2bdc49fb Update API incompatibility & documentation 2025-05-07 21:02:24 +02:00
Patrick Pelissier 725f42852e Update markdown 2025-05-07 21:02:24 +02:00
Patrick Pelissier bfcc4134d6 Fix markdown 2025-05-07 21:02:24 +02:00
Patrick Pelissier de6ccc293e Update doc
Update M_USE_GLOBAL_CONTEXT definition to be simpler.
2025-05-07 21:02:24 +02:00
Patrick Pelissier d9953daf59 Update doc 2025-05-07 21:02:24 +02:00
Patrick Pelissier 1a62f5a4ed Update doc 2025-05-07 21:02:24 +02:00
Patrick Pelissier 3721129da1 Update prototype of memory functions
to always get the memory context parameter
(even if it is not needed) to provide a cleaner
and simpler user interface
(instead of requesting the user to use directly m_context).
2025-05-07 21:02:24 +02:00
Patrick Pelissier f585167fa8 Simplify code (remove redundant intermediary macro) 2025-05-07 21:02:24 +02:00
Patrick Pelissier 6d5d34fb72 Update 2025-05-07 21:02:24 +02:00
Patrick Pelissier 196db1cf62 Improve test suite 2025-05-07 21:02:24 +02:00
Patrick Pelissier 174c62015b Workaround wrong analyze by GCC analyzer 2025-05-07 21:02:24 +02:00
Patrick Pelissier fb3140e645 Fix warning of unused variable. 2025-05-07 21:02:24 +02:00
Patrick Pelissier 431e3a1228 Update doc (WIP) 2025-05-07 21:02:24 +02:00
Patrick Pelissier 3ddf9438da Update 2025-05-07 21:02:24 +02:00
Patrick Pelissier 6e8aa58bd6 Update doc 2025-05-07 21:02:24 +02:00
Patrick Pelissier df37a5d8f1 Fix 2025-05-07 21:02:24 +02:00
Patrick Pelissier 2ad8d42401 Update summary 2025-05-07 21:02:24 +02:00
Patrick Pelissier 8f47fb35ae Use of context for nearly all M*LIB 2025-05-07 21:02:24 +02:00
Patrick Pelissier a1a5ae749d Fix oplist of bitset 2025-05-07 21:02:24 +02:00
Patrick Pelissier 147bc0a363 Fix ALGO and oplist doesn't providing IT_REF 2025-05-07 21:02:24 +02:00
Patrick Pelissier 33aa102b7c Port DualPush list to new format 2025-05-07 21:02:24 +02:00
Patrick Pelissier bd9485a3fe Port algo to new memory format 2025-05-07 21:02:24 +02:00
Patrick Pelissier 0de3c622e3 Port algo to new memory format 2025-05-07 21:02:24 +02:00
Patrick Pelissier 93a1be616d Rename parameter pool to context 2025-05-07 21:02:24 +02:00
Patrick Pelissier 692c0487af Update oplist for new memory model 2025-05-07 21:02:24 +02:00
Patrick Pelissier 22ccae74c3 Minor optimization of stable_sort 2025-05-07 21:02:24 +02:00
Patrick Pelissier 586d0ae13a Update documentation 2025-05-07 21:02:24 +02:00
Patrick Pelissier c7bad98a7e Port ARRAY oplist to support local memcontext 2025-05-07 21:02:24 +02:00
Patrick Pelissier 8e3a06b852 Simplify OPLIST definition as INIT_MOVE is mandatory for arrays 2025-05-07 21:02:24 +02:00
Patrick Pelissier d548117538 Make M*LIB stricter for INIT_MOVE operator:
* either the default INIT_MOVE operator works ('=')
* either an INIT_MOVE method is defined
* or it is disabled.
As a consequence, fix usage of testobj_t and mpz_t in the repository

The emulation layer of INIT_MOVE using INIT_SET+CLEAR is removed:
it wasn't working correctly with the local memory context
(INIT_MOVE doesn't need any local memory context to work,
 whereas INIT_SET/CLEAR does)
Remove macro M_DO_INIT_MOVE as it is now useless and uses M_CALL_INIT_MOVE.

The INIT_MOVE default method no longer sets the old 'src' to '0':
it was useless (it doesn't catch any error) and complicates compiler parsing.

Fix POD & A1 & CSTR & classic oplists definition (missing INIT_MOVE operators).
2025-05-07 21:02:24 +02:00
Patrick Pelissier 3a39ce5a4a Reformat 2025-05-07 21:02:24 +02:00
Patrick Pelissier fa41f3affd M_INIT_VAI supports API 2025-05-07 21:02:24 +02:00
Patrick Pelissier 0d0747dea9 Update oplists 2025-05-07 21:02:24 +02:00
Patrick Pelissier ab75397e21 TCC now supports m-generic 2025-05-07 21:02:24 +02:00
Patrick Pelissier cc97aa887f Fix reference 2025-05-07 21:02:24 +02:00
Patrick Pelissier b275859a37 Fix issue if BString is empty 2025-05-07 21:02:24 +02:00
Patrick Pelissier 256ff916a2 Port serial to new memory format 2025-05-07 21:02:24 +02:00
Patrick Pelissier 77195fb1d0 Remove m-mutex header 2025-05-07 21:02:24 +02:00
Patrick Pelissier c2cfb2f0a0 Cleanup & support of M_USE_GLOBAL_POOL 2025-05-07 21:02:24 +02:00
Patrick Pelissier 03cceafd73 Update 2025-05-07 21:02:24 +02:00
Patrick Pelissier 505e2c4a62 Fix spelling 2025-05-07 21:02:24 +02:00
Patrick Pelissier daadf693f7 Update doc 2025-05-07 21:02:24 +02:00
Patrick Pelissier 98b3bbd7a1 Fix for MACOS 2025-05-07 21:02:24 +02:00
Patrick Pelissier decec8f7ae Fix for tcc 2025-05-07 21:02:24 +02:00
Patrick Pelissier ce3f674ba8 Add new header m-queue (Doc to do) 2025-05-07 21:02:24 +02:00
Patrick Pelissier 01f5228a36 Port to new memory model:
Communication thread buffer needs to use the global pool,
and not a local pool, since the allocation is done accross
several threads.
Therefore the non thread fixed queue & stack interfaces are moved into
a dedicated header (m-queue)
and remove support for non thread stack & queue into m-buffer
Remove also deprecated policy too.
2025-05-07 21:02:24 +02:00
Patrick Pelissier a7294abe0c Update documentation and error message 2025-05-07 21:02:24 +02:00
Patrick Pelissier 77e5666802 Update string oplist definition with pool 2025-05-07 21:02:24 +02:00
Patrick Pelissier 689527c2dc Port m-shared-ptr to new memory model
(use global pool)
2025-05-07 21:02:24 +02:00
Patrick Pelissier 5d1ebdf5bb Update obsolete header files:
* remove obsolete m-mutex header file
* mark m-concurrent, m-shared and m-i-shared as obsolete (They won't support new memory model)
2025-05-07 21:02:24 +02:00
Patrick Pelissier 10d918bbc9 Port to new memory model (add global context 0) 2025-05-07 21:02:24 +02:00
Patrick Pelissier 8069fae3d5 Remove use of invalid representation, which is refused by clang & C++ 2025-05-07 21:02:24 +02:00
Patrick Pelissier 94dd0a5571 Add example of using pool 2025-05-07 21:02:24 +02:00
Patrick Pelissier 32a3d95104 Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier d08aeb32fe Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 26fc2bcffe Port tree to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 7617048073 Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 57e7510ae7 Format 2025-05-07 21:02:23 +02:00
Patrick Pelissier 324ba5d256 Port to new memory model: M_MEMORY_FULL cannot return anymore 2025-05-07 21:02:23 +02:00
Patrick Pelissier 8939b4d046 New memory model is for 0.8.0 version 2025-05-07 21:02:23 +02:00
Patrick Pelissier 1cf7c4fa4d Port to new memory model: M_MEMORY_FULL cannot return anymore 2025-05-07 21:02:23 +02:00
Patrick Pelissier 839e7c2957 Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 06e3046268 Port to new memory interface 2025-05-07 21:02:23 +02:00
Patrick Pelissier e959c001aa Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier bcc9f2c894 Port B+TREE to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 6e828eabaf Port list to new memory pool
Remove support for mempool. A new support for mempool should be provided later through the new memory context parameter (TODO / better API)
2025-05-07 21:02:23 +02:00
Patrick Pelissier 7677b13d8a Update contract comment. 2025-05-07 21:02:23 +02:00
Patrick Pelissier e16c6987e4 Port to new memory interface (remove mempool support, but it was undocumented) 2025-05-07 21:02:23 +02:00
Patrick Pelissier aa4cbfebdb Port to new memory interface 2025-05-07 21:02:23 +02:00
Patrick Pelissier 66f376ea80 Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 6de8eab5c3 Add comments, rename macro for maintenance 2025-05-07 21:02:23 +02:00
Patrick Pelissier 9e63289a38 Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier bf617ab6ab Port to new memory interface (WIP) 2025-05-07 21:02:23 +02:00
Patrick Pelissier 3f8c30e592 Port ARRAY & STRING to new memory model with memory context support 2025-05-07 21:02:23 +02:00
Patrick Pelissier 37b2f4d2ac Update test suite to properly allocation & deallocation 2025-05-07 21:02:23 +02:00
Patrick Pelissier 50e8d1f469 Fix old size parameter for memory allocation 2025-05-07 21:02:23 +02:00
Patrick Pelissier d40a6e5bc3 Document limitation 2025-05-07 21:02:23 +02:00
Patrick Pelissier b4c29d5423 Update spec 2025-05-07 21:02:23 +02:00
Patrick Pelissier 9f4f9d1d5d Update 2025-05-07 21:02:23 +02:00
Patrick Pelissier 1e196fe22a Port to new spec M_MEMORY_FULL 2025-05-07 21:02:23 +02:00
Patrick Pelissier 450d2c9000 Update spec of M_MEMORY_FULL 2025-05-07 21:02:23 +02:00
Patrick Pelissier 93112a5a7c Update example 2025-05-07 21:02:23 +02:00
Patrick Pelissier 1f2584ccea Port to new memory model 2025-05-07 21:02:23 +02:00
Patrick Pelissier 5b4c9cd618 Update spec of memory functions 2025-05-07 21:02:23 +02:00
Patrick Pelissier f4e3d9cd4c Release V0.7.4 2025-05-07 21:02:01 +02:00
Patrick Pelissier 4988661b7d Fix target armv6 (2) for Continuous Integration 2025-05-06 00:47:29 +02:00
Patrick Pelissier 7033a922c7 Fix target armv6 for Continuous Integration 2025-05-05 23:50:43 +02:00
Patrick Pelissier dd607252eb Add target risc64, ppc64le and armv6 for Continuous Integration 2025-05-05 23:46:09 +02:00
Patrick Pelissier 73da9a4316 Update obsolete header section 2025-04-18 21:58:15 +02:00
Patrick Pelissier b3a0d70147 Fix memory safety errors in examples. 2025-04-08 21:46:16 +02:00
Patrick Pelissier c55bb6b9ad Fix random segfault on AARCH64 CI 2025-03-13 22:24:13 +01:00
Patrick Pelissier 68fdc2dde1 Fix undefined behavior in using setjmp 2025-03-06 15:38:44 +01:00
Patrick Pelissier 8877af10b0 Update copyright 2025-03-03 23:45:04 +01:00
Patrick Pelissier 782963dec5 Add example of disabling small name usage 2025-03-01 12:20:28 +01:00
Patrick Pelissier 36d11bc2bd Fix small name usage 2025-03-01 12:19:35 +01:00
Patrick Pelissier 6d621703eb Copyright go to 2025 year 2025-03-01 11:03:38 +01:00
Patrick Pelissier 76b40d247d Fix #133 : forgotten const for second argument (reported by Brian Sipos) 2025-02-03 23:12:22 +01:00
Patrick Pelissier 033deac9f5 Workaround for GCC 12 bug in warning 2025-02-03 23:06:41 +01:00
Patrick Pelissier b60553165d Fix usage of M_USE_SMALL_NAME (Reported by Brian Sipos) 2025-02-03 22:33:07 +01:00
Patrick Pelissier e1a4d359a2 Update doc 2025-02-02 10:50:41 +01:00
Patrick Pelissier 4ab2615a1c Clarify constraint on _splice 2024-12-25 20:48:34 +01:00
Patrick Pelissier 9136a4a972 Update 2024-12-08 19:34:15 +01:00
Patrick Pelissier 55dcf0e438 Fix example 2024-11-30 13:04:45 +01:00
Patrick Pelissier fb604f02b7 Update / add todo 2024-11-30 10:26:11 +01:00
Patrick Pelissier 1db417e30e Rename _new_copy into _clone 2024-11-30 10:25:51 +01:00
Patrick Pelissier 5b77fe7207 Add some missing const in the interface 2024-11-30 10:25:36 +01:00
Patrick Pelissier dd509f2b7e Update documentation of new shared pointer interface. 2024-11-30 10:25:15 +01:00
Patrick Pelissier 50d4e15297 Simplify lock as mutex should be released by the thread that performed the initial lock to be portable 2024-11-26 22:26:25 +01:00
Patrick Pelissier f6d1b16338 Fix C++ warning 2024-11-25 00:04:54 +01:00
Patrick Pelissier 64a035559d Update test suite 2024-11-24 23:57:22 +01:00
Patrick Pelissier c4bc37560c Improve testing of new shared pointer
Adding measure of coverage. Need to move some codes outside the main C file, to avoid polluting the measure of the coverage by some functions we don't need to measure.
2024-11-24 18:21:21 +01:00
Patrick Pelissier e7309b7d3a Add forgotten "const" attribute to string_length_u and string_utf8_p 2024-11-24 18:20:19 +01:00
Patrick Pelissier c461b4eca4 Update shared pointer new interface 2024-11-24 11:26:30 +01:00
Patrick Pelissier f21d427de1 Convert example to new shared ptr interface 2024-11-16 18:02:13 +01:00
Patrick Pelissier 0f9edaa7ee Fix oplist usage of new shared-ptr 2024-11-16 18:01:58 +01:00
Patrick Pelissier 64888127c4 Update to get the lastest v2 2024-11-16 16:55:42 +01:00
Patrick Pelissier d5b4ec249b Update test suite 2024-11-16 13:26:21 +01:00
Patrick Pelissier 0bcd78a8f5 Fix warning. 2024-11-12 22:31:05 +01:00
Patrick Pelissier 27b0850af4 Fix case of the same variable is passed as 2/3 arguments of a function. 2024-11-12 22:26:45 +01:00
Patrick Pelissier ae55ccfc40 Update test to consider change of default for deque 2024-11-11 23:56:33 +01:00
Patrick Pelissier 5bf644ccf0 Improve test suite 2024-11-11 22:38:10 +01:00
Patrick Pelissier 3f09adc507 Fix ADD/SUB/MUL/DIV methods of a shared pointer 2024-11-11 22:38:02 +01:00
Patrick Pelissier 4e851142c2 Change the operator POP of a DEQUE to pop_front instead of pop_back:
it is more inline with what we expects / uses a deque for.
(buffer FIFO)
2024-11-11 22:36:56 +01:00
Patrick Pelissier fec6b42260 Add comment 2024-11-11 21:47:54 +01:00
Patrick Pelissier 9dbc48aa5d Update 2024-11-09 19:07:08 +01:00
Patrick Pelissier b92bae34eb Update comments 2024-11-09 14:54:16 +01:00
Patrick Pelissier 8faf7f79c4 Add first test cases for m-shared-ptr 2024-11-09 14:27:16 +01:00
Patrick Pelissier c3e29a16df Fix warnings 2024-11-09 14:26:57 +01:00
Patrick Pelissier 1bc33a10fc Fix issues (WIP) 2024-11-09 11:12:12 +01:00
Patrick Pelissier b7074d1c69 Fix some issues 2024-11-06 23:13:23 +01:00
Patrick Pelissier 26d541fe06 Move internal functions to internal namespace 2024-11-05 23:17:28 +01:00
Patrick Pelissier 2991c7cc5a Add oplist 2024-11-02 23:35:21 +01:00
Patrick Pelissier 4e7cbae766 Add short name support 2024-11-02 17:35:25 +01:00
Patrick Pelissier d5e0620e4c Minor fix 2024-11-02 17:21:59 +01:00
Patrick Pelissier 32cfbd23bc Update comments. 2024-11-02 16:59:40 +01:00
Patrick Pelissier 0e09681a6a Move m_typeof to core module. 2024-10-30 23:42:45 +01:00
Patrick Pelissier bc8098b337 Update comments. 2024-10-30 23:31:47 +01:00
Patrick Pelissier c5692e7e9f Update comment 2024-10-28 17:46:48 +01:00
Patrick Pelissier f43db93791 Adding first WIP version of the new shared pointer header. 2024-10-27 22:01:00 +01:00
Patrick Pelissier 14c954c6ce Adding new property:
multithread safety if the container handles itself the multi thread
2024-10-27 20:38:36 +01:00
Patrick Pelissier 60280087c7 Fix default of FULL_P 2024-10-27 20:37:57 +01:00
Patrick Pelissier 1e57d9c6d6 Fix default of FULL_P 2024-10-27 19:48:47 +01:00
Patrick Pelissier eeaf74d24f Add FULL_P operator
Add M_EMPLACE_LIST_TYPE_VAR_ALTER which doesn't put a comma before.
2024-10-26 16:56:54 +02:00
Patrick Pelissier a318d36c35 Fix concurrent access. 2024-10-19 11:55:41 +02:00
Patrick Pelissier edcf8ee37a Final update of the byte string 2024-10-05 11:40:23 +02:00
Patrick Pelissier 8a1f30eeff Finish implementation of Byte String 2024-10-05 10:23:47 +02:00
Patrick Pelissier 2995b119b7 Add precision 2024-10-05 10:21:19 +02:00
Patrick Pelissier 08bd5a888b Fix formatting 2024-10-05 10:21:09 +02:00
Patrick Pelissier 2f1b17f805 Fix for clang 2024-09-26 22:36:45 +02:00
Patrick Pelissier 0379f5f1c6 Add header for handling Byte String as per #123
API designed for streaming bytes and easy integration with other libraries.
2024-09-26 22:33:03 +02:00
Patrick Pelissier 45b11fb49b Fix #129:
Support of incomplete type in m-shared.
2024-09-21 12:39:29 +02:00
Patrick Pelissier 82ea4341de Fix documentation (#128) 2024-08-19 22:02:56 +02:00
Patrick Pelissier 341d145b4d Fix internal terminoligy 2024-07-26 23:22:58 +02:00
Patrick Pelissier 847f52fa5e Fix clang warning 2024-07-11 22:07:51 +02:00
Patrick Pelissier 02828a0109 Fix interface of Intrusive List
(Reported by Brian Sipos):
* incorrect clear operator usage
* useless prototype of _insert, inconsistent with the policy of other functions
* syntax error in _insert
2024-07-11 21:41:14 +02:00
Patrick Pelissier 0f7d5bf366 Update doc 2024-06-15 09:51:51 +02:00
Patrick Pelissier 1277c75ea3 Update doc 2024-06-15 09:47:10 +02:00
Patrick Pelissier 47a51ee5c4 Update doc 2024-06-15 09:45:13 +02:00
Patrick Pelissier 3f102c2270 Use of memcmp instead of strcmp for string_equal_p 2024-06-12 23:15:56 +02:00
Patrick Pelissier 4ba55dc3ca Rename new USE directive to be uniform with string one. 2024-06-08 00:08:27 +02:00
Patrick Pelissier 2c0db40158 Simplify logic for set_at of both dict,
(after analyzing which condition is likely)
which should help code size & performance
a little bit.
2024-06-01 23:49:08 +02:00
Patrick Pelissier 8987288a10 Fix spelling 2024-06-01 11:05:35 +02:00
Patrick Pelissier cf8c2afddd Fix warning on previous modification 2024-06-01 11:03:39 +02:00
Patrick Pelissier 0a8e336037 Fix internal & subtil issue
when comparing two methods to check for their presence in an oplist, we may generate the name of an already existing macro.
For example, in the oplist (SIZE())
when we expanded we get the macro M_ADD_SIZE to check if ADD is present,
but M_ADD_SIZE is an already existing macro, which generates an error.
To avoid this issue, define a prefix for the definition of the operators (M_X)
2024-06-01 10:47:21 +02:00
Patrick Pelissier e7e29d913e Add new example of variant 2024-06-01 10:16:59 +02:00
Patrick Pelissier f3ebc8c7eb Update comments 2024-06-01 10:16:47 +02:00
Patrick Pelissier 2d4fc9dc97 Allow generation of EMPLACE_TYPE
for variant if all oplists defined a FIELD operator.
2024-06-01 10:16:39 +02:00
Patrick Pelissier afedb06dee Fix buffer overflow in 32 bits mode 2024-05-28 23:31:26 +02:00
Patrick Pelissier 3fd2146bbe Fix issue in 32 bits mode 2024-05-28 23:31:16 +02:00
Patrick Pelissier 8e70b6175a Fix clang warning 2024-05-28 23:14:44 +02:00
Patrick Pelissier 67e9c8ff06 New internal string format:
* maximum size of a string is 2^32-1 now by default on a 64 bits system (can be increased using M_USE_STRING_LARGE_INDEX if needed).
* This reduce consumption of a string_t from 24 bytes to 16 bytes.
* Change the stack representation of a string, so that it lets 15 allocated bytes for the user even in 64 bytes mode. For 32 bits system, it increases to 15 allocated byte for stack representation.
* synthetic performance benchmark shows a little bit of decrease of performance. But performance in real application shows a significant increase (probably due to the reduced size of the type).
*Augment test suite to be sure to test all corner cases of the new representaiton.
2024-05-28 23:05:54 +02:00
Patrick Pelissier bf59c53921 Migrate to V0.7.4 2024-05-27 00:39:00 +02:00
Patrick Pelissier 5589105f5c Update comments 2024-05-27 00:34:24 +02:00
Patrick Pelissier 66d2aa26d3 Fix clang warning 2024-05-15 21:19:43 +02:00
Patrick Pelissier 1ae904dff2 Add support of INITçWITH for variant
in C11 / provided all types of the variant are exclusive.
2024-05-15 20:46:20 +02:00
Patrick Pelissier 4549f4ab91 Improve comments 2024-05-14 21:29:47 +02:00
Patrick Pelissier 75d5627378 Simplify logic and should increase performance 2024-05-13 23:25:11 +02:00
Patrick Pelissier 8cbd27e629 Fix overflow 2024-05-12 22:59:58 +02:00
Patrick Pelissier 333cc2ccf8 Provide prefetch on non GCC target & compatible 2024-05-12 22:59:58 +02:00
Patrick Pelissier 000dba1da5 Correct API documentation 2024-05-11 09:43:20 +02:00
Patrick Pelissier 11946f155d Update 2024-05-11 09:21:10 +02:00
Patrick Pelissier 99713e873c Add example of multi B+Tree 2024-05-11 09:19:10 +02:00
Patrick Pelissier ea5c993ff0 Add bulk_update for DICT OA 2024-05-09 19:28:46 +02:00
Patrick Pelissier 22d62f94bb Add WIP bulk functions for DICT/OA 2024-05-09 14:10:22 +02:00
Patrick Pelissier 8fd79f07d5 Mark the functions to be optimized
those that shall not be.
2024-05-07 10:05:49 +02:00
Patrick Pelissier 29fe80a6af Adding M_HOT_ATTRIBUTE 2024-05-07 10:05:23 +02:00
Patrick Pelissier e787b64978 Fix issue in C wrapper 2024-05-07 09:51:33 +02:00
Patrick Pelissier 4f9db5eba6 Remove unused header 2024-05-07 08:20:59 +02:00
Patrick Pelissier db5990de03 DICT:
* Reduce  memory allocation usage
2024-05-05 19:26:07 +02:00
Patrick Pelissier d4383ef854 DICT:
* Reduce  memory allocation usage
* Add more assertions
2024-05-05 11:32:28 +02:00
Patrick Pelissier d10c8c1b41 Fix comment 2024-05-05 10:18:26 +02:00
Patrick Pelissier 3c669548a2 Update to dev version 0.7.3 2024-05-04 19:56:51 +02:00
Patrick Pelissier ec11230b5e Fix shrink_to_fit for dict 2024-05-03 19:16:51 +02:00
Patrick Pelissier 567473869b Update documentation 2024-05-03 16:42:51 +02:00
Patrick Pelissier 0b6733fdcb Ass _reserve method for standard dict 2024-05-03 16:40:35 +02:00
Patrick Pelissier 8e330862e1 [Coverage] Adding missing tests 2024-05-03 10:03:22 +02:00
Patrick Pelissier b2adc77d76 Update issues 2024-05-02 22:47:03 +02:00
Patrick Pelissier 82cbe9e282 Reformat 2024-05-02 17:12:48 +02:00
Patrick Pelissier 93429c8bf3 Simplify GET OA_DICT code 2024-05-02 16:25:03 +02:00
Patrick Pelissier 150bef4d4d Add more assertion and control for new dict. 2024-05-02 14:05:00 +02:00
Patrick Pelissier 562fd875e9 Improve test suite 2024-05-02 14:04:38 +02:00
Patrick Pelissier 5513a9df1b Fix issue in new dict for case of erase all / reset scenario 2024-05-02 01:15:15 +02:00
Patrick Pelissier 6fd2610eec Fix valgrind warning 2024-05-01 23:39:49 +02:00
Patrick Pelissier 11bf5e4691 Fix C++ compatibility and compound literals... 2024-05-01 23:08:29 +02:00
Patrick Pelissier cf0d82a706 Update 2024-05-01 22:57:33 +02:00
Patrick Pelissier b0cc17ea86 Update documentation with newest dictionary properties 2024-05-01 22:56:14 +02:00
Patrick Pelissier 04d4bf6866 Complete new version of normal dictionary :
* limits: max elements is 0.75*2^32
* much faster
* use less memory
2024-05-01 22:39:36 +02:00
Patrick Pelissier 738123d4a7 Fix the example 2024-05-01 22:17:59 +02:00
Patrick Pelissier a3ece09e66 Add new tests for exception and bitsets 2024-04-29 22:44:02 +02:00
Patrick Pelissier 8109c094db Update comments 2024-04-29 22:43:46 +02:00
Patrick Pelissier 2c81582419 Improve static analyzer report 2024-04-29 22:35:27 +02:00
Patrick Pelissier f8c79d9259 Add support for exception for emplace function for list/array 2024-04-27 09:04:47 +02:00
Patrick Pelissier c7a894b34e Fix format 2024-04-27 09:04:20 +02:00
Patrick Pelissier 5c36e045ff Add proper support for exception for DEQUE 2024-04-27 08:55:51 +02:00
Patrick Pelissier 0f883d2ecf Simplify code 2024-04-26 23:59:11 +02:00
Patrick Pelissier 9dfcf41b64 Improve detection of error 2024-04-23 21:14:36 +02:00
Patrick Pelissier 1416f4e225 Fix issue 2024-04-23 21:13:37 +02:00
Patrick Pelissier 16c6a13fb1 Make string functions exception aware 2024-04-22 23:41:23 +02:00
Patrick Pelissier 9382cdc0f7 Improve exception integration tests 2024-04-22 23:00:04 +02:00
Patrick Pelissier ced2719760 Make string functions exception aware 2024-04-22 22:59:54 +02:00
Patrick Pelissier 1798530284 Fix issue in string_fgets
where it could be blocked infinity in the parsing
2024-04-22 22:51:59 +02:00
Patrick Pelissier 68f890a015 Improve exception integration tests 2024-04-22 22:47:11 +02:00
Patrick Pelissier 5805038943 Make string functions exception aware 2024-04-22 22:47:00 +02:00
Patrick Pelissier 2395ceb3d2 Remove obsolete and undocumented STRING_DECL_INIT macro 2024-04-20 00:42:38 +02:00
Patrick Pelissier 2b455c5c6b Fix test issue in C++ 2024-04-20 00:42:07 +02:00
Patrick Pelissier d466e76702 Factorize common test function 2024-04-13 08:19:05 +02:00
Patrick Pelissier 9d33c85e13 Fix issue in _push_front_new
Make list services compatible with exception
2024-04-13 08:02:07 +02:00
Patrick Pelissier 4e24e7a533 Improve exception integration tests 2024-04-13 08:01:36 +02:00
Patrick Pelissier 0914c26b8d Update test suite
testing more functions in exception scenario
2024-04-11 01:00:06 +02:00
Patrick Pelissier c245285123 Make more array/list functions exception aware 2024-04-11 00:55:47 +02:00
Patrick Pelissier be4ff6f94d Make list functions exception aware 2024-04-08 23:12:23 +02:00
Patrick Pelissier aa055d0b00 Add tests for exception + lists 2024-04-08 23:10:14 +02:00
Patrick Pelissier e7d6e0d6ac Generate dependency for new files 2024-04-08 23:08:39 +02:00
Patrick Pelissier 8f97a194ae Add m_volatile 2024-04-08 23:08:16 +02:00
Patrick Pelissier b191baec8b Make array functions exception aware 2024-04-06 19:14:36 +02:00
Patrick Pelissier e4a85cdc61 Add tests for exception+array 2024-04-06 19:13:38 +02:00
Patrick Pelissier 12ee3213b0 Simplify M_ON_EXCEPTION / M_IF_EXCEPTION 2024-04-06 19:13:08 +02:00
Patrick Pelissier 6cba7ad8ba Update 2024-04-06 19:12:35 +02:00
Patrick Pelissier 955c8bfb81 Adding new example of generic 2024-04-04 22:35:08 +02:00
Patrick Pelissier 8602a259fd Adding let & print as synonymous in m-generic mode 2024-04-04 22:31:27 +02:00
Patrick Pelissier 7404e63b53 Fix C++ / G++ introduced issue 2024-03-31 22:22:41 +02:00
Patrick Pelissier c539eb57b7 Prepare exception aware integration 2024-03-31 21:55:02 +02:00
Patrick Pelissier 3c4943015b Fix introduced issue with TCC 2024-03-27 00:28:06 +01:00
Patrick Pelissier c9fef6d80a Fix issues in reworked lock:
* starving of writer threads by reader threads
* monopolization of memory bus by reader threads.
2024-03-26 23:11:57 +01:00
Patrick Pelissier 5174781a7a Improve internal backoff to perform
lazy initialization of the seed.
2024-03-26 23:11:03 +01:00
Patrick Pelissier 7bf9f6a377 Update comments 2024-03-26 22:47:13 +01:00
Patrick Pelissier 7575681e27 Rewrite Read / Write lock to be less costly 2024-03-26 22:41:41 +01:00
Patrick Pelissier cfab3cf0cf Add m_mutex_trylock function 2024-03-26 22:41:04 +01:00
Patrick Pelissier a523a97a0e Update doc 2024-03-18 22:44:53 +01:00
Patrick Pelissier 6f75bb938b Update doc 2024-03-18 22:32:16 +01:00
Patrick Pelissier a22046bc2a Update doc 2024-03-15 14:40:47 +01:00
Patrick Pelissier 84ee15b2e7 Remove workaround
This header is already quite demanding on compiler compliance , so there is no need to implement workaround for old compilers.
2024-03-15 14:40:36 +01:00
Patrick Pelissier fd6a8f1361 Update doc 2024-03-14 23:27:23 +01:00
Patrick Pelissier d22e75d7a0 Make I/O functions exception aware.
Simplify their logic
Fix other bugs.
2024-03-08 00:15:08 +01:00
Patrick Pelissier ca25dfb537 Add missing TYPE operator name in the error message if it is missing in the oplist 2024-02-27 01:12:29 +01:00
Patrick Pelissier e2159109b3 Update analysis of issues 2024-02-24 12:17:30 +01:00
Patrick Pelissier cc6bdbf06c Add missing method name in the error message
if the method is missing.
2024-02-23 22:52:05 +01:00
PpHd cd37efc69d Merge pull request #115 from derskythe/feat-styled-readme
Refine explanations and improve formatting in README.md
2024-02-08 13:32:47 +01:00
DerSkythe db08e62f1a Fix incorrect word-wrap of defines 2024-02-08 15:31:52 +04:00
DerSkythe 3be653d55a Improve README.md formatting and clarifications
Corrections were made according PR #115
2024-02-08 11:48:40 +04:00
DerSkythe e351730ad0 Refine explanations and improve formatting in README.md
Several corrections and readability enhancements were applied to the README.md file. This includes fixing typos, changing the format of code examples, refining links to sections, and adjusting the indenting of API documentation listings for better readability. General explanations and instructions about the API usage and the constructor and destructor behavior were also improved.
2024-02-07 00:58:25 +04:00
Patrick Pelissier d7a232364f Update infrastructure to the fact that the bench directory has been deleted 2024-01-19 22:27:33 +01:00
Patrick Pelissier f7580e7ce9 Move bench directory to https://github.com/P-p-H-d/c-stl-comparison
since it is better to have the comparison centralized in one place.
2024-01-19 21:53:20 +01:00
Patrick Pelissier 26cec1591a * Remove some obsolete macros
* Fix spelling
* A little bit of refactoring
2024-01-19 21:37:47 +01:00
Patrick Pelissier 52b58c5a51 Happy new year 2024 2024-01-19 21:37:47 +01:00
Patrick Pelissier 8902a752f5 Migrate to V0.7.2 2023-12-31 13:02:42 +01:00
PpHd 3445da13f3 Merge generic interface (WIP) 2023-12-31 12:53:10 +01:00
Patrick Pelissier 127530abaa Fix format 2023-12-30 16:27:25 +01:00
Patrick Pelissier ed0f538fb7 Add documentation of generic interface 2023-12-30 16:23:02 +01:00
Patrick Pelissier 76ef061dec Add basic example of generic interface 2023-12-30 16:23:02 +01:00
Patrick Pelissier c98786a3b3 Update TODO items 2023-12-30 16:23:02 +01:00
Patrick Pelissier 422f3c2c04 Add GENTYPE operator to all oplists 2023-12-30 16:23:02 +01:00
Patrick Pelissier 5ceb34d980 Cosmetics changes 2023-12-30 16:23:02 +01:00
Patrick Pelissier 73e3d1d1bd Register string_t globally 2023-12-30 16:23:02 +01:00
Patrick Pelissier 044a085fb6 Fix support for pop generic macro function 2023-12-30 16:23:02 +01:00
Patrick Pelissier 5344423ad9 Reorder header / comment 2023-12-30 16:23:02 +01:00
Patrick Pelissier 76ea989865 Fix warnings 2023-12-30 16:23:02 +01:00
Patrick Pelissier 29c4ba3dcc Add support for M_PRINT 2023-12-30 16:23:02 +01:00
Patrick Pelissier 42184f69db Disable generic tests for TCC 2023-12-30 16:23:02 +01:00
Patrick Pelissier fb8bf65233 Worlaround for warning with clang 2023-12-30 16:23:02 +01:00
Patrick Pelissier 1d86e51f4a Improve test suite 2023-12-30 16:23:02 +01:00
Patrick Pelissier 53c35a22d7 First version of Generic header 2023-12-30 16:23:02 +01:00
Patrick Pelissier 722737d60c Add M_QUAD internal macros 2023-12-30 16:23:02 +01:00
Patrick Pelissier f951a947ec Adding a try catch example 2023-12-30 13:42:47 +01:00
Patrick Pelissier d9401371a6 Fix warnings reported by CLANG,
the C warning generator.
2023-12-23 13:16:42 +01:00
Patrick Pelissier 690fd72c8b Fix documentation 2023-12-23 11:24:25 +01:00
Patrick Pelissier d318493c6c Remove M_PRINT_ARG macro to simplify maintenance
(M_FPRINT_MACRO with stdout can be used instead)
2023-12-22 23:46:07 +01:00
Patrick Pelissier fecece19cb Add retry for TCC due the random segfault it generates 2023-12-22 23:45:39 +01:00
Patrick Pelissier ea640482a7 Add new examples with custom allocation 2023-12-20 19:25:27 +01:00
Patrick Pelissier 3e558af559 Remove unused function 2023-12-20 19:24:57 +01:00
Patrick Pelissier 91a602f774 Add bench of boost unordered flat hashmap 2023-12-19 20:58:02 +01:00
Patrick Pelissier 6a5c136c72 Complete bench-verstable with bench on big structure 2023-12-13 19:35:08 +01:00
Patrick Pelissier e50fdc82ef Add benchmark for verstable 2023-12-12 23:59:17 +01:00
Patrick Pelissier ec0a082701 Update ISSUE 2023-12-12 22:57:13 +01:00
Patrick Pelissier 7bf3ea3a87 Update Function Object interface
to make interface trivially copiable

There is no need to perform a copy-protected type as there is no init/clear for this type.
It is always created through an instance.
2023-12-09 12:40:45 +01:00
Patrick Pelissier 15492394ad Update list of exported files 2023-12-08 20:58:47 +01:00
Patrick Pelissier 09f40b5695 Fix format 2023-12-08 20:58:33 +01:00
Patrick Pelissier eabfcd1d49 Update list of ignored files 2023-12-08 00:25:53 +01:00
Patrick Pelissier 00aeafc616 Update comments 2023-12-06 23:55:11 +01:00
Patrick Pelissier f512051807 Migrate to actions/checkout@v4 2023-12-06 23:52:04 +01:00
Patrick Pelissier 4e8b929da9 Migrate to vmactions/freebsd-vm v1 2023-12-06 23:51:40 +01:00
Patrick Pelissier 5841f936e6 Work around static analyzer
that raise an issue on freeing non heap pointer
2023-12-02 13:48:21 +01:00
Patrick Pelissier 2ee40339b0 Using emplace to generate init_with for shared_ptr:
Equivalent to C++ make_shared)
2023-12-02 13:21:45 +01:00
Patrick Pelissier 0c8eca893d Add example of REST client
by using libcurl for HTTP connection
and M*LIB for JSON formating.
2023-11-25 13:33:27 +01:00
Patrick Pelissier b16048a55b Add test cases for tuple emplace 2023-11-25 13:32:57 +01:00
Patrick Pelissier 9dc582c6bc Add emplace set functions for tuple 2023-11-25 13:30:08 +01:00
Patrick Pelissier 1df8bf84fb Fix oplist definition for tuple.
The oplist didn't include as many sub-oplists as the number of variables.
2023-11-25 13:29:48 +01:00
Patrick Pelissier e017a90e5c Fix format 2023-11-04 19:28:32 +01:00
Patrick Pelissier 58b317b759 Add timeout for run in VM 2023-11-04 18:50:43 +01:00
Patrick Pelissier 3a057fc319 M_STATIC_ASSERT: provide expression compatible
macro even in pure C11 mode
(Remove GCC extension)

Add M_OPL_AS_STR to display the oplist conditionnaly as the generated string may be too big for some compilers.

Translate some M_STATIC_FAILURE into M_STATIC_ASSERT
as M_STATIC_FAILURE is not an expression whereas M_STATIC_ASSERT is an expression.
2023-11-04 18:48:13 +01:00
Patrick Pelissier 703ab6ff9c Update examples 2023-11-04 18:48:13 +01:00
Patrick Pelissier f7f5db61d3 Fix formatting 2023-11-01 11:13:54 +01:00
Patrick Pelissier 32b00eb433 Add new operator GENTYPE 2023-10-21 19:05:08 +02:00
Patrick Pelissier 796f2619e5 Change M_BY_NARGS semantics:
simplify suffix by __ # NARGS
instead of __NARGS_ # NARGS

Driver: reduce macro name length.
2023-10-20 00:02:38 +02:00
Patrick Pelissier 268192138a WIP M_ON_EXCEPTION 2023-10-19 22:54:51 +02:00
Patrick Pelissier 0671a2ea41 Update doc 2023-10-15 22:21:12 +02:00
Patrick Pelissier 07dce84e51 Tuple init: handle case of exception
during construction of tuple
2023-10-14 20:15:14 +02:00
Patrick Pelissier 5f4303624f Adding macro M_CHAIN_OBJ 2023-10-14 15:55:59 +02:00
Patrick Pelissier 19e61467d5 Adding tuple generation
for analysis of generated code.
2023-10-14 15:31:37 +02:00
Patrick Pelissier 1454bcebe4 Add unique name identifier
in order to avoid Wshadow warning
in generated code.
2023-10-14 15:30:57 +02:00
Patrick Pelissier d48f2ff84d Update comment 2023-10-06 22:38:36 +02:00
Patrick Pelissier 269636b4d8 Update doc with new macro M_CHAIN_INIT 2023-09-09 18:40:59 +02:00
Patrick Pelissier bf68340106 Add macro M_CHAIN_INIT for support of partially constructed object. 2023-09-09 18:40:32 +02:00
Patrick Pelissier 5dba526e29 Update ISSUES 2023-09-08 22:12:16 +02:00
Patrick Pelissier 469239adcd Update comments 2023-08-24 23:32:47 +02:00
Patrick Pelissier 589abda0c2 Fix bug in list insertion:
The output iterator has its previous field incorrect.
2023-08-13 23:27:12 +02:00
Patrick Pelissier 8f141896ba Clarify operators for memory allocation 2023-08-02 23:20:02 +02:00
Patrick Pelissier a0cbec4329 Support of output failure for serialization of strings 2023-08-02 23:20:02 +02:00
Patrick Pelissier df66db562d Add comments 2023-08-01 00:33:43 +02:00
Patrick Pelissier 1829a70601 Update documentation 2023-07-20 23:42:15 +02:00
Patrick Pelissier e9b25f2414 Rename internal field size to capacity
to match what it is used for.
2023-07-13 21:48:33 +02:00
Patrick Pelissier c9bb739f24 Fix format 2023-07-10 00:59:18 +02:00
Patrick Pelissier e049b83a9e Rename M_SUFFIX_FUNCTION_BY_NARGS in M_BY_NARGS
and updates suffix with __NARGS_
in order to be more explicit.
2023-07-10 00:58:31 +02:00
Patrick Pelissier 31ac3999a1 Add new operators 2023-07-07 22:17:39 +02:00
Patrick Pelissier a0818419ab Fix format 2023-07-04 01:08:40 +02:00
Patrick Pelissier 496f885a84 Add threads backend for FreeRTOS tasks. 2023-07-04 01:07:59 +02:00
Patrick Pelissier d5d9a4bd5c Update documentation of threads 2023-07-04 00:46:32 +02:00
Patrick Pelissier ca9571a110 Fix no stdio:
M_CSTR macro cannot be available if there is no stdio
2023-06-29 21:26:22 +02:00
Patrick Pelissier 4497214b9c Fix spelling 2023-06-29 00:32:23 +02:00
PpHd ccb1658afd Provide way of avoiding code duplication for multiple source C files
Use of compiler extension for that, only compatible for GCC /CLANG.
This provides easy and total customization (for example non template functions like strings can also avoid code duplication).
2023-06-13 23:46:58 +02:00
Patrick Pelissier eaf2783f1a Synchro with master branch 2023-06-11 23:32:42 +02:00
Patrick Pelissier 5705819814 Update doc 2023-06-11 23:32:02 +02:00
Patrick Pelissier 91f5f513c4 Update macro names
Make global logic easier.
2023-06-11 23:32:02 +02:00
Patrick Pelissier 3dec4e2cbe Fix example 2023-06-11 23:32:02 +02:00
Patrick Pelissier e70766beaa Update list of ignored file 2023-06-11 23:32:02 +02:00
Patrick Pelissier d7639f0ae0 Update example to match new semantic 2023-06-11 23:32:02 +02:00
Patrick Pelissier c63ce84e45 Update logic to select which linkage to use
so that user code can be written easily
M_USE_EXTERN_DECL / M_USE_DECL activates functions that are not inlined.
If M_USE_EXTERN_DEF / M_USE_DEF is also defined, then the definition is emitted instead of the declaration.
2023-06-11 23:32:02 +02:00
Patrick Pelissier a043c379c0 Update user code as per new liberty 2023-06-11 23:32:02 +02:00
Patrick Pelissier 92bb380117 Rewrite order in preprocessing if to simplify user code 2023-06-11 23:32:02 +02:00
Patrick Pelissier 6bb7a184db Fix compil rules 2023-06-11 23:32:02 +02:00
Patrick Pelissier fa9163be9d Make linker options optional 2023-06-11 23:32:02 +02:00
Patrick Pelissier 0bade1563c Also build exn.exe by default 2023-06-11 23:32:02 +02:00
Patrick Pelissier 1278d2f8a0 Implement noinline another way 2023-06-11 23:32:02 +02:00
Patrick Pelissier 53af2aae68 Add example of separating inline & function code 2023-06-11 23:32:02 +02:00
Patrick Pelissier 17ded6fcc8 Update M_USE_EXTERN_FINE_GRAINED to avoid warnings 2023-06-11 23:32:02 +02:00
Patrick Pelissier 19ce371533 Add support for M_USE_EXTERN_FINE_GRAINED to enable / disable inline semantics alongside the translation unit by redefining it when needed 2023-06-11 23:32:02 +02:00
Patrick Pelissier 0bd1bcb68e Another way to request the desired semantics, compatible with clang 2023-06-11 23:32:02 +02:00
Patrick Pelissier b50813713a Define internal M_INLINE:
it is used to control the inline semantics.
By combining it with optimize option "-fno-inline"
for the internal M*LIB function (using _Pragma optimize)
we can control easily if we want to use an inline version
or call of an external function.

Define new user API:
* M_USE_EXTERN_DECL : define it before including any MLIB header to requests to call the M*LIB functions and don't inline then.
* M_USE_EXTERN_DEF  : define it once in a translation unit to define the M*LIB functions. This translation unit shall contain all templated macro too.
If you use one option, you need to use the other one too for another translation unit.
2023-06-11 23:32:02 +02:00
Patrick Pelissier 2628536d20 Add +m_string_it_set_ref 2023-06-10 01:50:14 +02:00
Patrick Pelissier ecfa4a9152 Add new function string_pop_u
to pop the last unicode code point
of a string.
2023-06-03 00:59:34 +02:00
Patrick Pelissier dafe4bcec5 Add basic test for M_DEFAULT_ARGS
Print hint to developper in case of incorrect usage
2023-06-03 00:56:13 +02:00
Patrick Pelissier 8544a7f289 Update example 2023-05-30 14:01:30 +02:00
Patrick Pelissier aa18915d61 Fix overflow computation 2023-05-21 00:15:58 +02:00
Patrick Pelissier df4bc0a56c Remove alloc warning for analyzer:
it is not able to track allocations correctly and only give false warnings.
valgraind or memory sanitizer are much more reliable to track memory errors.
2023-05-13 00:52:06 +02:00
Patrick Pelissier b6046e5e23 Fix new warnings with GCC13 2023-05-13 00:52:06 +02:00
Patrick Pelissier b487bfe5ad Adding missing inline 2023-05-08 23:54:16 +02:00
PpHd 408272c810 Merge pull request
Add m_string_cat_vprintf
2023-05-08 23:35:54 +02:00
kitterion 6a9ee17eca Add tests 2023-05-08 23:17:19 +03:00
kitterion 05c5a94634 Add m_string_cat_vprintf 2023-05-08 20:52:47 +03:00
Patrick Pelissier b1b0927e9f Update examples 2023-05-06 21:54:08 +02:00
Patrick Pelissier 37770365a1 Fix several bugs:
* in case of some invalid format starting with ']' directly it wrongly assume that the parsing was valid.
* incase of exception handling, and an exception was raised during the internal parsing, some destructors wern't called.
2023-05-05 22:16:39 +02:00
Patrick Pelissier 82bdcc685a Add internal macro MQLET for internal M_LET use 2023-05-05 22:15:13 +02:00
Patrick Pelissier 26fa733f70 Fix format 2023-05-05 22:14:41 +02:00
Patrick Pelissier cd2fc05573 Add FIXED_SIZE property 2023-05-05 21:41:05 +02:00
Patrick Pelissier 33430d4359 Update 2023-04-29 18:46:09 +02:00
Patrick Pelissier dc95b49f05 Update 2023-04-29 12:07:10 +02:00
Patrick Pelissier 3ec24cfa40 Update 2023-04-29 11:41:08 +02:00
Patrick Pelissier 12b099dcf8 Update 2023-04-29 11:25:56 +02:00
Patrick Pelissier b885d6136c Update 2023-04-29 11:20:00 +02:00
Patrick Pelissier b51c4bea2b Update 2023-04-29 11:19:54 +02:00
Patrick Pelissier 302c5eb300 Remove export passthrough of NEW/DEL/REALLOC/FREE interface
from the container oplist.
It has no real practical purpose.
NEW/DEL/REALLOC/FREE shall be a property of the container
to be defined, and not a property for an already defined container.
2023-04-29 10:50:04 +02:00
Patrick Pelissier 6d2900a2bd Improve coverage of test suite. 2023-04-27 00:04:44 +02:00
Patrick Pelissier 573e09c56e Remove variable number of elements support which was not working.
Request an explicit number of elements.
2023-04-27 00:04:03 +02:00
Patrick Pelissier 0a39a17d36 The undefined number of element value of serial _array_start
is now (size_t)-1 and no longer 0.
Moreoever, a serialization interface may refuse outputting data
if it doesn't know the number of elements.
In which case, the container shall explictly compute the number
of elements and recall the serial object _array_start method.
2023-04-27 00:01:50 +02:00
Patrick Pelissier a913d0947c Rewrite code to fix coverage 2023-04-26 23:34:13 +02:00
Patrick Pelissier abbd8ad6ce Update 2023-04-26 22:56:23 +02:00
Patrick Pelissier a09c9fe1c3 Update 2023-04-26 22:56:23 +02:00
193 changed files with 18711 additions and 23288 deletions

No files matched your search

+84 -14
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: CPU Information
run: lscpu || sysctl -a|grep cpu || echo "CPU information not available"
- name: make check C99
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: update package list
run: sudo apt-get update
- name: install multilib
@@ -50,7 +50,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: CPU Information
run: lscpu || echo "CPU information not available"
- name: Clone TCC
@@ -64,13 +64,13 @@ jobs:
run: make depend
- name: make check
shell: bash
run: make CC="/tmp/tcc/bin/tcc -std=c99" check
run: make CC="/tmp/tcc/bin/tcc -std=c99" check -j4 || make CC="/tmp/tcc/bin/tcc -std=c99" check -j2 || make CC="/tmp/tcc/bin/tcc -std=c99" check
build_linux_sanitize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: make sanitize
working-directory: ./tests
run: make sanitize -j 2
@@ -79,7 +79,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: make check
shell: bash
run: mingw32-make -j 2 CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
@@ -90,7 +90,7 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
@@ -103,7 +103,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: make check
shell: cmd
working-directory: ./tests
@@ -113,19 +113,21 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: make check
shell: cmd
working-directory: ./tests
run: Make-check-cl.bat clang-cl
# The host should always be linux
build_freebsd:
runs-on: macos-12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0
uses: vmactions/freebsd-vm@v1
timeout-minutes: 60
with:
usesh: true
run: |
@@ -138,13 +140,81 @@ jobs:
runs-on: ubuntu-latest
name: Build on aarch64
steps:
- uses: actions/checkout@v3
- uses: uraimo/run-on-arch-action@v2.5.0
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: Run commands on aarch64
id: make
timeout-minutes: 60
with:
arch: aarch64
distro: bullseye
# Not required, but speeds up builds
githubToken: ${{ github.token }}
shell: /bin/sh
run: |
apt-get update -q -y
apt-get install -q -y build-essential git gcc
lscpu
cd tests && make check -j 2 && make clean
# The host should always be linux
build_armv6:
runs-on: ubuntu-latest
name: Build on armv6
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: Run commands on armv6
id: make
timeout-minutes: 60
with:
arch: armv6
distro: alpine_latest
# Not required, but speeds up builds
githubToken: ${{ github.token }}
shell: /bin/sh
run: |
apk update
apk add build-base gcc make
cd tests && make check -j 2 && make clean
# The host should always be linux
build_riscv64:
runs-on: ubuntu-latest
name: Build on riscv64
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: Run commands on riscv64
id: make
timeout-minutes: 60
with:
arch: riscv64
distro: ubuntu22.04
# Not required, but speeds up builds
githubToken: ${{ github.token }}
shell: /bin/sh
run: |
apt-get update -q -y
apt-get install -q -y build-essential git gcc
lscpu
cd tests && make check -j 2 && make clean
# The host should always be linux
build_ppc64le:
runs-on: ubuntu-latest
name: Build on ppc64le
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: Run commands on ppc64le
id: make
timeout-minutes: 60
with:
arch: ppc64le
distro: ubuntu22.04
# Not required, but speeds up builds
githubToken: ${{ github.token }}
shell: /bin/sh
run: |
apt-get update -q -y
+1
View File
@@ -38,6 +38,7 @@ depend
other/
a*.dat
config
config.*
*.log
.vscode
.vs
+1 -1
View File
@@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2017-2023, Patrick Pelissier
Copyright (c) 2017-2025, Patrick Pelissier
All rights reserved.
Redistribution and use in source and binary forms, with or without
+6 -7
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2023, Patrick Pelissier
# Copyright (c) 2017-2025, Patrick Pelissier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -34,14 +34,14 @@ DESTDIR=
# Package name & version.
PACKAGE=m_lib-$(VERSION)
VERSION=0.7.1
VERSION=0.8.1
# 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-thread.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-tree.h m-try.h m-tuple.h m-variant.h m-worker.h
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-list.h m-thread.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h m-bstring.h m-shared-ptr.h m-queue.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/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.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-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c
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/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/test-mishared.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp tests/test-mtree.c tests/check-bptree-map.cpp tests/check-bptree-set.cpp tests/check-prioqueue.cpp tests/check-umap.cpp
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 doc/bench-array-log.png doc/bench-array.png doc/bench-list-log.png doc/bench-list.png doc/bench-oset-log.png doc/bench-oset.png doc/bench-umap-log.png doc/bench-umap.png doc/cc.sh
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.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-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c example/ex-curl.c example/ex-dict05.c example/ex-dict06.c example/exm-lib.c example/exm-main.c example/exn-lib.c example/exn-main.c example/exm-header.h example/exn-header.h example/ex-alloc1.c example/ex-alloc2.c example/ex-try01.c example/ex11-generic01.c example/ex-bptree02.c example/ex-i-list.c example/ex11-generic02.c example/ex11-small-name.c example/ex11-variant01.c example/ex11-use-pool.c
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.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-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.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-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp tests/test-mtree.c tests/check-bptree-map.cpp tests/check-bptree-set.cpp tests/check-prioqueue.cpp tests/check-umap.cpp tests/test-mtry.c tests/tgen-mmap.c tests/tgen-try.c tests/tgen-tuple.c tests/test-mgeneric.c tests/test-mbstring.c tests/except-array.c tests/except-bitset.c tests/except-deque.c tests/except-list.c tests/except-string.c tests/test-mshared-ptr.c tests/test-mshared-ptr.h tests/test-obj-except.h tests/test-mqueue.c
.PHONY: all test check doc clean distclean depend install uninstall dist
@@ -66,7 +66,6 @@ doc: html doc/depend.png
clean:
cd tests && $(MAKE) clean
cd example && $(MAKE) clean
cd bench && $(MAKE) clean
$(RM) README.html depend.dot
$(RM) -r 'm_lib-$(VERSION)'
$(RM) 'm_lib-$(VERSION).tar.bz2'
+5834 -4498
View File
File diff suppressed because it is too large. Load diff
-500
View File
@@ -1,500 +0,0 @@
# Copyright (c) 2017-2023, 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.
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-emhash bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain bench-rigtorp-mpmc-queue bench-cmc
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-cmc bench-sparsepp bench-densehashmap bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-emhash bench-hopscotchmap bench-uthash bench-qlibc bench-libsrt bench-nedtries bench-rigtorp-hashmap bench-ctl bench-stc bench-pottery bench-CC
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
queue: bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-rigtorp-mpmc-queue
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
mempool: bench-mempool
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
string: bench-string
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
plain: bench-plain
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
#########################################################################
bench-mlib:
$(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 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 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) $(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 shall point to the the directory containing the file .pc are stored
bench-qt:
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
bench-qt0:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -fPIC bench-qt.cpp common.c -o bench-qt.exe `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core`
@./bench-qt.exe 10
@./bench-qt.exe 20
@./bench-qt.exe 30
@./bench-qt.exe 40
@./bench-qt.exe 41
@./bench-qt.exe 42
@./bench-qt.exe 50
# GLIB shall point to the the directory containing the file .pc are stored
bench-glib:
@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
@./bench-glib.exe 40
@./bench-glib.exe 41
@./bench-glib.exe 50
# KLIB shall point to the directory where the headers are
bench-klib:
@if test -n "$${KLIB}" ; then $(MAKE) bench-klib0 ; else echo "Nothing to be done for KLIB." ; fi
bench-klib0:
$(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
@./bench-klib.exe 40
@./bench-klib.exe 41
@./bench-klib.exe 50
# CMC shall point to the directory where the CMC main header is
bench-cmc:
@if test -n "$${CMC}" ; then $(MAKE) bench-cmc0 ; else echo "Nothing to be done for CMC." ; fi
bench-cmc0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CMC} bench-cmc.c common.c -o bench-cmc.exe
@./bench-cmc.exe 20
@./bench-cmc.exe 30
@./bench-cmc.exe 40
# LIBDYNAMIC shall point to the the source directory where the source is built
bench-libdynamic:
@if test -n "$${LIBDYNAMIC}" ; then $(MAKE) bench-libdynamic0 ; else echo "Nothing to be done for LIBDYNAMIC." ; fi
bench-libdynamic0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-libdynamic.c common.c -o bench-libdynamic.exe -I$${LIBDYNAMIC}/src $${LIBDYNAMIC}/.libs/libdynamic.a
@./bench-libdynamic.exe 20
@./bench-libdynamic.exe 40
@./bench-libdynamic.exe 41
# 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) $(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
# DENSEHASHMAP shall point to the directory where the src are
bench-densehashmap:
@if test -n "$${DENSEHASHMAP}" ; then $(MAKE) bench-densehashmap0 ; else echo "Nothing to be done Google DENSEHASHMAP." ; fi
bench-densehashmap0:
$(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 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) $(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
@./bench-collectionc.exe 40
@./bench-collectionc.exe 41
@./bench-collectionc.exe 50
# TOMMYDS shall point to the source directory.
bench-tommyds:
@if test -n "$${TOMMYDS}" ; then $(MAKE) bench-tommyds0 ; else echo "Nothing to be done for TOMMYDS." ; fi
bench-tommyds0:
$(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
@./bench-tommyds.exe 40
@./bench-tommyds.exe 41
# FLATHASHMAP shall point to the directory where the headers are
bench-flathashmap:
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
bench-flathashmap0:
$(CXX) -std=c++14 $(CFLAGS) $(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
# EMHASh shall point to the directory where the headers are
bench-emhash:
@if test -n "$${EMHASH}" ; then $(MAKE) bench-emhash0 ; else echo "Nothing to be done for EMHASH." ; fi
bench-emhash0:
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${EMHASH}" bench-emhash.cpp common.c -o bench-emhash.exe
@./bench-emhash.exe 41
@./bench-emhash.exe 42
@./bench-emhash.exe 46
@./bench-emhash.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) $(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
# UTHASH shall point to the directory where the source are
bench-uthash:
@if test -n "$${UTHASH}" ; then $(MAKE) bench-uthash0 ; else echo "Nothing to be done for UTHASH." ; fi
bench-uthash0:
$(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 50
# QLIBC shall point to the directory where the library is installed.
bench-qlibc:
@if test -n "$${QLIBC}" ; then $(MAKE) bench-qlibc0 ; else echo "Nothing to be done for QLIBC." ; fi
bench-qlibc0:
$(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
# 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) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c common.c -o bench-libsrt.exe $${LIBSRT}/src/.libs/libsrt.a
@./bench-libsrt.exe 20
@./bench-libsrt.exe 50
# LIBLFDS shall point to the directory where the bin/build/inc/obj/src directories are
# 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) $(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
# CONCURRENTQUEUE shall point to the directory where the bin/build/inc/obj/src directories are
# 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) $(XCFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-concurrentqueue.exe -lpthread
@./bench-concurrentqueue.exe 60
# XXHASH shall point to the source directory of xxhash
# The library shall be built.
bench-xxhash:
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
bench-xxhash0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c common.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
@./bench-xxhash.exe 70
# BOOST shall point to the header location.
bench-boost:
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
bench-boost0:
$(CXX) $(CFLAGS) $(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
# RIGTORP_MPMC_QUEUE shall point to the directory where the headers are
bench-rigtorp-mpmc-queue:
@if test -n "$${RIGTORP_MPMC_QUEUE}" ; then $(MAKE) bench-rigtorp-mpmc-queue0 ; else echo "Nothing to be done for RIGTORP_MPMC_QUEUE." ; fi
bench-rigtorp-mpmc-queue0:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_MPMC_QUEUE}" bench-rigtorp-mpmcqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-mpmcqueue.exe -lpthread
@./bench-mpmcqueue.exe 60
# CTL shall point to the source directory (not where the headers are)
bench-ctl:
@if test -n "$${CTL}" ; then $(MAKE) bench-ctl0 ; else echo "Nothing to be done for CTL." ; fi
bench-ctl0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CTL} bench-ctl.c common.c -o bench-ctl.exe
@./bench-ctl.exe 10
@./bench-ctl.exe 20
@./bench-ctl.exe 30
@./bench-ctl.exe 40
@./bench-ctl.exe 50
# STC shall point to the source directory (not where the headers are)
bench-stc:
@if test -n "$${STC}" ; then $(MAKE) bench-stc0 ; else echo "Nothing to be done for STC." ; fi
bench-stc0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${STC}/include bench-stc.c common.c -o bench-stc.exe
@./bench-stc.exe 10
@./bench-stc.exe 20
@./bench-stc.exe 30
@./bench-stc.exe 40
@./bench-stc.exe 41
@./bench-stc.exe 50
# POTTERY shall point to the source directory (not where the headers are)
bench-pottery:
@if test -n "$${POTTERY}" ; then $(MAKE) bench-pottery0 ; else echo "Nothing to be done for POTTERY." ; fi
bench-pottery0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DPOTTERY_AVAILABLE=1 -I $${POTTERY}/include bench-pottery.c common.c -o bench-pottery.exe
@./bench-pottery.exe 10
@./bench-pottery.exe 20
@./bench-pottery.exe 30
@./bench-pottery.exe 40
@./bench-pottery.exe 41
@./bench-pottery.exe 50
# CCLIB shall point to the source directory of CC (where the headers are)
bench-CC:
@if test -n "$${CCLIB}" ; then $(MAKE) bench-CC0 ; else echo "Nothing to be done for CC." ; fi
bench-CC0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CCLIB}/ bench-CC.c common.c -o bench-CC.exe
@./bench-CC.exe 10
@./bench-CC.exe 20
@./bench-CC.exe 40
@./bench-CC.exe 41
############################################################################
bench-mlib-json: bench-mlib
@./bench-mlib.exe 110
@./bench-mlib.exe 111
@./bench-mlib.exe 112
bench-rapidjson:
@if test -n "$${RAPIDJSON}" ; then $(MAKE) bench-rapidjson0 ; else echo "Nothing to be done for RAPIDJSON." ; fi
bench-rapidjson0:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RAPIDJSON}/" bench-rapidjson.cpp common.c -o bench-rapidjson.exe
@./bench-rapidjson.exe 110
@./bench-rapidjson.exe 111
@./bench-rapidjson.exe 112
############################################################################
bench-plain:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
@./bench-plain-c.exe
############################################################################
bench-mempool:
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
bench-mempool0:
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
@./bench-mempool.exe
############################################################################
bench-maxdict:
$(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
bench-string:
@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-step1:
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/src/.libs/libsrt.a" ; else $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF)"; fi
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
bench-string-step2:
@if test -n "$${SDS}" ; then $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; 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:
@if test -n "$${POTTERY}" ; then $(MAKE) bench-string-step6 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_POTTERY -I$${POTTERY}/include -I$${POTTERY}/util/ $${POTTERY}/util/pottery/string/string.c" ; else $(MAKE) bench-string-step6 BENCH_DEF="$(BENCH_DEF)"; fi
bench-string-step6:
$(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 4
@./bench-string-2.exe 2000000 5
bench-string-pgo:
$(MAKE) bench-string XCFLAGS="-fprofile-generate=pgo"
$(MAKE) bench-string XCFLAGS="-fprofile-use=pgo"
############################################################################
bench-find-sort:
g++ bench-find-sort.cpp -O2 -march=native -std=c++11 -I.. -g common.c -o bench-find-sort.exe
./bench-find-sort.exe 10000000
############################################################################
clean:
$(RM) *.o bench-*.exe
$(RM) bench-*.s *~ massif.out.*
$(RM) plot-*.dat
$(RM) pgo
$(RM) tmp-serial.*
cd nucleotide && $(MAKE) clean
############################################################################
massif-do: all
@valgrind --tool=massif ./bench-mlib.exe $(N)
@valgrind --tool=massif ./bench-mlib-mempool.exe $(N)
@valgrind --tool=massif ./bench-stl.exe $(N)
@valgrind --tool=massif ./bench-qt.exe $(N)
@valgrind --tool=massif ./bench-glib.exe $(N)
@test -f ./bench-klib.exe && valgrind --tool=massif ./bench-klib.exe $(N)
@test -f ./bench-libdynamic.exe && valgrind --tool=massif ./bench-libdynamic.exe $(N)
massif-visu:
test -f massif.out.* || $(MAKE) massif-do
for i in massif.out.* ; do ms_print $$i|head -30 ; done
-140
View File
@@ -1,140 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "common.h"
#include "cc.h"
/********************************************************************************************/
static void test_array(size_t n)
{
vec( unsigned int ) a1, a2;
init(&a1);
init(&a2);
for(size_t i = 0; i < n; i++) {
push(&a1, rand_get() );
push(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *get(&a1, i ) * *get(&a2, i );
}
g_result = s;
cleanup(&a1);
cleanup(&a2);
}
/********************************************************************************************/
static void test_list(size_t n)
{
list( unsigned int ) a1, a2;
init(&a1);
init(&a2);
for(size_t i = 0; i < n; i++) {
push(&a1, rand_get() );
push(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned *it1 = first( &a1 ), *it2 = first( &a2 ); it1 != end( &a1 ); it1 = next( &a1, it1 ), it2 = next(&a2, it2) ) {
s += *it1 * *it2;
}
g_result = s;
cleanup(&a1);
cleanup(&a2);
}
/********************************************************************************************/
static void
test_dict(size_t n)
{
map( unsigned long, unsigned long ) dict;
init(&dict);
for (size_t i = 0; i < n; i++) {
unsigned long value = rand_get();
unsigned long key = rand_get();
insert(&dict, key, value);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
const unsigned long *val = get(&dict, key);
if (val != NULL)
s += *val;
}
g_result = s;
cleanup(&dict);
}
/********************************************************************************************/
typedef struct {
char a[256];
} char_array_t;
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
static inline bool char_equal_p (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a)==0; }
static inline bool char_cmp (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a); }
static inline void char_clear (char_array_t *a) { (void)a; }
static inline size_t char_hash(const char_array_t *a) {
size_t hash = 0;
const char *s = a->a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
#define CC_DTOR char_array_t, { char_clear(&val); }
#define CC_CMPR char_array_t, { return char_cmp(&val_1, &val_2); }
#define CC_HASH char_array_t, { return char_hash(&val); }
#include "cc.h"
static void
test_dict_big(size_t n)
{
map( char_array_t, char_array_t) dict;
init(&dict);
for (size_t i = 0; i < n; i++) {
char_array_t s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
insert(&dict, s1, s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1.a, "%u", rand_get());
const char_array_t *val = get(&dict, s1);
if (val != NULL)
s ++;
}
g_result = s;
cleanup(&dict);
}
/********************************************************************************************/
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},
};
int main(int argc, const char *argv[])
{
test("CC", numberof(table), table, argc, argv);
exit(0);
}
-96
View File
@@ -1,96 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <boost/lockfree/queue.hpp>
#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;
static void final(void *arg)
{
size_t *p_n = (size_t*) arg;
size_t n = *p_n;
unsigned long long j, s = 0;
for(unsigned int i = 0; i < n;i++) {
while (!g_final.pop(j));
s += j;
}
g_result = s;
}
static void conso(void *arg)
{
unsigned int j;
size_t *p_n = (size_t*)arg;
size_t n = *p_n;
unsigned long long s = 0;
for(unsigned int i = 0; i < n;i++) {
while (!g_buff.pop(j));
s += j;
}
while (!g_final.push(s));
}
static void prod(void *arg)
{
size_t *p_n = (size_t*)arg;
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!g_buff.push(r));
r = r * 31421U + 6927U;
}
}
static void test_queue(size_t n)
{
const int cpu_count = 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;
}
rand_get();
// 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);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final, &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);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
test("BOOST-LOCKFREE", numberof(table), table, argc, argv);
exit(0);
}
-204
View File
@@ -1,204 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "macro_collections.h"
#include "common.h"
/********************************************************************************************/
// CMC list are dynamic array
#define MY_LIST_PARAMS (list_uint, list_uint_s, , , unsigned int)
C_MACRO_COLLECTIONS_EXTENDED(CMC, LIST, MY_LIST_PARAMS, (STR))
struct list_uint_s_fval list_fval = { .cmp = NULL,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = NULL,
.pri = NULL };
static void test_array(size_t n)
{
struct list_uint_s *a1 = list_uint_new(1, &list_fval);
struct list_uint_s *a2 = list_uint_new(1, &list_fval);
for(size_t i = 0; i < n; i++) {
list_uint_push_back(a1, rand_get() );
list_uint_push_back(a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += list_uint_get(a1, i ) * list_uint_get(a2, i );
}
g_result = s;
list_uint_free(a1);
list_uint_free(a2);
}
/********************************************************************************************/
#define my_cmp(a,b) ( (a) < (b) ? -1 : (a) > (b) )
#define MY_TMAP_PARAMS (tree_ulong, tree_ulong_s, , unsigned long, bool)
C_MACRO_COLLECTIONS_EXTENDED(CMC, TREEMAP, MY_TMAP_PARAMS, (STR))
int cmp_ulong(unsigned long a, unsigned long b) { return my_cmp(a,b); }
struct tree_ulong_s_fkey tree_fkey = { .cmp = cmp_ulong,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = NULL,
.pri = NULL };
struct tree_ulong_s_fval tree_fval = { .cmp = NULL,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = NULL,
.pri = NULL };
static void test_rbtree(size_t n)
{
struct tree_ulong_s *tree;
tree = tree_ulong_new(&tree_fkey, &tree_fval);
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
tree_ulong_insert(tree, j, true);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
if (tree_ulong_get_ref(tree, j) != 0)
s+= j;
}
g_result = s;
tree_ulong_free(tree);
}
/********************************************************************************************/
#define hash_func(a) kh_int64_hash_func(a)
size_t hash_ulong(unsigned long a)
{
return ( (a) >> 33 ) ^ (a) ^ ((a) << 11);
}
#define MY_DICT_PARAMS (hmap, hmap_s, , unsigned long, unsigned long)
C_MACRO_COLLECTIONS_EXTENDED(CMC, HASHMAP, MY_DICT_PARAMS, (STR))
struct hmap_s_fkey hmap_fkey = { .cmp = cmp_ulong,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = hash_ulong,
.pri = NULL };
struct hmap_s_fval hmap_fval = { .cmp = NULL,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = NULL,
.pri = NULL };
static void
test_dict(size_t n)
{
struct hmap_s *dict = hmap_new(16, 0.7, &hmap_fkey, &hmap_fval);
for (size_t i = 0; i < n; i++) {
hmap_insert(dict, rand_get(), rand_get());
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long *val = hmap_get_ref(dict, rand_get());
if (val != 0)
s += *val;
}
g_result = s;
hmap_free(dict);
}
/********************************************************************************************/
#if 0 // Not working issue in using not integer types
typedef struct {
char a[256];
} char_array_t;
static inline int char_cmp (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a); }
static inline size_t char_hash(const char_array_t a) {
size_t hash = 0;
const char *s = a.a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
#define MY_DICTB_PARAMS (bhmap, bhmap_s, , char_array_t, char_array_t)
C_MACRO_COLLECTIONS_EXTENDED(CMC, HASHMAP, MY_DICT_PARAMS, (STR))
struct bhmap_s_fkey bhmap_fkey = { .cmp = char_cmp,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = char_hash,
.pri = NULL };
struct bhmap_s_fval bhmap_fval = { .cmp = NULL,
.cpy = NULL,
.str = NULL,
.free = NULL,
.hash = NULL,
.pri = NULL };
static void
test_dict_big(size_t n)
{
struct bhmap_s *dict = bhmap_new(16, 0.7, &bhmap_fkey, &bhmap_fval);
for (size_t i = 0; i < n; i++) {
char_array_t s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
bhmap_insert(dict, s1, s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1.a, "%u", rand_get() );
char_array_t *val = bhmap_get_ref(dict, s1);
if (val != 0)
s ++;
}
g_result = s;
bhmap_free(dict);
}
#endif
/********************************************************************************************/
const config_func_t table[] = {
{ 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[])
{
test("CMC", numberof(table), table, argc, argv);
exit(0);
}
-266
View File
@@ -1,266 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "array.h"
#include "slist.h"
#include "treetable.h"
#include "hashtable.h"
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
Array *a1, *a2;
enum cc_stat stat;
stat = array_new(&a1);
if (stat != CC_OK) abort();
stat = array_new(&a2);
if (stat != CC_OK) abort();
for(size_t i = 0; i < n; i++) {
stat = array_add(a1, (void*) (uintptr_t)rand_get() );
if (stat != CC_OK) abort();
stat = array_add(a2, (void*) (uintptr_t)rand_get() );
if (stat != CC_OK) abort();
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
void *e, *f;
stat = array_get_at(a1, i, &e);
if (stat != CC_OK) abort();
stat = array_get_at(a2, i, &f);
if (stat != CC_OK) abort();
s += ((unsigned int)(uintptr_t) e) * ((unsigned int)(uintptr_t) f);
}
g_result = s;
array_destroy(a1);
array_destroy(a2);
}
/********************************************************************************************/
static void test_list(size_t n)
{
SList *a1, *a2;
enum cc_stat stat;
stat = slist_new(&a1);
if (stat != CC_OK) abort();
stat = slist_new(&a2);
if (stat != CC_OK) abort();
for(size_t i = 0; i < n; i++) {
stat = slist_add(a1, (void*) (uintptr_t)rand_get() );
if (stat != CC_OK) abort();
stat = slist_add(a2, (void*) (uintptr_t)rand_get() );
if (stat != CC_OK) abort();
}
unsigned int s = 0;
SListIter iter1, iter2;
void *e, *f;
slist_iter_init(&iter1, a1);
slist_iter_init(&iter2, a2);
while (slist_iter_next(&iter1, &e) == CC_OK && slist_iter_next(&iter2, &f) == CC_OK) {
s += ((unsigned int)(uintptr_t) e) * ((unsigned int)(uintptr_t) f);
}
g_result = s;
slist_destroy(a1);
slist_destroy(a2);
}
/********************************************************************************************/
static int compare(const void *a, const void *b)
{
const uintptr_t pa = (uintptr_t) a;
const uintptr_t pb = (uintptr_t) b;
return (pa < pb) ? -1 : (pa > pb);
}
static void test_rbtree(size_t n)
{
TreeTable *tree;
enum cc_stat stat;
stat = treetable_new(compare, &tree);
if (stat != CC_OK) abort();
for (size_t i = 0; i < n; i++) {
void *key = (void*)(uintptr_t) rand_get();
stat = treetable_add(tree, key, key);
if (stat != CC_OK) abort();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
void *e;
void *key = (void*) (uintptr_t) rand_get();
stat = treetable_get(tree, key, &e);
if (stat == CC_OK)
s += (uintptr_t)e;
}
g_result = s;
treetable_destroy(tree);
}
/********************************************************************************************/
static size_t hash_func (const void *key, int l, uint32_t seed)
{
const unsigned long pa = (uintptr_t) key;
(void)l;
return pa ^ seed;
}
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) ? -1 : pa > pb;
}
static void
test_dict(size_t n)
{
HashTable *dict;
enum cc_stat stat;
HashTableConf htc;
hashtable_conf_init(&htc);
htc.hash = hash_func;
htc.key_compare = equal_func;
stat = hashtable_new_conf(&htc, &dict);
if (stat != CC_OK) abort();
for (size_t i = 0; i < n; i++) {
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();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
void *key = (void*)(uintptr_t) rand_get();
void *r;
stat = hashtable_get(dict, key, &r);
if (stat == CC_OK)
s += (uintptr_t) r;
}
g_result = s;
hashtable_destroy(dict);
}
/********************************************************************************************/
typedef char char_array_t[256];
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);
}
static size_t char_hash(const void *a, int l, uint32_t seed)
{
const char_array_t *pa = (const char_array_t *)a;
const char *s = *pa;
(void)l ;
size_t hash = seed;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
static void
test_dict_big(size_t n)
{
HashTable *dict;
enum cc_stat stat;
HashTableConf htc;
hashtable_conf_init(&htc);
htc.hash = char_hash;
htc.key_compare = char_equal;
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 );
if (stat != CC_OK) abort();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
void *r;
sprintf(s1, "%u", rand_get());
stat = hashtable_get(dict, s1, &r);
if (stat == CC_OK)
s ++;
}
g_result = s;
hashtable_destroy(dict);
if (cpt != 2*n) abort();
for(size_t i = 0; i < 2*n;i++)
free(tab[i]);
}
/********************************************************************************************/
static int cmp_float(const void * a, const void *b)
{
const float *const*pa = a, *const*pb = b;
return (**pa < **pb) ? -1 : (**pa > **pb);
}
static void test_sort(size_t n)
{
Array *a1;
enum cc_stat stat;
stat = array_new(&a1);
if (stat != CC_OK) abort();
for(size_t i = 0; i < n; i++) {
float *v = malloc(sizeof(float));
*v = rand_get();
stat = array_add(a1, v );
if (stat != CC_OK) abort();
}
array_sort(a1, cmp_float);
void *e;
stat = array_get_at(a1, 0, &e);
if (stat != CC_OK) abort();
g_result = *(float*)e;
array_destroy(a1);
}
/********************************************************************************************/
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[])
{
test("CollectionC", numberof(table), table, argc, argv);
exit(0);
}
-96
View File
@@ -1,96 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "concurrentqueue.h"
#include "common.h"
/********************************************************************************************/
moodycamel::ConcurrentQueue<unsigned int> g_buff;
moodycamel::ConcurrentQueue<unsigned long long> g_final;
static void final(void *arg)
{
size_t *p_n = (size_t*) arg;
size_t n = *p_n;
unsigned long long j, s = 0;
for(unsigned int i = 0; i < n;i++) {
while (!g_final.try_dequeue(j));
s += j;
}
g_result = s;
}
static void conso(void *arg)
{
unsigned int j;
size_t *p_n = (size_t*)arg;
size_t n = *p_n;
unsigned long long s = 0;
for(unsigned int i = 0; i < n;i++) {
while (!g_buff.try_dequeue(j));
s += j;
}
while (!g_final.try_enqueue(s));
}
static void prod(void *arg)
{
size_t *p_n = (size_t*)arg;
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
while (!g_buff.try_enqueue(r));
r = r * 31421U + 6927U;
}
}
static void test_queue(size_t n)
{
const int cpu_count = 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;
}
rand_get();
// 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);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final, &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);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
test("CONCURRENT-QUEUE", numberof(table), table, argc, argv);
exit(0);
}
-183
View File
@@ -1,183 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "m-core.h"
#include "common.h"
typedef unsigned int uint;
typedef unsigned long ulong;
/********************************************************************************************/
#define POD
#define T uint
#include "ctl/vector.h"
static void test_array(size_t n)
{
vec_uint a1 = vec_uint_init();
vec_uint a2 = vec_uint_init();
for(size_t i = 0; i < n; i++) {
vec_uint_push_back(&a1, rand_get() );
vec_uint_push_back(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *vec_uint_at(&a1, i ) * *vec_uint_at(&a2, i );
}
g_result = s;
vec_uint_free(&a1);
vec_uint_free(&a2);
}
/********************************************************************************************/
#define POD
#define T uint
#include "ctl/forward_list.h"
static void test_list (size_t n)
{
slist_uint a1 = slist_uint_init();
slist_uint a2 = slist_uint_init();
for(size_t i = 0; i < n; i++) {
slist_uint_push_front(&a1, rand_get());
slist_uint_push_front(&a2, rand_get());
}
unsigned int s = 0;
slist_uint_it it1 = slist_uint_begin(&a1);
slist_uint_it it2 = slist_uint_begin(&a2);
for( ; !slist_uint_it_done(&it1) ;
slist_uint_it_next(&it1), slist_uint_it_next(&it2)) {
s += *slist_uint_it_ref(&it1) * *slist_uint_it_ref(&it2);
}
g_result = s;
slist_uint_free(&a1);
slist_uint_free(&a2);
}
/********************************************************************************************/
#define POD
#define T ulong
#include "ctl/set.h"
static inline int
int_cmp(ulong *a, ulong *b) {
return *a < *b;
}
static void test_rbtree(size_t n)
{
set_ulong tree = set_ulong_init (int_cmp);
for (size_t i = 0; i < n; i++) {
set_ulong_insert (&tree, rand_get() );
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
if (set_ulong_find_node(&tree, j) != NULL)
s+= j;
}
g_result = s;
set_ulong_free(&tree);
}
/********************************************************************************************/
typedef struct {
unsigned long key;
unsigned long value;
} pair_ulong;
// Use of M*LIB hash function
static inline size_t
pair_ulong_hash(pair_ulong *a)
{
return M_HASH_DEFAULT(a->key);
}
static inline int
pair_ulong_equal(pair_ulong *a, pair_ulong *b)
{
return a->key == b->key;
}
static inline void
pair_ulong_free(pair_ulong *a) {
(void) a;
}
static inline pair_ulong
pair_ulong_copy(pair_ulong *self) {
return (pair_ulong) { .key = self->key, .value = self->value };
}
#define T pair_ulong
#include <ctl/unordered_map.h>
static void
test_dict(size_t n)
{
umap_pair_ulong dict = umap_pair_ulong_init(pair_ulong_hash, pair_ulong_equal);
for (size_t i = 0; i < n; i++) {
unsigned long value = rand_get();
unsigned long key = rand_get();
umap_pair_ulong_insert(&dict, (pair_ulong) { key, value });
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
umap_pair_ulong_it it = umap_pair_ulong_find(&dict, (pair_ulong) { rand_get() , 0 } );
if (!umap_pair_ulong_it_done(&it) )
s += it.ref->value;
}
g_result = s;
umap_pair_ulong_free(&dict);
}
/********************************************************************************************/
#define POD
#define T float
#include "ctl/vector.h"
static void test_sort(size_t n)
{
vec_float a1 = vec_float_init();
for(size_t i = 0; i < n; i++) {
vec_float_push_back(&a1, rand_get());
}
vec_float_sort(&a1);
g_result = *vec_float_at(&a1, 0);
vec_float_free(&a1);
}
/********************************************************************************************/
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},
{ 50, "Sort", 10000000, 0, test_sort, 0},
};
int main(int argc, const char *argv[])
{
test("CTL", numberof(table), table, argc, argv);
exit(0);
}
-144
View File
@@ -1,144 +0,0 @@
#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 <google/dense_hash_map>
using google::dense_hash_map;
using namespace std;
#include "common.h"
/********************************************************************************************/
static void
test_dict2(size_t n)
{
dense_hash_map<unsigned long, unsigned long> dict;
dict.set_empty_key(-1);
dict.set_deleted_key(-2);
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++) {
dense_hash_map<unsigned long, unsigned long>::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;
};
};
namespace tr1 {
template <> struct hash<char_array_s> {
std::size_t operator()(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)
{
dense_hash_map<char_array_s, char_array_s> dict;
struct char_array_s empty, del;
memset(&empty, 0, sizeof empty); empty.a[1] = 255;
memset(&del, 0, sizeof del); del.a[1] = 255;
dict.set_empty_key(empty);
dict.set_deleted_key(del);
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());
dense_hash_map<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)
{
dense_hash_map<string, string> dict;
string empty, del;
empty = "";
del = "DEL";
dict.set_empty_key(empty);
dict.set_deleted_key(del);
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();
dense_hash_map<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}
};
int main(int argc, const char *argv[])
{
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
exit(0);
}
-138
View File
@@ -1,138 +0,0 @@
#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 "hash_table7.hpp"
using namespace emhash7;
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("EMHASH", numberof(table), table, argc, argv);
exit(0);
}
-138
View File
@@ -1,138 +0,0 @@
#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);
}
-101
View File
@@ -1,101 +0,0 @@
// program to find longest contiguous subsequence
// based on https://www.geeksforgeeks.org/longest-consecutive-subsequence/
#include <iostream>
#include <unordered_set>
using namespace std;
// Returns length of the longest contiguous subsequence
int cpp_find_longest(int arr[], int n)
{
unordered_set<int> S;
int ans = 0;
// Hash all the array elements
for (int i = 0; i < n; i++)
S.insert(arr[i]);
// check each possible sequence from
// the start then update optimal length
for (int i = 0; i < n; i++) {
// if current element is the starting
// element of a sequence
if (S.find(arr[i] - 1) == S.end()) {
// Then check for next elements
// in the sequence
int j = arr[i] + 1;
while (S.find(j) != S.end())
j++;
// update optimal length if
// this length is more
ans = max(ans, j - arr[i]);
}
}
return ans;
}
#include "m-dict.h"
static inline bool oor_equal_p(int a, unsigned char c) { return a == ((c == 0) ? INT_MIN : INT_MAX); }
static inline void oor_set(int *a, unsigned char c) { *a = ((c == 0) ? INT_MIN : INT_MAX); }
DICT_OASET_DEF(dict_int, int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)))
// Returns length of the longest contiguous subsequence
int mlib_find_longest(int arr[], int n)
{
dict_int_t S;
int ans = 0;
dict_int_init(S);
// Hash all the array elements
for (int i = 0; i < n; i++)
dict_int_push(S, arr[i]);
// check each possible sequence from
// the start then update optimal length
for (int i = 0; i < n; i++) {
// if current element is the starting
// element of a sequence
int *p = dict_int_get(S, arr[i]-1);
if (p == NULL) {
// Then check for next elements
// in the sequence
int j = arr[i] + 1;
while (dict_int_get(S, j) != NULL)
j++;
// update optimal length if
// this length is more
ans = M_MAX(ans, j - arr[i]);
}
}
dict_int_clear(S);
return ans;
}
#include "common.h"
int main(int argc, const char *argv[])
{
int n = argc == 1 ? 10000 : atoi(argv[1]);
int *arr = (int*) malloc(n * sizeof(int));
for(int i = 0; i < n; i++)
arr[i] = rand_get();
unsigned long long t0 = cputime();
cout << "Length of the Longest contiguous subsequence is "
<< cpp_find_longest(arr, n) << endl;
unsigned long long t1 = cputime();
cout << "Length of the Longest contiguous subsequence is "
<< mlib_find_longest(arr, n) << endl;
unsigned long long t2 = cputime();
cout << "Time C++: " << (t1-t0) << " Time M*LIB: " << (t2-t1) << endl;
return 0;
}
-139
View File
@@ -1,139 +0,0 @@
// Need recent gcc
#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 "flat_hash_map.hpp"
using namespace ska;
using namespace std;
#include "common.h"
/********************************************************************************************/
static void
test_dict2(size_t n)
{
flat_hash_map<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++) {
flat_hash_map<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)
{
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];
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)
{
flat_hash_map<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());
flat_hash_map<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)
{
flat_hash_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();
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();
flat_hash_map<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("FLAT-HASPMAP", numberof(table), table, argc, argv);
exit(0);
}
-202
View File
@@ -1,202 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <glib.h>
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
GArray *a1, *a2;
a1 = g_array_new (FALSE, FALSE, sizeof (unsigned int));
a2 = g_array_new (FALSE, FALSE, sizeof (unsigned int));
for(size_t i = 0; i < n; i++) {
unsigned int v = rand_get();
g_array_append_val(a1, v );
v = rand_get();
g_array_append_val(a2, v );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += g_array_index(a1, unsigned int, i ) * g_array_index(a2, unsigned int, i );
}
g_result = s;
g_array_free(a1, FALSE);
g_array_free(a2, FALSE);
}
/********************************************************************************************/
static void test_list (size_t n)
{
GSList *a1 = NULL, *a2 = NULL;
for(size_t i = 0; i < n; i++) {
a1 = g_slist_prepend (a1, GINT_TO_POINTER (rand_get() ));
a2 = g_slist_prepend (a2, GINT_TO_POINTER (rand_get() ));
}
unsigned int s = 0;
for(GSList *it1 = a1, *it2 = a2 ; it1 != NULL; it1 = g_slist_next(it1), it2 = g_slist_next(it2) ) {
s += GPOINTER_TO_INT(it1->data) * GPOINTER_TO_INT(it2->data);
n--;
}
g_result = s;
g_slist_free(a1);
g_slist_free(a2);
}
/********************************************************************************************/
static int compare(gconstpointer a, gconstpointer b, gpointer user_data)
{
const unsigned long *pa = a;
const unsigned long *pb = b;
(void) user_data;
return (*pa < *pb) ? -1 : (*pa > *pb);
}
static void test_rbtree(size_t n)
{
GTree *tree = g_tree_new_full(compare, NULL, free, NULL);
for (size_t i = 0; i < n; i++) {
// NOTE: unsigned long are not designed to work with GPOINTER_TO_INT according to documentation.
unsigned long *p = malloc(sizeof(unsigned long));
*p = rand_get();
g_tree_insert(tree, p, p);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
unsigned long *p = g_tree_lookup(tree, &key);
if (p)
s += *p;
}
g_result = s;
g_tree_destroy(tree);
}
/********************************************************************************************/
static guint hash_func (gconstpointer a)
{
const unsigned long *pa = a;
return *pa;
}
static gboolean equal_func(gconstpointer a, gconstpointer b)
{
const unsigned long *pa = a;
const unsigned long *pb = b;
return *pa == *pb;
}
static void
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++) {
unsigned long *key = malloc(sizeof(unsigned long));
unsigned long *value = malloc(sizeof(unsigned long));
*key = rand_get();
*value = rand_get();
g_hash_table_insert(dict, key, value );
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
unsigned long *p = g_hash_table_lookup(dict, &key);
if (p)
s += *p;
}
g_result = s;
g_hash_table_destroy(dict);
}
/********************************************************************************************/
typedef char char_array_t[256];
static gboolean char_equal (gconstpointer a, gconstpointer 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;
}
static guint char_hash(gconstpointer a)
{
const char_array_t *pa = (const char_array_t *)a;
const char *s = *pa;
guint hash = 0;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
static void
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++) {
char_array_t *key = malloc (sizeof (char_array_t));
char_array_t *value = malloc (sizeof (char_array_t));
sprintf(*key, "%u", rand_get());
sprintf(*value, "%u", rand_get());
g_hash_table_insert(dict, key, value );
}
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 = g_hash_table_lookup(dict, &s1);
if (p)
s ++;
}
g_result = s;
g_hash_table_destroy(dict);
}
/********************************************************************************************/
static gint cmp_float(gconstpointer a,
gconstpointer b)
{
const float *pa = a, *pb = b;
return (*pa < *pb) ? -1 : (*pa > *pb);
}
static void test_sort(size_t n)
{
GArray *a1 = g_array_new (FALSE, FALSE, sizeof (float));
for(size_t i = 0; i < n; i++) {
float v = rand_get();
g_array_append_val(a1, v );
}
g_array_sort(a1, cmp_float);
g_result = g_array_index (a1, float, 0);
g_array_free(a1, FALSE);
}
/********************************************************************************************/
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[])
{
test("GLIB", numberof(table), table, argc, argv);
exit(0);
}
-123
View File
@@ -1,123 +0,0 @@
#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 "hopscotch_map.h"
using namespace tsl;
using namespace std;
#include "common.h"
/********************************************************************************************/
static void
test_dict2(size_t n)
{
tsl::hopscotch_map<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++) {
hopscotch_map<unsigned long, unsigned long>::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;
};
};
}
static void
test_dict_big(size_t n)
{
hopscotch_map<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());
hopscotch_map<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)
{
hopscotch_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();
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();
hopscotch_map<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}
};
int main(int argc, const char *argv[])
{
test("HOPSCOTCHMAP", numberof(table), table, argc, argv);
exit(0);
}
-202
View File
@@ -1,202 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "kvec.h"
#include "klist.h"
#include "kbtree.h"
#include "khash.h"
#include "ksort.h"
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
kvec_t(unsigned int) a1, a2;
kv_init(a1);
kv_init(a2);
for(size_t i = 0; i < n; i++) {
kv_push(unsigned int, a1, rand_get() );
kv_push(unsigned int, a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += kv_A(a1, i ) * kv_A(a2, i );
}
g_result = s;
kv_destroy(a1);
kv_destroy(a2);
}
/********************************************************************************************/
#define uint_free(x)
KLIST_INIT(list_uint, unsigned int, uint_free)
static void test_list (size_t n)
{
klist_t(list_uint) *a1, *a2;
a1 = kl_init(list_uint);
a2 = kl_init(list_uint);
for(size_t i = 0; i < n; i++) {
*kl_pushp (list_uint, a1) = rand_get();
*kl_pushp (list_uint, a2) = rand_get();
}
unsigned int s = 0;
kliter_t(list_uint) *it1, *it2;
for(it1 = kl_begin(a1), it2 = kl_begin(a2);
it1 != kl_end(a1);
it1 = kl_next(it1), it2 = kl_next(it2)) {
s += kl_val(it1) * kl_val(it2);
}
g_result = s;
kl_destroy(list_uint, a1);
kl_destroy(list_uint, a2);
}
/********************************************************************************************/
#define my_cmp(a,b) ( (a) < (b) ? -1 : (a) > (b) )
KBTREE_INIT(kTree, unsigned long, my_cmp)
static void test_rbtree(size_t n)
{
kbtree_t(kTree) *tree;
tree = kb_init(kTree, KB_DEFAULT_SIZE/2);
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
kb_putp(kTree, tree, &j);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
if (kb_getp(kTree, tree, &j) != 0)
s+= j;
}
g_result = s;
kb_destroy(kTree, tree);
}
/********************************************************************************************/
#define hash_eq(a, b) ((a) == (b))
#define hash_func(a) kh_int64_hash_func(a)
KHASH_INIT(iun, unsigned long, unsigned long, 1, hash_func, hash_eq)
static void
test_dict(size_t n)
{
khash_t(iun) *dict = kh_init(iun);
for (size_t i = 0; i < n; i++) {
int ret;
khiter_t k = kh_put(iun, dict, rand_get(), &ret);
kh_value(dict, k) = rand_get();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
khiter_t k = kh_get(iun, dict, rand_get());
if (kh_exist(dict, k))
s += kh_value(dict, k);
}
g_result = s;
kh_destroy(iun, dict);
}
/********************************************************************************************/
typedef struct {
char a[256];
} char_array_t;
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
static inline bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a)==0; }
static inline size_t char_hash(const char_array_t a) {
size_t hash = 0;
const char *s = a.a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
KHASH_INIT(iub, char_array_t, char_array_t, 1, char_hash, char_equal_p)
static void
test_dict_big(size_t n)
{
khash_t(iub) *dict = kh_init(iub);
for (size_t i = 0; i < n; i++) {
int ret;
char_array_t s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
khiter_t k = kh_put(iub, dict, s1, &ret);
char_set(&kh_value(dict, k), s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1.a, "%u", rand_get());
khiter_t k = kh_get(iub, dict, s1);
if (kh_exist(dict, k))
s ++;
}
g_result = s;
kh_destroy(iub, dict);
}
/********************************************************************************************/
// NOTE: Needed for compiling
double drand48(void) { return rand_get(); }
KSORT_INIT_GENERIC(float)
static void test_sort(size_t n)
{
kvec_t(float) a1;
kv_init(a1);
for(size_t i = 0; i < n; i++) {
kv_push(float, a1, rand_get() );
}
// NOTE: Is-it the right way to do? Seems very low level!
ks_introsort_float(n, & kv_A(a1, 0));
g_result = kv_A(a1, 0);
kv_destroy(a1);
}
/********************************************************************************************/
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[])
{
test("KLIB", numberof(table), table, argc, argv);
exit(0);
}
-170
View File
@@ -1,170 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#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"
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
vector a1, a2;
vector_construct(&a1, sizeof(unsigned int));
vector_construct(&a2, sizeof(unsigned int));
for(size_t i = 0; i < n; i++) {
unsigned int v = rand_get();
vector_push_back(&a1, &v);
v = rand_get();
vector_push_back(&a2, &v);
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *(unsigned int*) vector_at(&a1, i) * *(unsigned int*) vector_at(&a2, i);
}
g_result = s;
vector_destruct(&a1, NULL);
vector_destruct(&a2, NULL);
}
/********************************************************************************************/
static size_t hash_func (void *a)
{
return hash_data(a, sizeof (unsigned long));
}
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(void *a, void *b)
{
unsigned long *pa = a;
if (b == NULL) {
*pa = -1U;
} else {
const unsigned long *pb = b;
*pa = *pb;
}
}
typedef struct map_element map_element;
struct map_element
{
unsigned long key;
unsigned long value;
};
static void
test_dict(size_t n)
{
map dict;
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, set_func, equal_func, NULL);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
map_element *p = map_at(&dict, (map_element[]){{.key = rand_get()}}, hash_func, equal_func);
if (p)
s += p->value;
}
g_result = s;
map_destruct(&dict, NULL, NULL);
}
/********************************************************************************************/
typedef char char_array_t[256];
static int char_equal (void *a, void *b)
{
const char_array_t *pa = (const char_array_t *)a;
if (b == NULL)
return strcmp(*pa, "") == 0;
const char_array_t *pb = (const char_array_t *)b;
return strcmp(*pa,*pb)==0;
}
static size_t char_hash(void *a)
{
char_array_t *pa = (char_array_t *)a;
return hash_string(*pa);
}
static void char_set(void *a, void *b)
{
char_array_t *pa = (char_array_t *)a;
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;
struct big_map_element
{
char_array_t key;
char_array_t value;
};
static void
test_dict_big(size_t n)
{
map dict;
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_set, char_equal, NULL);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
big_map_element el;
sprintf(el.key, "%u", rand_get());
big_map_element *p = map_at(&dict, &el, char_hash, char_equal);
if (p)
s ++;
}
g_result = s;
map_destruct(&dict, NULL, NULL);
}
/********************************************************************************************/
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[])
{
test("LIBDYNAMIC", numberof(table), table, argc, argv);
exit(0);
}
-116
View File
@@ -1,116 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "liblfds711.h"
#include "common.h"
/********************************************************************************************/
struct lfds711_queue_bmm_state g_buff;
struct lfds711_queue_bmm_state g_final;
static void final(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
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++) {
void *p;
while (0 == lfds711_queue_bmm_dequeue(&g_final, &p, NULL)) { m_thread_yield(); }
j = (uintptr_t)p;
s += j;
}
g_result = s;
}
static void conso(void *arg)
{
unsigned int j;
size_t *p_n = arg;
size_t n = *p_n;
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++) {
void *p;
while (0 == lfds711_queue_bmm_dequeue(&g_buff, &p, NULL)) { m_thread_yield(); }
j = (uintptr_t) p;
s += j;
}
while (0 == lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL)) { m_thread_yield(); }
}
static void prod(void *arg)
{
size_t *p_n = arg;
size_t n = *p_n;
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++) {
while (0 == lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL)) { m_thread_yield(); }
r = r * 31421U + 6927U;
}
}
static void test_queue(size_t n)
{
const int cpu_count = 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;
}
rand_get();
// Init
const size_t size = m_core_roundpow2(64*cpu_count);
struct lfds711_queue_bmm_element g_buff_elements[size];
struct lfds711_queue_bmm_element g_final_elements[size];
lfds711_queue_bmm_init_valid_on_current_logical_core(&g_buff, g_buff_elements,
size, NULL);
lfds711_queue_bmm_init_valid_on_current_logical_core(&g_final, g_final_elements,
size, NULL);
// 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);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final, &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
lfds711_queue_bmm_cleanup(&g_final, NULL);
lfds711_queue_bmm_cleanup(&g_buff, NULL);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
test("LIBLFDS", numberof(table), table, argc, argv);
exit(0);
}
-74
View File
@@ -1,74 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "svector.h"
#include "smap.h"
#include "smset.h"
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
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();
sv_push(&a1, &value );
value = rand_get();
sv_push(&a2, &value );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *(unsigned int*)sv_at(a1, i ) * *(unsigned int *)sv_at(a2, i );
}
g_result = s;
sv_free(&a1);
sv_free(&a2);
}
/********************************************************************************************/
/* There is no 'unsigned long' key in smset */
/********************************************************************************************/
static int cmp_float (const void *a, const void *b)
{
const float *pa=a;
const float *pb = b;
return (*pa < *pb) ? -1 : *pa > *pb;
}
static void test_sort(size_t n)
{
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 );
}
sv_sort(a1);
g_result = *(float*)sv_at(a1, 0);
sv_free(&a1);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 20, "Array", 100000000, 0, test_array, 0},
{ 50, "sort", 10000000, 0, test_sort, 0}
};
int main(int argc, const char *argv[])
{
test("LIBSRT", numberof(table), table, argc, argv);
exit(0);
}
-1086
View File
File diff suppressed because it is too large. Load diff
-99
View File
@@ -1,99 +0,0 @@
#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);
}
-90
View File
@@ -1,90 +0,0 @@
// Inspired by: http://stackoverflow.com/questions/3664272/is-stdvector-so-much-slower-than-plain-arrays/3664349#3664349
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
#include "m-array.h"
#include "m-list.h"
#include "m-rbtree.h"
#include "m-dict.h"
#include "m-algo.h"
#include "m-mempool.h"
#include "m-string.h"
unsigned long g_result;
static unsigned long long
cputime (void)
{
struct rusage rus;
getrusage (0, &rus);
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
}
struct Pixel
{
unsigned char r, g, b;
};
ARRAY_DEF(array_pixel, struct Pixel, (INIT(M_NOTHING_DEFAULT), INIT_SET(M_MEMCPY_DEFAULT) ))
void UseMlib(void)
{
unsigned long long t1 = cputime();
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
array_pixel_t pixels;
array_pixel_init(pixels);
array_pixel_reserve(pixels, dimension * dimension);
for(int i = 0; i < dimension * dimension; ++i)
{
const struct Pixel p = { 255, 0, 0};
array_pixel_push_back (pixels, p);
}
array_pixel_clear(pixels);
}
unsigned long long t2 = cputime();
printf ("M*Lib Array: %Lu us\n", (t2-t1)/1000);
}
void UseNative(void)
{
unsigned long long t1 = cputime();
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
struct Pixel * pixels = malloc(sizeof(struct Pixel) * dimension * dimension);
for(int i = 0 ; i < dimension * dimension; ++i)
{
pixels[i].r = 255;
pixels[i].g = 0;
pixels[i].b = 0;
}
free(pixels);
}
unsigned long long t2 = cputime();
printf ("Native Array: %Lu us\n", (t2-t1)/1000);
}
int main(void)
{
UseNative();
UseMlib();
return 0;
}
-269
View File
@@ -1,269 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "common.h"
/********************************************************************************************/
#define POTTERY_VECTOR_PREFIX vec_uint
#define POTTERY_VECTOR_VALUE_TYPE unsigned int
#define POTTERY_VECTOR_LIFECYCLE_BY_VALUE 1
#include "pottery/vector/pottery_vector_static.t.h"
static void test_array(size_t n)
{
vec_uint_t a1, a2;
vec_uint_init(&a1);
vec_uint_init(&a2);
for(size_t i = 0; i < n; i++) {
vec_uint_insert_last(&a1, rand_get() );
vec_uint_insert_last(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *vec_uint_at(&a1, i ) * *vec_uint_at(&a2, i );
}
g_result = s;
vec_uint_destroy(&a1);
vec_uint_destroy(&a2);
}
/********************************************************************************************/
typedef struct foo_s {
struct foo_s* next;
struct foo_s* previous;
int data;
} foo_t;
#define POTTERY_DOUBLY_LINKED_LIST_PREFIX foo_list
#define POTTERY_DOUBLY_LINKED_LIST_VALUE_TYPE foo_t
#include "pottery/doubly_linked_list/pottery_doubly_linked_list_static.t.h"
static void test_list(size_t n)
{
foo_list_t a1, a2;
foo_list_init(&a1);
foo_list_init(&a2);
for(size_t i = 0; i < n; i++) {
foo_t *tmp = malloc(sizeof (foo_t));
if (tmp == NULL) abort();
tmp->data = rand_get();
foo_list_link_last(&a1, tmp);
tmp = malloc(sizeof (foo_t));
if (tmp == NULL) abort();
tmp->data = rand_get();
foo_list_link_last(&a2, tmp);
}
unsigned int s = 0;
for(foo_t *it1 = foo_list_first(&a1), *it2 = foo_list_first(&a2) ;
foo_list_entry_exists(&a1, it1) ;
it1 = foo_list_next(&a1, it1), it2 = foo_list_next(&a2, it2)) {
s += it1->data * it2->data;
}
g_result = s;
for(foo_t *it1 = foo_list_first(&a1);
foo_list_entry_exists(&a1, it1) ;
) {
foo_t *tmp = it1;
it1 = foo_list_next(&a1, it1);
free(tmp);
}
for(foo_t *it1 = foo_list_first(&a2);
foo_list_entry_exists(&a2, it1) ;
) {
foo_t *tmp = it1;
it1 = foo_list_next(&a2, it1);
free(tmp);
}
}
/********************************************************************************************/
// ERROR: it returns the wrong result.
#define POTTERY_TREE_MAP_PREFIX set_ulong
#define POTTERY_TREE_MAP_VALUE_TYPE unsigned long
#define POTTERY_TREE_MAP_LIFECYCLE_MOVE_BY_VALUE 1
#define POTTERY_TREE_MAP_COMPARE_THREE_WAY(a,b) ((a) < (b) ? -1 : (a) > (b))
#define POTTERY_TREE_MAP_LIFECYCLE_DESTROY(a) (void) a
#include "pottery/tree_map/pottery_tree_map_static.t.h"
static void test_rbtree(size_t n)
{
set_ulong_t tree;
set_ulong_init(&tree);
for (size_t i = 0; i < n; i++) {
set_ulong_insert(&tree, rand_get() );
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
if (set_ulong_find(&tree, &j ) != NULL)
s += j;
}
g_result = s;
set_ulong_destroy(&tree);
}
/********************************************************************************************/
#include "m-core.h" // For M_HASH_DEFAULT
typedef struct bar_s {
unsigned long key;
unsigned long value;
} bar_t;
#define POTTERY_OPEN_HASH_MAP_PREFIX map_ulong
#define POTTERY_OPEN_HASH_MAP_VALUE_TYPE bar_t
#define POTTERY_OPEN_HASH_MAP_KEY_TYPE unsigned long
#define POTTERY_OPEN_HASH_MAP_REF_KEY(v) v->key
#define POTTERY_OPEN_HASH_MAP_KEY_HASH(x) M_HASH_DEFAULT(x)
#define POTTERY_OPEN_HASH_MAP_KEY_EQUAL(x, y) x == y
#define POTTERY_OPEN_HASH_MAP_LIFECYCLE_MOVE(p,q) memcpy(p, q, sizeof (bar_t))
#include "pottery/open_hash_map/pottery_open_hash_map_static.t.h"
static void
test_dict(size_t n)
{
map_ulong_t dict;
map_ulong_init(&dict);
for (size_t i = 0; i < n; i++) {
unsigned long value = rand_get();
unsigned long key = rand_get();
bar_t * kyle = pottery_null;
map_ulong_emplace_key(&dict, key, &kyle, pottery_null);
kyle->key = key;
kyle->value = value;
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
bar_t *kyle = map_ulong_find(&dict, rand_get());
if (kyle != NULL)
s += kyle->value;
}
g_result = s;
map_ulong_destroy(&dict);
}
/********************************************************************************************/
typedef struct {
char a[256];
} char_array_t;
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
static inline bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a)==0; }
static inline void char_clear (char_array_t *a) { (void)a; }
static inline size_t char_hash(const char_array_t *a) {
size_t hash = 0;
const char *s = a->a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
typedef struct bars_s {
char_array_t key;
char_array_t value;
} bars_t;
#define POTTERY_OPEN_HASH_MAP_PREFIX map_achar
#define POTTERY_OPEN_HASH_MAP_VALUE_TYPE bars_t
#define POTTERY_OPEN_HASH_MAP_KEY_TYPE char_array_t
#define POTTERY_OPEN_HASH_MAP_REF_KEY(v) v->key
#define POTTERY_OPEN_HASH_MAP_KEY_HASH(x) char_hash(&x)
#define POTTERY_OPEN_HASH_MAP_KEY_EQUAL(x, y) char_equal_p(x, y)
#define POTTERY_OPEN_HASH_MAP_LIFECYCLE_MOVE(p,q) memcpy(p, q, sizeof (bars_t))
#include "pottery/open_hash_map/pottery_open_hash_map_static.t.h"
static void
test_dict_big(size_t n)
{
map_achar_t dict;
map_achar_init(&dict);
for (size_t i = 0; i < n; i++) {
char_array_t s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
bars_t * kyle = pottery_null;
map_achar_emplace_key(&dict, s1, &kyle, pottery_null);
char_set(&kyle->key, s1);
char_set(&kyle->value, s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1.a, "%u", rand_get());
bars_t *kyle = map_achar_find(&dict, s1);
if (kyle != NULL)
s ++;
}
g_result = s;
map_achar_destroy(&dict);
}
/********************************************************************************************/
#define POTTERY_VECTOR_PREFIX vec_float
#define POTTERY_VECTOR_VALUE_TYPE float
#define POTTERY_VECTOR_LIFECYCLE_BY_VALUE 1
#include "pottery/vector/pottery_vector_static.t.h"
#define POTTERY_INTRO_SORT_PREFIX sort_float
#define POTTERY_INTRO_SORT_VALUE_TYPE float
#define POTTERY_INTRO_SORT_LIFECYCLE_MOVE_BY_VALUE 1
#define POTTERY_INTRO_SORT_COMPARE_THREE_WAY(x, y) ( *x < *y ? -1 : *x > *y)
#include "pottery/intro_sort/pottery_intro_sort_static.t.h"
static void test_sort(size_t n)
{
vec_float_t a1;
vec_float_init(&a1);
for(size_t i = 0; i < n; i++) {
vec_float_insert_last(&a1, rand_get() );
}
sort_float(vec_float_at(&a1, 0), n);
g_result = *vec_float_at(&a1, 0);
vec_float_destroy(&a1);
}
/********************************************************************************************/
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[])
{
test("POTTERY", numberof(table), table, argc, argv);
exit(0);
}
-113
View File
@@ -1,113 +0,0 @@
#define NDEBUG
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include "qlibc/qlibc.h"
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
qvector_t *a1 = qvector(16, sizeof(unsigned int), QVECTOR_RESIZE_DOUBLE);
if (!a1) abort();
qvector_t *a2 = qvector(16, sizeof(unsigned int), QVECTOR_RESIZE_DOUBLE);
if (!a2) abort();
for(size_t i = 0; i < n; i++) {
unsigned int v = rand_get();
bool success = a1->addlast(a1, &v);
if (!success) abort();
v = rand_get();
success = a2->addlast(a2, &v);
if (!success) abort();
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *(unsigned int*)a1->getat(a1, i, false) * *(unsigned int *) a2->getat(a2, i, false);
}
g_result = s;
a1->free(a1);
a2->free(a2);
}
/********************************************************************************************/
static void test_list (size_t n)
{
qlist_t *a1 = qlist(0);
qlist_t *a2 = qlist(0);
for(size_t i = 0; i < n; i++) {
unsigned int u = rand_get(), v = rand_get();
a1->addlast(a1, &u, sizeof(unsigned int));
a2->addlast(a2, &v, sizeof(unsigned int));
}
unsigned int s = 0;
qlist_obj_t it1, it2;
memset(&it1, 0, sizeof (it1));
memset(&it2, 0, sizeof (it2));
while (a1->getnext(a1, &it1, false) == true
&& a2->getnext(a2, &it2, false) == true) {
s += *(unsigned int*)it1.data * *(unsigned int*)it2.data;
}
g_result = s;
a1->free(a1);
a2->free(a2);
}
/********************************************************************************************/
static int compare(const void *key1, size_t size1,
const void *key2, size_t size2)
{
assert(size1 == sizeof(unsigned long));
assert(size2 == sizeof(unsigned long));
const unsigned long *pa = key1;
const unsigned long *pb = key2;
return (*pa < *pb) ? -1 : (*pa > *pb);
}
static void test_rbtree(size_t n)
{
qtreetbl_t *tree = qtreetbl(0);
tree->set_compare(tree, compare);
for (size_t i = 0; i < n; i++) {
unsigned long v = rand_get();
tree->put_by_obj(tree,
&v, sizeof(unsigned long), /* key */
&v, sizeof(unsigned long) /* value */ );
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long v = rand_get();
void *obj = tree->get_by_obj(tree, (const char*)&v, sizeof(unsigned long), NULL, 0);
if (obj)
s += *(unsigned long*)obj;
}
g_result = s;
tree->free(tree);
}
/* 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[])
{
test("QLIBC", numberof(table), table, argc, argv);
exit(0);
}
-179
View File
@@ -1,179 +0,0 @@
#define NDEBUG
#include <QLinkedList>
#include <QVector>
#include <QSet>
#include <QMap>
#include <QHash>
#include <QtAlgorithms>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
using namespace std;
#include "common.h"
/********************************************************************************************/
static void test_list(size_t n)
{
QLinkedList<unsigned int> a1, a2;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
a2.push_back(rand_get() );
}
unsigned int s = 0;
for (QLinkedList<unsigned int>::const_iterator ci1 = a1.begin(), ci2 = a2.begin (); ci1 != a1.end(); ++ci1, ++ci2) {
s += *ci1 * *ci2;
}
g_result = s;
}
/********************************************************************************************/
static void test_array(size_t n)
{
QVector<unsigned int> a1, a2;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
a2.push_back(rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += a1[i] * a2[i];
}
g_result = s;
}
/********************************************************************************************/
/* NOTE: QSet doesn't perform full ordering. I can't find any container
which is a set with total ordering within Qt. So we have to fake it. */
static void test_rbtree(size_t n)
{
QMap<unsigned long, QHashDummyValue> tree;
for (size_t i = 0; i < n; i++) {
tree[rand_get()] = QHashDummyValue();
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
QMap<unsigned long, QHashDummyValue>::iterator it = tree.find(rand_get());
if (it != tree.end())
s += it.key();
}
g_result = s;
}
/********************************************************************************************/
static void
test_dict1(size_t n)
{
QMap<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++) {
QMap<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
if (it != dict.end())
s += it.value();
}
g_result = s;
}
/********************************************************************************************/
static void
test_dict2(size_t n)
{
QHash<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++) {
QHash<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
if (it != dict.end())
s += it.value();
}
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; }
};
uint qHash(const char_array_s &k)
{
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)
{
QHash<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());
QHash<char_array_s, char_array_s>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
}
g_result = s;
}
/********************************************************************************************/
static void test_sort(size_t n)
{
QVector<float> a1;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
}
qSort(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},
{ 50, "Sort",10000000, 0, test_sort, 0}
};
int main(int argc, const char *argv[])
{
test("QT", numberof(table), table, argc, argv);
exit(0);
}
-185
View File
@@ -1,185 +0,0 @@
#define NDEBUG
#define RAPIDJSON_HAS_STDSTRING 1
#include "rapidjson/reader.h"
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/error/en.h"
#include <stdio.h>
#include <string>
#include <vector>
#include "common.h"
using namespace std;
/********************************************************************************************/
vector<string> vstring;
vector<string> vstring2;
static void
bench_vector_string_init(size_t n)
{
for(size_t i = 0; i < n; i++)
{
char buff[100];
snprintf(buff, sizeof(buff), "%zu", i);
std::string s(buff);
vstring.push_back(s);
}
}
static void
bench_vector_string_init_big(size_t n)
{
for(size_t i = 0; i < n; i++)
{
char buff[100];
snprintf(buff, sizeof(buff), "%zu%zu%zu%zu%zu%zu", i, i, i, i, i, i);
std::string s(buff);
vstring.push_back(s);
}
}
static void
bench_vector_string_json_run(size_t n)
{
if (vstring.size() != n) {
printf("Size are different!\n");
abort();
}
FILE *f = fopen("tmp-serial.json", "wt");
if (!f) abort();
{
// Prepare writer and output stream.
char writeBuffer[65536];
typedef rapidjson::AutoUTFOutputStream<unsigned, rapidjson::FileWriteStream> OutputStream;
rapidjson::FileWriteStream os(f, writeBuffer, sizeof(writeBuffer));
OutputStream eos(os, rapidjson::kUTF8, false);
rapidjson::PrettyWriter<OutputStream, rapidjson::UTF8<>, rapidjson::AutoUTF<unsigned> > writer(eos);
writer.StartArray();
for(size_t i = 0; i < n; i++){
writer.String(vstring[i]);
}
writer.EndArray();
}
fclose(f);
f = fopen("tmp-serial.json", "rt");
if (!f) abort();
{
char readBuffer[65536];
rapidjson::FileReadStream is(f, readBuffer, sizeof(readBuffer));
rapidjson::Document d;
d.ParseStream(is);
for(size_t i = 0; i < d.Size(); i++) {
rapidjson::Value& s = d[i];
vstring2.push_back(s.GetString());
}
}
fclose(f);
if (vstring2.size() != n) {
printf("Size are different!\n");
abort();
}
}
static void
bench_vector_string_clear(void)
{
if (vstring != vstring2) {
printf("Array are different!\n");
abort();
}
}
/********************************************************************************************/
vector<unsigned long> vulong;
vector<unsigned long> vulong2;
static void
bench_vector_ulong_init(size_t n)
{
for(size_t i = 0; i < n; i++)
{
vulong.push_back(i*i);
}
}
static void
bench_vector_ulong_json_run(size_t n)
{
if (vulong.size() != n) {
printf("Size are different!\n");
abort();
}
FILE *f = fopen("tmp-serial.json", "wt");
if (!f) abort();
{
// Prepare writer and output stream.
char writeBuffer[65536];
typedef rapidjson::AutoUTFOutputStream<unsigned, rapidjson::FileWriteStream> OutputStream;
rapidjson::FileWriteStream os(f, writeBuffer, sizeof(writeBuffer));
OutputStream eos(os, rapidjson::kUTF8, false);
rapidjson::PrettyWriter<OutputStream, rapidjson::UTF8<>, rapidjson::AutoUTF<unsigned> > writer(eos);
writer.StartArray();
for(size_t i = 0; i < n; i++){
writer.Int64(vulong[i]);
}
writer.EndArray();
}
fclose(f);
f = fopen("tmp-serial.json", "rt");
if (!f) abort();
{
char readBuffer[65536];
rapidjson::FileReadStream is(f, readBuffer, sizeof(readBuffer));
rapidjson::Document d;
d.ParseStream(is);
for(size_t i = 0; i < d.Size(); i++) {
rapidjson::Value& s = d[i];
vulong2.push_back(s.GetInt64());
}
}
fclose(f);
if (vulong2.size() != n) {
printf("Size are different!\n");
abort();
}
}
static void
bench_vector_ulong_clear(void)
{
if (vulong != vulong2) {
printf("Array are different!\n");
abort();
}
}
/********************************************************************************************/
const config_func_t table[] = {
{110, "serial-json STR", 10000000, bench_vector_string_init, bench_vector_string_json_run, bench_vector_string_clear},
{111, "serial-json STR.big", 10000000, bench_vector_string_init_big, bench_vector_string_json_run, bench_vector_string_clear},
{112, "serial-json INT", 10000000, bench_vector_ulong_init, bench_vector_ulong_json_run, bench_vector_ulong_clear}
};
int main(int argc, const char *argv[])
{
test("RAPIDJSON", numberof(table), table, argc, argv);
exit(0);
}
-159
View File
@@ -1,159 +0,0 @@
#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);
}
-96
View File
@@ -1,96 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <rigtorp/MPMCQueue.h>
#include "common.h"
/********************************************************************************************/
rigtorp::MPMCQueue<unsigned int> g_buff(4*64);
rigtorp::MPMCQueue<unsigned long long> g_final(4*64);
static void final(void *arg)
{
size_t *p_n = (size_t*) arg;
size_t n = *p_n;
unsigned long long j, s = 0;
for(unsigned int i = 0; i < n;i++) {
g_final.pop(j);
s += j;
}
g_result = s;
}
static void conso(void *arg)
{
unsigned int j;
size_t *p_n = (size_t*)arg;
size_t n = *p_n;
unsigned long long s = 0;
for(unsigned int i = 0; i < n;i++) {
g_buff.pop(j);
s += j;
}
g_final.push(s);
}
static void prod(void *arg)
{
size_t *p_n = (size_t*)arg;
size_t n = *p_n;
size_t r = n;
for(unsigned int i = 0; i < n;i++) {
g_buff.push(r);
r = r * 31421U + 6927U;
}
}
static void test_queue(size_t n)
{
const int cpu_count = 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;
}
rand_get();
// 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);
}
for(int i = 0; i < conso_count; i++) {
m_thread_create (idx_c[i], conso, &n);
}
size_t n2 = conso_count;
m_thread_create(idx_final, final, &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);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
};
int main(int argc, const char *argv[])
{
test("RIGTORP-MPMC-QUEUE", numberof(table), table, argc, argv);
exit(0);
}
-122
View File
@@ -1,122 +0,0 @@
#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 <sparsepp/spp.h>
using spp::sparse_hash_map;
using namespace std;
#include "common.h"
/********************************************************************************************/
static void
test_dict2(size_t n)
{
sparse_hash_map<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++) {
sparse_hash_map<unsigned long, unsigned long>::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;
};
};
}
static void
test_dict_big(size_t n)
{
sparse_hash_map<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());
sparse_hash_map<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)
{
sparse_hash_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();
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();
sparse_hash_map<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}
};
int main(int argc, const char *argv[])
{
test("SPARSEPP", numberof(table), table, argc, argv);
exit(0);
}
-193
View File
@@ -1,193 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "common.h"
/********************************************************************************************/
#define i_val unsigned int
#define i_tag uint
#include <stc/cvec.h>
static void test_array(size_t n)
{
cvec_uint a1 = cvec_uint_init(), a2 = cvec_uint_init();
for(size_t i = 0; i < n; i++) {
cvec_uint_push_back(&a1, rand_get() );
cvec_uint_push_back(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *cvec_uint_at(&a1, i ) * *cvec_uint_at(&a2, i );
}
g_result = s;
cvec_uint_drop(&a1);
cvec_uint_drop(&a2);
}
/********************************************************************************************/
#define i_val unsigned int
#define i_tag uint
#include <stc/clist.h>
static void test_list(size_t n)
{
clist_uint a1 = clist_uint_init(), a2 = clist_uint_init();
for(size_t i = 0; i < n; i++) {
clist_uint_push_front(&a1, rand_get() );
clist_uint_push_front(&a2, rand_get() );
}
unsigned int s = 0;
for(clist_uint_iter it1 = clist_uint_begin(&a1), it2 = clist_uint_begin(&a2) ;
it1.ref != clist_uint_end(&a1).ref ;
clist_uint_next(&it1), clist_uint_next(&it2) ) {
s += *it1.ref * *it2.ref;
}
g_result = s;
clist_uint_drop(&a1);
clist_uint_drop(&a2);
}
/********************************************************************************************/
#define i_tag ulong
#define i_key unsigned long
#include <stc/csset.h>
static void test_rbtree(size_t n)
{
csset_ulong tree = csset_ulong_init();
for (size_t i = 0; i < n; i++) {
csset_ulong_insert(&tree, rand_get() );
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long j = rand_get();
if (csset_ulong_get(&tree, j ) != NULL)
s+= j;
}
g_result = s;
csset_ulong_drop(&tree);
}
/********************************************************************************************/
#define i_tag ulong
#define i_key unsigned long
#define i_val unsigned long
#include <stc/cmap.h>
static void
test_dict(size_t n)
{
cmap_ulong dict = cmap_ulong_init();
for (size_t i = 0; i < n; i++) {
unsigned long value = rand_get();
unsigned long key = rand_get();
cmap_ulong_insert_or_assign(&dict, key, value);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
const cmap_ulong_value*val = cmap_ulong_get(&dict, rand_get());
if (val != NULL)
s += val->second;
}
g_result = s;
cmap_ulong_drop(&dict);
}
/********************************************************************************************/
typedef struct {
char a[256];
} char_array_t;
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
static inline bool char_equal_p (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a)==0; }
static inline void char_clear (char_array_t *a) { (void)a; }
static inline size_t char_hash(const char_array_t *a) {
size_t hash = 0;
const char *s = a->a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
#define i_tag achar
#define i_key char_array_t
#define i_val char_array_t
#define i_hash char_hash
#define i_eq char_equal_p
#include <stc/cmap.h>
static void
test_dict_big(size_t n)
{
cmap_achar dict = cmap_achar_init();
for (size_t i = 0; i < n; i++) {
char_array_t s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
cmap_achar_insert_or_assign(&dict, s1, s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1.a, "%u", rand_get());
const cmap_achar_value*val = cmap_achar_get(&dict, s1);
if (val != NULL)
s ++;
}
g_result = s;
cmap_achar_drop(&dict);
}
/********************************************************************************************/
#define i_val float
#define i_tag float
#include <stc/cvec.h>
static void test_sort(size_t n)
{
cvec_float a1 = cvec_float_init();
for(size_t i = 0; i < n; i++) {
cvec_float_push_back(&a1, rand_get() );
}
cvec_float_sort(&a1);
g_result = *cvec_float_at(&a1, 0);
cvec_float_drop(&a1);
}
/********************************************************************************************/
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[])
{
test("STC", numberof(table), table, argc, argv);
exit(0);
}
-278
View File
@@ -1,278 +0,0 @@
#define NDEBUG
#include <vector>
#include <deque>
#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>
using namespace std;
#include "common.h"
/********************************************************************************************/
static void test_list(size_t n)
{
list<unsigned int> a1, a2;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
a2.push_back(rand_get() );
}
unsigned int s = 0;
for (list<unsigned int>::const_iterator ci1 = a1.begin(), ci2 = a2.begin (); ci1 != a1.end(); ++ci1, ++ci2) {
s += *ci1 * *ci2;
}
g_result = s;
}
/********************************************************************************************/
static void test_array(size_t n)
{
vector<unsigned int> a1, a2;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
a2.push_back(rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += a1[i] * a2[i];
}
g_result = s;
}
/********************************************************************************************/
static void test_deque(size_t n)
{
deque<unsigned int> a1, a2;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
a2.push_back(rand_get() );
}
unsigned int s = 0;
for(auto it1 = a1.cbegin(), it2 = a2.cbegin();
it1 != a1.cend() ;it1++, it2++) {
s += *it1 * *it2;
}
g_result = s;
}
/********************************************************************************************/
static void test_rbtree(size_t n)
{
set<unsigned long> tree;
for (size_t i = 0; i < n; i++) {
tree.insert(rand_get());
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
set<unsigned long>::iterator it = tree.find(rand_get());
if (it != tree.end())
s += *it;
}
g_result = s;
}
/********************************************************************************************/
static void
test_dict1(size_t n)
{
map<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++) {
map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
if (it != dict.end())
s += it->second;
}
g_result = s;
}
/********************************************************************************************/
static void
test_dict2(size_t n)
{
unordered_map<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++) {
unordered_map<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)
{
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 {
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)
{
unordered_map<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());
unordered_map<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)
{
unordered_map<string, string> dict;
for (size_t i = 0; i < n; i++) {
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 = to_string (rand_get());
unordered_map<string, string>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
}
g_result = s;
}
static void
test_dict_str2(size_t n)
{
map<string, string> dict;
for (size_t i = 0; i < n; i++) {
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 = to_string(rand_get());
map<string, string>::iterator it = dict.find(s1);
if (it != dict.end())
s ++;
}
g_result = s;
}
/********************************************************************************************/
static void test_sort(size_t n)
{
vector<float> a1;
for(size_t i = 0; i < n; i++) {
a1.push_back(rand_get() );
}
sort(a1.begin(), a1.end());
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},
{ 21, "Deque", 100000000, 0, test_deque, 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[])
{
test("STL", numberof(table), table, argc, argv);
exit(0);
}
-323
View File
@@ -1,323 +0,0 @@
/*
* M*LIB - String Bench
*
* Copyright (c) 2017-2023, 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.
*/
#define NDEBUG
#include <string>
#include <sstream>
#include <string>
#include <vector>
#define M_USE_SMALL_NAME 0
#include "common.h"
#include "m-string.h"
#ifdef BENCH_CAN_USE_SDS
extern "C" {
#include "sds.h"
};
#endif
#ifdef BENCH_CAN_USE_BSTRLIB
#include "bstrlib.h"
#include "bstrwrap.h"
#include "bstraux.h"
#endif
#if defined(BENCH_CAN_USE_POTTERY)
#include "pottery/string/string.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;
static void replace_all_stl(string &str, const char pattern[], const char replace[])
{
size_t pos = 0;
do {
pos = str.find(pattern, pos);
if (pos != string::npos) {
str.replace (pos, strlen(pattern), replace);
}
} while (pos != string::npos);
}
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]];
}
replace_all_stl(str, "1234", "WELL");
replace_all_stl(str, "56789", "DONE");
return str.length();
}
size_t bench_mlib(unsigned n)
{
m_string_t *tab = (m_string_t*) malloc (n * sizeof (m_string_t));
assert (tab != 0);
// P1
for(unsigned i = 0; i < n; i++) {
m_string_init(tab[i]);
m_string_set_ui(tab[i], rand_get());
}
// P2
m_string_t str;
m_string_init(str);
for(unsigned i = 0; i < n; i++) {
m_string_cat(str, tab[permutation_tab[i]]);
}
// P3
m_string_replace_all_cstr(str, "1234", "WELL");
m_string_replace_all_cstr(str, "56789", "DONE");
size_t length = m_string_size(str);
// Clean
m_string_clear(str);
for(unsigned i = 0; i < n; i++) {
m_string_clear(tab[i]);
}
free(tab);
return length;
}
#ifdef BENCH_CAN_USE_BSTRLIB
static void replace_all_bstrlib(CBString &str, const char pattern[], const char replace[])
{
size_t pos = 0;
do {
pos = str.find(pattern, pos);
if (pos != string::npos) {
str.replace (pos, strlen(pattern), replace);
}
} while (pos != string::npos);
}
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]];
}
replace_all_bstrlib(str, "1234", "WELL");
replace_all_bstrlib(str, "56789", "DONE");
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;
}
sds replace_all_sds(sds str, const char pattern[], const char replace[])
{
size_t pos = 0;
do {
char *p = strstr(&str[pos], pattern);
pos = p == NULL ? -1U : (p - str);
if (pos != -1U) {
str = SDS_replace_at(str, pos, strlen(pattern), replace);
}
} while (pos != -1U);
return str;
}
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
str = replace_all_sds(str, "1234", "WELL");
str = replace_all_sds(str, "56789", "DONE");
// Clean
size_t length = sdslen(str);
sdsfree(str);
for(unsigned i = 0; i < n; i++) {
sdsfree(tab[i]);
}
free(tab);
return length;
}
#endif
#if defined(BENCH_CAN_USE_POTTERY)
size_t pottery_find_cstr(string_t *b, size_t index, const char *pattern)
{
// I cannot find such function in the API
const char *s = string_cstr(b);
const char *r = strstr(&s[index], pattern);
return r == NULL ? SIZE_MAX : r - s;
}
void pottery_replace_all_str(string_t *str, const char *s, const char *d)
{
size_t i = 0;
while (true) {
i = pottery_find_cstr(str, i, s);
if (i==SIZE_MAX) return;
string_remove(str, i, strlen(s));
string_insert_cstr(str, i, d);
}
}
size_t bench_pottery(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_append_format(&tab[i], "%u", rand_get());
}
// P2
string_t str;
string_init(&str);
for(unsigned i = 0; i < n; i++) {
string_append_string(&str, &tab[permutation_tab[i]]);
}
// P3
pottery_replace_all_str(&str, "1234", "WELL");
pottery_replace_all_str(&str, "56789", "DONE");
size_t length = string_length(&str);
// Clean
string_clear(&str);
for(unsigned i = 0; i < n; i++) {
string_clear(&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_BSTRLIB
case 4:
length = bench_bstrlib(n);
name = "BSTRLIB";
break;
#endif
#if defined(BENCH_CAN_USE_POTTERY)
case 5:
length = bench_pottery(n);
name = "POTTERY";
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;
}
File diff suppressed because it is too large. Load diff
-221
View File
@@ -1,221 +0,0 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "tommyds/tommy.h"
#include "common.h"
/********************************************************************************************/
static void test_array(size_t n)
{
tommy_array a1, a2;
tommy_array_init(&a1);
tommy_array_init(&a2);
for(size_t i = 0; i < n; i++) {
unsigned int v = rand_get();
tommy_array_insert(&a1, (void*)(uintptr_t) v);
v = rand_get();
tommy_array_insert(&a2, (void*)(uintptr_t) v);
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += (uintptr_t) *tommy_array_ref(&a1, i) * (uintptr_t) *tommy_array_ref(&a2, i );
}
g_result = s;
tommy_array_done(&a1);
tommy_array_done(&a2);
}
/********************************************************************************************/
struct list_node_s {
unsigned int value;
tommy_node node;
};
static void test_list (size_t n)
{
tommy_list a1, a2;
tommy_list_init(&a1);
tommy_list_init(&a2);
for(size_t i = 0; i < n; i++) {
struct list_node_s *obj;
obj = malloc(sizeof(struct list_node_s));
if (obj == NULL) abort();
obj->value = rand_get();
tommy_list_insert_tail(&a1, &obj->node, obj);
obj = malloc(sizeof(struct list_node_s));
if (obj == NULL) abort();
obj->value = rand_get();
tommy_list_insert_tail(&a2, &obj->node, obj);
}
unsigned int s = 0;
for(tommy_node *it1 = tommy_list_head(&a1), *it2 = tommy_list_head(&a2)
; it1 != NULL; it1 = it1->next, it2 = it2->next ) {
struct list_node_s *obj1 = it1->data, *obj2 = it2->data;
s += obj1->value * obj2->value;
}
g_result = s;
tommy_list_foreach(&a1, free);
tommy_list_foreach(&a2, free);
}
/********************************************************************************************/
struct tree_node_s {
unsigned long value;
tommy_tree_node node;
} ;
static int compare(const void *a, const void *b)
{
const struct tree_node_s *pa = a;
const struct tree_node_s *pb = b;
return (pa->value < pb->value) ? -1 : (pa->value > pb->value);
}
static void test_rbtree(size_t n)
{
tommy_tree tree;
tommy_tree_init(&tree, compare);
for (size_t i = 0; i < n; i++) {
struct tree_node_s *obj = malloc(sizeof(struct tree_node_s));
if (obj == NULL) abort();
obj->value = rand_get();
tommy_tree_insert(&tree, &obj->node, obj);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
struct tree_node_s key = { .value = rand_get() };
struct tree_node_s *obj = tommy_tree_search (&tree, &key);
if (obj)
s += obj->value;
}
g_result = s;
tommy_tree_foreach(&tree, free);
}
/********************************************************************************************/
struct hash_i_node_s {
unsigned long key;
unsigned long value;
tommy_node node;
};
static inline size_t hash_func (const unsigned long key)
{
return key;
}
static int equal_func(const void* arg, const void *obj_p)
{
const struct hash_i_node_s *obj = obj_p;
const unsigned long *key = arg;
return *key != obj->key;
}
static void
test_dict(size_t n)
{
tommy_hashlin dict;
tommy_hashlin_init(&dict);
for (size_t i = 0; i < n; i++) {
struct hash_i_node_s *obj = malloc(sizeof (struct hash_i_node_s));
if(!obj) abort();
obj->key = rand_get();
obj->value = rand_get();
tommy_hashlin_insert(&dict, &obj->node, obj, hash_func(obj->key));
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
struct hash_i_node_s *obj = tommy_hashlin_search(&dict, &equal_func,
&key,
hash_func(key));
if (obj)
s += obj->value;
}
g_result = s;
tommy_hashlin_done(&dict);
}
/********************************************************************************************/
typedef char char_array_t[256];
struct hash_b_node_s {
char_array_t key;
char_array_t value;
tommy_node node;
};
static int char_equal (const void *arg, const void *obj_p)
{
const struct hash_b_node_s *obj = obj_p;
const char_array_t *key = (const char_array_t *)arg;
return strcmp(*key, (obj->key));
}
static size_t char_hash(const char *s)
{
size_t hash = 0;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
static void
test_dict_big(size_t n)
{
tommy_hashlin dict;
tommy_hashlin_init(&dict);
for (size_t i = 0; i < n; i++) {
struct hash_b_node_s *obj = malloc(sizeof (struct hash_b_node_s));
if(!obj) abort();
sprintf(obj->key, "%u", rand_get());
sprintf(obj->value, "%u", rand_get());
/* NOTE: this is not an insert_or_replace func...
It will always insert a new node, even if the key already exists...
Maybe searching then inserting will be a better representative?
*/
tommy_hashlin_insert(&dict, &obj->node, obj, char_hash(obj->key));
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1, "%u", rand_get());
struct hash_b_node_s *obj = tommy_hashlin_search(&dict, &char_equal,
&s1,
char_hash(s1));
if (obj)
s ++;
}
g_result = s;
tommy_hashlin_done(&dict);
}
/********************************************************************************************/
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[])
{
test("TommyDS", numberof(table), table, argc, argv);
exit(0);
}
-215
View File
@@ -1,215 +0,0 @@
#define NDEBUG
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "include/uthash.h"
#include "include/utarray.h"
#include "include/utlist.h"
#include "common.h"
/********************************************************************************************/
static const UT_icd int_icd = {
.sz = sizeof (unsigned int),
.init = NULL,
.dtor = NULL,
.copy = NULL
};
static void test_array(size_t n)
{
UT_array a1, a2;
utarray_init(&a1, &int_icd);
utarray_init(&a2, &int_icd);
for(size_t i = 0; i < n; i++) {
unsigned int v1 = rand_get(), v2 = rand_get();
utarray_push_back(&a1, &v1);
utarray_push_back(&a2, &v2);
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *(unsigned long *) utarray_eltptr(&a1, i ) * *(unsigned long *)utarray_eltptr(&a2, i );
}
g_result = s;
utarray_done(&a1);
utarray_done(&a2);
}
/********************************************************************************************/
struct list_node_s {
unsigned int value;
struct list_node_s *next;
};
static void test_list (size_t n)
{
struct list_node_s *a1 = NULL, *a2 = NULL;
for(size_t i = 0; i < n; i++) {
struct list_node_s *p = malloc(sizeof(struct list_node_s));
if (!p) abort();
p->value = rand_get();
LL_PREPEND(a1, p);
p = malloc(sizeof(struct list_node_s));
if (!p) abort();
p->value = rand_get();
LL_PREPEND(a2, p);
}
unsigned int s = 0;
for(struct list_node_s *it1 = a1, *it2 = a2
; it1 != NULL
; it1 = it1->next, it2 = it2->next) {
s += it1->value * it2->value;
}
g_result = s;
struct list_node_s *el;
LL_FOREACH(a1, el) free(el);
LL_FOREACH(a2, el) free(el);
}
/********************************************************************************************/
struct hash_i_node_s {
unsigned long key;
unsigned long value;
UT_hash_handle hh;
};
static void
test_dict(size_t n)
{
struct hash_i_node_s *dict = NULL;
for (size_t i = 0; i < n; i++) {
unsigned long k = rand_get();
struct hash_i_node_s *s;
HASH_FIND(hh, dict, &k, sizeof (unsigned long), s);
if (s == NULL) {
s = malloc(sizeof(struct hash_i_node_s));
if (!s) abort();
s->key = k;
s->value = rand_get();
HASH_ADD(hh, dict, key, sizeof(unsigned long), s);
}
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long k = rand_get();
struct hash_i_node_s *sp;
HASH_FIND(hh, dict, &k, sizeof (unsigned long), sp);
if (sp != NULL)
s += sp->value;
}
g_result = s;
struct hash_i_node_s *current, *tmp;
HASH_ITER(hh, dict, current, tmp) {
HASH_DEL(dict,current); /* delete it (users advances to next) */
free(current); /* free it */
}
}
/********************************************************************************************/
typedef struct {
char a[256];
} char_array_t;
struct hash_b_node_s {
char_array_t key;
char_array_t value;
UT_hash_handle hh;
};
static void
test_dict_big(size_t n)
{
struct hash_b_node_s *dict = NULL;
for (size_t i = 0; i < n; i++) {
char_array_t k;
sprintf(k.a, "%u", rand_get());
struct hash_b_node_s *s;
HASH_FIND(hh, dict, &k, sizeof (char_array_t), s);
if (s == NULL) {
s = malloc(sizeof(struct hash_b_node_s));
if (!s) abort();
memcpy(s->key.a, k.a, sizeof(s->key));
sprintf(s->value.a, "%u", rand_get());
HASH_ADD(hh, dict, key, sizeof(char_array_t), s);
}
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t k;
sprintf(k.a, "%u", rand_get());
struct hash_b_node_s *sp;
HASH_FIND(hh, dict, &k, sizeof (char_array_t), sp);
if (sp != NULL)
s ++;
}
g_result = s;
struct hash_b_node_s *current, *tmp;
HASH_ITER(hh, dict, current, tmp) {
HASH_DEL(dict,current); /* delete it (users advances to next) */
free(current); /* free it */
}
}
/********************************************************************************************/
static const UT_icd float_icd = {
.sz = sizeof (float),
.init = NULL,
.dtor = NULL,
.copy = NULL
};
static int cmp(const void *a1, const void *a2)
{
const float *f1 = a1;
const float *f2 = a2;
return (*f1 < *f2) ? -1 : (*f1 > *f2);
}
static void test_sort(size_t n)
{
UT_array a1;
utarray_init(&a1, &float_icd);
for(size_t i = 0; i < n; i++) {
float v1 = rand_get();
utarray_push_back(&a1, &v1);
}
utarray_sort(&a1, cmp);
g_result = *(float *)utarray_eltptr(&a1, 0);
utarray_done(&a1);
}
/********************************************************************************************/
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[])
{
test("UT-HASH", numberof(table), table, argc, argv);
exit(0);
}
-42
View File
@@ -1,42 +0,0 @@
#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);
}
-306
View File
@@ -1,306 +0,0 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2023, 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)] [--graph]\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 the file '%s'\n", filename);
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);
} else {
ba = avg;
}
// 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;
}
-171
View File
@@ -1,171 +0,0 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2023, 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 __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-thread.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 in microsecond. */
#if defined(_WIN32)
/* WINDOWS variant */
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);
}
#else
/* UNIX variant */
static inline unsigned long long
cputime (void)
{
#if defined(MULTI_THREAD_MEASURE)
// Multi thread: Uses real time
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec * 1000000ULL + tv.tv_usec;
#else
// Single thread: Uses process usage time
struct rusage rus;
getrusage (0, &rus);
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
#endif
}
#endif
// The result of the bench
extern unsigned long g_result;
// 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 inline unsigned int rand_get(void)
{
randValue = randValue * 31421U + 6927U;
return randValue;
}
// 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;
if (str != NULL) {
printf ("%20.20s time %.2f ms for n = %lu [r=%lu]\n", str, result, (unsigned long) n, g_result);
}
return result;
}
// 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)
{
#if defined(_WIN32)
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#elif defined(USE_SYSCTL)
int nm[2];
int count = 0;
size_t len = sizeof (count);
nm[0] = CTL_HW;
nm[1] = HW_NCPU;
sysctl(nm, 2, &count, &len, NULL, 0);
return MAX(1, count);
#elif defined (_SC_NPROCESSORS_ONLN)
return sysconf(_SC_NPROCESSORS_ONLN);
#else
return 1;
#endif
}
#ifdef __cplusplus
extern "C"
#endif
void compiler_barrier(void *p);
#endif
-71
View File
@@ -1,71 +0,0 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2023, 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define NDEBUG
#include "m-dict.h"
static inline bool oor_equal_p(int64_t k, unsigned char n)
{
return k == (int)-n-1;
}
static inline void oor_set(int64_t *k, unsigned char n)
{
*k = (int)-n-1;
}
DICT_OA_DEF2(dict_int64, int64_t, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)), int64_t, M_DEFAULT_OPLIST)
static int64_t get_rand(void)
{
int64_t n;
do {
n = (rand() * RAND_MAX + rand()) * RAND_MAX + rand();
} while (n == -1 || n == -2);
return n;
}
// Let's push value into the dict until the system abandons!
static void find_max(void)
{
dict_int64_t dict;
dict_int64_init(dict);
size_t n = 0;
while (true) {
dict_int64_set_at (dict, get_rand(), get_rand());
n++;
if ((n & ((1<<20)-1)) == 0) {
printf ("n=%zu\n", n);
}
}
dict_int64_clear(dict);
}
int main()
{
find_max();
}
-22
View File
@@ -1,22 +0,0 @@
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
@@ -1,344 +0,0 @@
// 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;
}
-266
View File
@@ -1,266 +0,0 @@
// 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);
}
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_DEFAULT_OPLIST)
#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;
}
-940
View File
@@ -1,940 +0,0 @@
/**
* Memory pool benchmark.
*
* Results (the better values for each measure from at least 3 runs)for
* Intel Core i7-4650U CPU 1.70GHz, 8GB RAM:
*
* small object size: 24
* big object size: 240
* huge object size: 8305
*
* boost::pool (Small): 258ms
* boost::pool w/ free (Small): 165ms
* boost::pool (Big): 174ms
* boost::pool w/ free (Big): 154ms
* boost::pool cr. & destr.: 133ms
*
* ngx_pool (Small): 305ms
* ngx_pool (Big): 132ms
* ngx_pool w/ free (Mix): 542ms
* ngx_pool cr. & destr.: 98ms
*
* tfw_pool (Small): 279ms
* tfw_pool w/ free (Small): 101ms
* tfw_pool (Big): 106ms
* tfw_pool w/ free (Big): 50ms
* tfw_pool w/ free (Mix): 107ms
* tfw_pool cr. & destr.: 53ms
*
* Copyright (C) 2015 Alexander Krizhanovsky (ak@natsys-lab.com).
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* See http://www.gnu.org/licenses/lgpl.html .
*
* From https://github.com/natsys/blog/tree/master/mempool
*/
#define NDEBUG
#include <assert.h>
#include <stdlib.h>
#include <chrono>
#include <functional>
#include <iomanip>
#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;
// sizeof(TfwStr)
struct Small {
long l[3];
std::string
operator+(const char *str)
{ return std::string(str) + " (Small)"; }
};
// size of common HTTP headers table
struct Big {
Small s[10];
std::string
operator+(const char *str)
{ return std::string(str) + " (Big)"; }
};
// very big allocation
struct Huge {
char c[PAGE_SIZE * 2 + 113];
std::string
operator+(const char *str)
{ return std::string(str) + " (Huge)"; }
};
static const size_t N = 20 * 1000 * 1000;
void
benchmark(std::string &&desc, std::function<void ()> cb)
{
using namespace std::chrono;
// steady_clock has the same resolution as high_resolution_clock
auto t(steady_clock::now());
cb();
auto dt = steady_clock::now() - t;
std::cout << std::setw(30) << std::right << desc << ": "
<< duration_cast<milliseconds>(dt).count()
<< "ms" << std::endl;
}
#define touch_obj(o) \
if (__builtin_expect(!o, 0)) { \
std::cerr << "failed alloc" << std::endl; \
break; \
} else { \
*(long *)o = 1; \
}
/*
* ------------------------------------------------------------------------
* Boost::pool
* ------------------------------------------------------------------------
*/
#ifdef USE_BOOST
template<class T>
void
benchmark_boost_pool()
{
boost::object_pool<T> p;
benchmark(std::move(T() + "boost::pool"), [&]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = p.malloc();
touch_obj(o);
}
});
}
template<class T>
void
benchmark_boost_pool_free()
{
boost::object_pool<T> p;
benchmark(std::move(T() + "boost::pool w/ free"), [&]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = p.malloc();
touch_obj(o);
if (__builtin_expect(!(i & 3), 0))
p.free(o);
}
});
}
void
benchmark_boost_pool_create_and_destroy()
{
benchmark(std::move(std::string("boost::pool cr. & destr.")), []() {
for (size_t i = 0; i < N / 100; ++i) {
if (__builtin_expect(!(i & 3), 0)) {
boost::object_pool<Big> p;
for (size_t j = 0; j < 100; ++j) {
Big *o = p.malloc();
touch_obj(o);
}
} else {
boost::object_pool<Small> p;
for (size_t j = 0; j < 100; ++j) {
Small *o = p.malloc();
touch_obj(o);
}
}
}
});
}
#endif
/*
* ------------------------------------------------------------------------
* malloc/free
* ------------------------------------------------------------------------
*/
template<class T>
void
benchmark_mallocfree()
{
benchmark(std::move(T() + "mallocfree"), [&]() {
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T*)malloc(sizeof(T));
ptrs[i] = o;
touch_obj(o);
}
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
free(ptrs[i]);
}
});
}
template<class T>
void
benchmark_mallocfree_free()
{
benchmark(std::move(T() + "mallocfree w/ free"), [&]() {
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T*)malloc(sizeof(T));
ptrs[i] = o;
touch_obj(o);
if (__builtin_expect(!(i & 3), 0)) {
free(o);
ptrs[i] = NULL;
}
}
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
free(ptrs[i]);
}
});
}
/*
* ------------------------------------------------------------------------
* GLib slices
* ------------------------------------------------------------------------
*/
#ifdef USE_GLIB
template<class T>
void
benchmark_glib_slices()
{
benchmark(std::move(T() + "GLib slices"), [&]() {
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T*)g_slice_alloc(sizeof(T));
ptrs[i] = o;
touch_obj(o);
}
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
g_slice_free1(sizeof(T), ptrs[i]);
}
});
}
template<class T>
void
benchmark_glib_slices_free()
{
benchmark(std::move(T() + "GLib slices w/ free"), [&]() {
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T*)g_slice_alloc(sizeof(T));
ptrs[i] = o;
touch_obj(o);
if (__builtin_expect(!(i & 3), 0)) {
g_slice_free1(sizeof(T), o);
ptrs[i] = NULL;
}
}
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
g_slice_free1(sizeof(T), ptrs[i]);
}
});
}
#endif
/*
* ------------------------------------------------------------------------
* Nginx pool
* Simplified version of src/core/ngx_palloc.c from nginx-1.9.5 with
* some code adjustments to be built in this benchmark.
* ------------------------------------------------------------------------
*/
struct ngx_pool_t;
typedef uintptr_t ngx_uint_t;
#define NGX_POOL_ALIGNMENT 16
#define NGX_ALIGNMENT sizeof(unsigned long)
#define ngx_align_ptr(p, a) \
(u_char *)(((uintptr_t)(p) + ((uintptr_t)a - 1)) & ~((uintptr_t)a - 1))
#define NGX_MAX_ALLOC_FROM_POOL ((size_t)PAGE_SIZE - 1)
struct ngx_pool_data_t {
unsigned char *last;
unsigned char *end;
ngx_pool_t *next;
unsigned int failed;
};
struct ngx_pool_large_t {
ngx_pool_large_t *next;
void *alloc;
};
struct ngx_pool_t {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_pool_large_t *large;
};
ngx_pool_t *
ngx_create_pool(size_t size)
{
ngx_pool_t *p;
if (posix_memalign((void **)&p, NGX_POOL_ALIGNMENT, size))
return NULL;
p->d.last = (unsigned char *)p + sizeof(ngx_pool_t);
p->d.end = (unsigned char *)p + size;
p->d.next = NULL;
p->d.failed = 0;
size = size - sizeof(ngx_pool_t);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL)
? size : NGX_MAX_ALLOC_FROM_POOL;
p->current = p;
p->large = NULL;
return p;
}
void
ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
for (l = pool->large; l; l = l->next) {
if (l->alloc)
free(l->alloc);
}
for (p = pool, n = pool->d.next; ; p = n, n = n->d.next) {
free(p);
if (n == NULL)
break;
}
}
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
unsigned char *m;
size_t psize = (size_t)(pool->d.end - (unsigned char *)pool);
ngx_pool_t *p, *p_new;
if (posix_memalign((void **)&m, NGX_POOL_ALIGNMENT, psize))
return NULL;
p_new = (ngx_pool_t *)m;
p_new->d.end = m + psize;
p_new->d.next = NULL;
p_new->d.failed = 0;
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
p_new->d.last = m + size;
for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4)
pool->current = p->d.next;
}
p->d.next = p_new;
return m;
}
void *ngx_palloc(ngx_pool_t *pool, size_t size);
static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
unsigned int n = 0;
ngx_pool_large_t *large;
p = malloc(size);
if (p == NULL)
return NULL;
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
if (n++ > 3)
break;
}
large = (ngx_pool_large_t *)ngx_palloc(pool, sizeof(ngx_pool_large_t));
if (large == NULL) {
free(p);
return NULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
unsigned char *m;
ngx_pool_t *p;
if (size <= pool->max) {
p = pool->current;
do {
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
if ((size_t)(p->d.end - m) >= size) {
p->d.last = m + size;
return m;
}
p = p->d.next;
} while (p);
return ngx_palloc_block(pool, size);
}
return ngx_palloc_large(pool, size);
}
void
ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l;
for (l = pool->large; l; l = l->next) {
if (p == l->alloc) {
free(l->alloc);
l->alloc = NULL;
return;
}
}
}
template<class T>
void
benchmark_ngx_pool()
{
ngx_pool_t *p = ngx_create_pool(PAGE_SIZE);
assert(p);
benchmark(std::move(T() + "ngx_pool"), [=]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T *)ngx_palloc(p, sizeof(T));
touch_obj(o);
}
});
ngx_destroy_pool(p);
}
void
benchmark_ngx_pool_mix_free()
{
ngx_pool_t *p = ngx_create_pool(PAGE_SIZE);
assert(p);
benchmark(std::move(std::string("ngx_pool w/ free (Mix)")), [=]() {
for (size_t i = 0; i < N; ++i) {
if (__builtin_expect(!(i & 0xfff), 0)) {
Huge *o;
o = (Huge *)ngx_palloc(p, sizeof(*o));
touch_obj(o);
if (!(i & 1))
ngx_pfree(p, o);
}
else if (__builtin_expect(!(i & 3), 0)) {
Big *o = (Big *)ngx_palloc(p, sizeof(*o));
touch_obj(o);
if (!(i & 1))
ngx_pfree(p, o);
}
else {
Small *o = (Small *)ngx_palloc(p, sizeof(*o));
touch_obj(o);
if (!(i & 1))
ngx_pfree(p, o);
}
}
});
ngx_destroy_pool(p);
}
void
benchmark_ngx_pool_create_and_destroy()
{
benchmark(std::move(std::string("ngx_pool cr. & destr.")), []() {
for (size_t i = 0; i < N / 100; ++i) {
ngx_pool_t *p = ngx_create_pool(PAGE_SIZE);
for (size_t j = 0; j < 100; ++j) {
if (__builtin_expect(!(i & 3), 0)) {
Big *o;
o = (Big *)ngx_palloc(p, sizeof(*o));
touch_obj(o);
} else {
Small *o;
o = (Small *)ngx_palloc(p, sizeof(*o));
touch_obj(o);
}
}
ngx_destroy_pool(p);
}
});
}
/*
* ------------------------------------------------------------------------
* Tempesta FW pool
* Simplified and ported to user-space from
* https://github.com/natsys/tempesta/blob/master/tempesta_fw/pool.c
* ------------------------------------------------------------------------
*/
/**
* Emulation of Linux buddy allocator.
*/
#define GFP_ATOMIC 0
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define likely(x) __builtin_expect(x, 1)
#define unlikely(x) __builtin_expect(x, 0)
#define free_pages(p, o) free((void *)(p))
#define get_order(n) (assert((n) < PAGE_SIZE * 128), \
(n) < PAGE_SIZE ? 0 \
: (n) < PAGE_SIZE * 2 ? 1 \
: (n) < PAGE_SIZE * 4 ? 2 \
: (n) < PAGE_SIZE * 8 ? 3 \
: (n) < PAGE_SIZE * 16 ? 4 \
: (n) < PAGE_SIZE * 32 ? 5 \
: (n) < PAGE_SIZE * 64 ? 6 \
: 7)
static inline unsigned long
__get_free_pages(int flags __attribute__((unused)), int order)
{
void *p;
if (posix_memalign((void **)&p, PAGE_SIZE, PAGE_SIZE << order))
return 0;
return (unsigned long)p;
}
/**
* Memory pool chunk descriptor.
*
* @next - pointer to next memory chunk;
* @order - order of number of pages in the chunk;
* @off - current chunk offset;
*/
struct TfwPoolChunk {
TfwPoolChunk *next;
unsigned int order;
unsigned int off;
};
/**
* Memory pool descriptor.
*
* @curr - current chunk to allocate memory from;
* @order,@off - cached members of @curr;
*/
typedef struct {
TfwPoolChunk *curr;
unsigned int order;
unsigned int off;
} TfwPool;
#define TFW_POOL_CHUNK_SZ(p) (PAGE_SIZE << (p)->order)
#define TFW_POOL_CHUNK_BASE(c) ((unsigned long)(c) & PAGE_MASK)
#define TFW_POOL_CHUNK_END(p) (TFW_POOL_CHUNK_BASE((p)->curr) + (p)->off)
#define TFW_POOL_ALIGN_SZ(n) (((n) + 7) & ~7UL)
#define TFW_POOL_HEAD_OFF (TFW_POOL_ALIGN_SZ(sizeof(TfwPool)) \
+ TFW_POOL_ALIGN_SZ(sizeof(TfwPoolChunk)))
#define TFW_POOL_PGCACHE_SZ 256
static unsigned int pg_next;
static unsigned long pg_cache[TFW_POOL_PGCACHE_SZ];
static unsigned long
tfw_pool_alloc_pages(unsigned int order)
{
if (likely(pg_next && !order))
return pg_cache[--pg_next];
return __get_free_pages(GFP_ATOMIC, order);
}
static void
tfw_pool_free_pages(unsigned long addr, unsigned int order)
{
if (likely(pg_next < TFW_POOL_PGCACHE_SZ && !order)) {
pg_cache[pg_next++] = addr;
} else {
free_pages(addr, order);
}
}
TfwPool *
__tfw_pool_new(size_t n)
{
TfwPool *p;
TfwPoolChunk *c;
unsigned int order;
order = get_order(TFW_POOL_ALIGN_SZ(n) + TFW_POOL_HEAD_OFF);
c = (TfwPoolChunk *)tfw_pool_alloc_pages(order);
if (unlikely(!c))
return NULL;
p = (TfwPool *)((char *)c + TFW_POOL_ALIGN_SZ(sizeof(*c)));
c->next = NULL;
p->order = order;
p->off = TFW_POOL_HEAD_OFF;
p->curr = c;
return p;
}
void *
tfw_pool_alloc(TfwPool *p, size_t n)
{
void *a;
n = TFW_POOL_ALIGN_SZ(n);
if (unlikely(p->off + n > TFW_POOL_CHUNK_SZ(p))) {
TfwPoolChunk *c, *curr = p->curr;
unsigned int off = TFW_POOL_ALIGN_SZ(sizeof(TfwPoolChunk)) + n;
unsigned int order = get_order(off);
c = (TfwPoolChunk *)tfw_pool_alloc_pages(order);
if (!c)
return NULL;
c->next = curr;
curr->order = p->order;
curr->off = p->off;
p->order = order;
p->off = off;
p->curr = c;
return (void *)TFW_POOL_ALIGN_SZ((unsigned long)(c + 1));
}
a = (char *)TFW_POOL_CHUNK_END(p);
p->off += n;
return a;
}
void
tfw_pool_free(TfwPool *p, void *ptr, size_t n)
{
n = TFW_POOL_ALIGN_SZ(n);
/* Stack-like usage is expected. */
if (unlikely((char *)ptr + n != (char *)TFW_POOL_CHUNK_END(p)))
return;
p->off -= n;
/* Free empty chunk which doesn't contain the pool header. */
if (unlikely(p->off == TFW_POOL_ALIGN_SZ(sizeof(TfwPoolChunk)))) {
TfwPoolChunk *next = p->curr->next;
tfw_pool_free_pages(TFW_POOL_CHUNK_BASE(p->curr), p->order);
p->curr = next;
p->order = next->order;
p->off = next->off;
}
}
void
tfw_pool_destroy(TfwPool *p)
{
TfwPoolChunk *c, *next;
for (c = p->curr; c; c = next) {
next = c->next;
tfw_pool_free_pages(TFW_POOL_CHUNK_BASE(c), c->order);
}
}
template<class T>
void
benchmark_tfw_pool()
{
TfwPool *p = __tfw_pool_new(0);
assert(p);
benchmark(std::move(T() + "tfw_pool"), [=]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T *)tfw_pool_alloc(p, sizeof(T));
touch_obj(o);
}
});
tfw_pool_destroy(p);
}
template<class T>
void
benchmark_tfw_pool_free()
{
TfwPool *p = __tfw_pool_new(0);
assert(p);
benchmark(std::move(T() + "tfw_pool w/ free"), [&]() {
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
T *o = (T *)tfw_pool_alloc(p, sizeof(T));
touch_obj(o);
if (__builtin_expect(!(i & 3), 0))
tfw_pool_free(p, o, sizeof(T));
}
});
tfw_pool_destroy(p);
}
void
benchmark_tfw_pool_mix_free()
{
TfwPool *p = __tfw_pool_new(0);
assert(p);
benchmark(std::move(std::string("tfw_pool w/ free (Mix)")), [=]() {
for (size_t i = 0; i < N; ++i) {
if (__builtin_expect(!(i & 0xfff), 0)) {
Huge *o;
o = (Huge *)tfw_pool_alloc(p, sizeof(*o));
touch_obj(o);
if (!(i & 1))
tfw_pool_free(p, o, sizeof(*o));
}
else if (__builtin_expect(!(i & 3), 0)) {
Big *o = (Big *)tfw_pool_alloc(p, sizeof(*o));
touch_obj(o);
if (!(i & 1))
tfw_pool_free(p, o, sizeof(*o));
}
else {
Small *o;
o = (Small *)tfw_pool_alloc(p, sizeof(*o));
touch_obj(o);
}
}
});
tfw_pool_destroy(p);
}
void
benchmark_tfw_pool_create_and_destroy()
{
benchmark(std::move(std::string("tfw_pool cr. & destr.")), []() {
for (size_t i = 0; i < N / 100; ++i) {
TfwPool *p = __tfw_pool_new(0);
for (size_t j = 0; j < 100; ++j) {
if (__builtin_expect(!(i & 3), 0)) {
Big *o;
o = (Big *)tfw_pool_alloc(p,
sizeof(*o));
touch_obj(o);
} else {
Small *o;
o = (Small *)tfw_pool_alloc(p,
sizeof(*o));
touch_obj(o);
}
}
tfw_pool_destroy(p);
}
});
}
/**************************************************************************/
/* Adding M*LIB mempool */
/**************************************************************************/
#include "m-mempool.h"
MEMPOOL_DEF(mempool_small, Small)
MEMPOOL_DEF(mempool_big, Big)
MEMPOOL_DEF(mempool_huge, Huge)
void
benchmark_mempool_small()
{
mempool_small_t p;
mempool_small_init (p);
mempool_small_t *ptr = &p; // C++ lambda !
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);
}
});
mempool_small_clear(p);
}
void
benchmark_mempool_big()
{
mempool_big_t p;
mempool_big_init (p);
mempool_big_t *ptr = &p; // C++ lambda !
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);
}
});
mempool_big_clear(p);
}
void
benchmark_mempool_mix_free()
{
mempool_small_t s, *ptr_s = &s;
mempool_big_t b, *ptr_b = &b;
mempool_huge_t h, *ptr_h = &h;
mempool_small_init (s);
mempool_big_init (b);
mempool_huge_init (h);
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);
touch_obj(o);
if (!(i & 1))
mempool_huge_free (*ptr_h, o);
}
else if (__builtin_expect(!(i & 3), 0)) {
Big *o = mempool_big_alloc(*ptr_b);
touch_obj(o);
if (!(i & 1))
mempool_big_free (*ptr_b, o);
}
else {
Small *o = mempool_small_alloc (*ptr_s);
touch_obj(o);
if (!(i & 1))
mempool_small_free (*ptr_s, o);
}
}
});
mempool_small_clear (s);
mempool_big_clear (b);
mempool_huge_clear (h);
}
/*
* ------------------------------------------------------------------------
* Main part of the benchmark
* ------------------------------------------------------------------------
*/
int
main()
{
std::cout << std::setw(35) << std::right << "small object size: "
<< sizeof(Small) << std::endl;
std::cout << std::setw(35) << std::right << "big object size: "
<< sizeof(Big) << std::endl;
std::cout << std::setw(35) << std::right << "huge object size: "
<< sizeof(Huge) << "\n" << std::endl;
// Warm up malloc().
static const size_t n = N * sizeof(Big);
void *p = malloc(n);
assert(p);
memset(p, 0, n);
free(p);
benchmark_mallocfree<Small>();
benchmark_mallocfree_free<Small>();
benchmark_mallocfree<Big>();
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();
benchmark_ngx_pool_create_and_destroy();
std::cout << std::endl;
benchmark_mempool_small();
benchmark_mempool_big();
benchmark_mempool_mix_free();
std::cout << std::endl;
benchmark_tfw_pool<Small>();
benchmark_tfw_pool_free<Small>();
benchmark_tfw_pool<Big>();
benchmark_tfw_pool_free<Big>();
benchmark_tfw_pool_mix_free();
benchmark_tfw_pool_create_and_destroy();
return 0;
}
-53
View File
@@ -1,53 +0,0 @@
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
@@ -1,22 +0,0 @@
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
@@ -1,73 +0,0 @@
#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
@@ -1,53 +0,0 @@
#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
@@ -1,33 +0,0 @@
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
@@ -1,8 +0,0 @@
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
@@ -1,34 +0,0 @@
#include "../common.c"
#define M_USE_DICT_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;
}
+31 -1
View File
@@ -81,4 +81,34 @@ V0.6.*: * Rename M_DEFAULT_OPLIST in M_BASIC_OPLIST
so except if you are using the prefixed version of the strings
or the bounded strings API, you won't need to upgrade your code.
V0.7.*: * Rename m-mutex.h header in m-thread.h
V0.7.2: * Rename m-mutex.h header in m-thread.h
V0.7.3 : * DICT_STOREHASH_DEF2 / DICT_STOREHASH_DEF2_AS are deprecated.
Use DICT_DEF2 / DICT_DEF2_AS
V0.8.0 : * Memory interface massive compatibility break to allow more efficient integration of custom allocators.
The prototypes of NEW / DEL REALLOC / FREE operators are incompatible:
* Add the old number of elements of the array as argument of REALLOC
* Add the number of elements of the array and its base type as arguments of FREE.
* Add the context argument (which shall be ignored in not specific build)
(You can ignore the new arguments which are only hints provided to memory functions).
Also impact M_MEMORY_NEW / M_MEMORY_DEL / M_MEMORY_REALLOC & M_MEMORY_FREE
M_MEMORY_FULL: replace the failed size allocation size argument into the base type and the number of elements that failed to be allocated.
* m-buffer:
* non-thread safe queue & stack are moved into their dedicated header (m-queue)
* BUFFER_THREAD_UNSAFE policy is no longer supported.
* Disabling BUFFER_BLOCKING policy is no longer supported (default policy is now forced).
* _push / _pop now returns now void like other containers instead of a bool (it was undocumented)
* Remove MEMPOOL support from list & R/B tree
* m-mempool: Header is obsolete (and no longer useful at all as lists & RB Tree no longer can use it. To remove). Use new M_USE_CONTEXT interface to pass local pool context.
* M*LIB becomes stricter for the INIT_MOVE operator:
The Emulation layer using CLEAR+INIT_SET methods, when there is no INIT_MOVE operator, has been removed:
either default INIT_MOVE operator (operator '=') is working, or a method is provided for INIT_MOVE or it is disabled.
If it is an array of size 1, you can use M_COPY_A1_DEFAULT as a default method.
(It's because supporting emulation layer with new memory context parameter was complicated)
* Remove obsole headers: m-c-mempool, m-mempool, m-shared, m-i-shared, -mconcurrent
V0.8.1 : * m-algo: Update interface of _transform to be more flexible: adding user context pointer, and filtering of the input elements.
* m-algo: Update interface of _reduce to be more user friendly: return value indicates if the reduction is empty or not.
* m-algo: Rename _map_reduce into _transform_reduce and update its interface to be more flexible: adding user context pointer, and filtering of the input elements and return value indicates if the reduction is empty or not.
* Rename _push_raw method by _push_back_raw for all containers (clearer on the semantic)
+372 -39
View File
@@ -1,17 +1,325 @@
*Long term issues*
------------------
==========================================================
* DONE #34 : Pass old size arguments to memory allocator :ENHANCEMENT:
** State
Some custom allocators don't store the old size of an object to maximize memory usage
used by the allocated objects. On realloc, they need the old size argument.
This is inline with the GMP allocator which provides the old size argument.
There is no difficulty in the data structure to compute the old size of the object
(it is quite easily available).
It is needed for REALLOC operator (and FREE)
** Solution #1
Add the oldsize argument to the REALLOC and FREE operators.
** Solution #2
Create a local variable m_local_old_size before calling the REALLOC or FREE operators
Modify GAIA interface to link OLDSIZE patttern to m_local_old_size
Need to handle the warnings correctly.
** Tradeof analysis
Solution #1 is simpler but breaks API. However custom allocators are not common.
Solution #2 keeps API as it is but increases code complexity by adding more hacks into the codebase.
* TODO #33 : Handling of partially constructed object :ANOMALY:
** State
Support of partially constructed object needs special code to handle it
correctly (register a correct cleanup function in case of exception).
Current containers don't support such features.
> NOTE:
An exception for init_set supposes that the object is *not* initialized.
An exception for set supposes that the object *remains* initialized.
Some containers perform an init_set using init+set
others perform a set using a clear+init_set
The property of init_set (resp. set) is broken if an exception is raised in the set (resp. init_set):
the object is not let in the expected uninitialized (resp. initialized) state.
** Actions
Add such support in M*LIB containers.
Need to analyse each function for execption safety.
It can be difficult. For example, for array _init_set
function due to the copy of the object one per one. Each copied sub object
may throw an exception, so we always need to have the container in a correct
state so that it can be cleanup.
* DONE #32 : User specialization fields in the container :ENHANCEMENT:
** State
Some methods of a contained object may need to have some options that
are neither constant nor global, but are specialized in function of the container.
For example, a memory handler may need a parameter which is needed to identify
which pool of memory has to be used.
** Plan:
Add a new operator "CONTEXT" which is a type to be added in the
defined container. Then it can be accessed by the operator through an extension
of the GAIA by adding support for identification of specialization like this:
( CONTEXT( memory_pool_ref ),
NEW( API(NONE, memory_pool_alloc, CONTEXT, ARG1)),
DEL( API(NONE, memory_pool_free, CONTEXT, ARG1)) )
** Open points:
- How to get access to the specialization field of the container?
==> Define the "self" name as the name of the container for all methods?
- What to do when there are several containers for a method?
==> self shall be the destination?
- How to initialize the specialization field of the container? provide custom initialization functions? It seems to be the hard point.
Use GAIA interface and a local variable with force naming : m_local_context for example.
** Proposed Solution:
New operator USER_CONTEXT created. If operator USER_CONTEXT exist:
+ add a USER_CONTEXT user_data in the data structure.
+ for INIT & INIT_WITH, add a new parameter to the function to pass the context to initialize
+ for INIT_SET & INIT_MOVE & MOVE, initialize it with the source object (FIXME: This may not be what we want for INIT_SET if we want to copy an object from an allocator to another)
+ for each functions that use an operator that may use the USER_CONTEXT, add a local variable m_local_context and initialize to the user context
==> Limit user context to pointer or integer (Good enough)
+ Expand GAIA interface to handle specially the term USER_CONTEXT: if it exists, expands it to m_local_context. The user will therefore be able to expand its operator with the user provided local context.
+ Update OPLIST of data structure to change the call to INIT & INIT_WITH to use GAIA/USER_CONTEXT if the basic type has a USER_CONTEXT
+ To be checked: EMPLACE_TYPE ?
+ Create M_USING_CONTEXT to initialize a local variable m_local_context and initalize it with the user context to be used y the M_LET macro:
M_USING_CONTEXT(int *, &y)
M_LET(x, object_t) {
}
Done in branch feature/user-data-v2 with a full working implementation for array
** Solution limitation:
INIT & INIT_WITH interface change for the user. This however is an acceptable limitation.
Adding a user context to each data structure will increase a lot the size of the data structure in case of recursive data structures: For example, an array of string_t. Each string_t will need a user data context for it to work properly, so we'll need N*sizeof(user data context) memory just to make it work. This becomes even more problematic if we chain more data structures.
And in general, we use custom allocators to be faster and use less memory: this will defeat the purpose of custom allocators a lot.
Another limitation is that string_t / bitset_t are not supported correctly. We can make this change globally but it will prevent mixing different codebase that uses the string_t together (one expects a user context, the other not).
** Alternative #1
Passing user context arguments to each function call seems to be a little bit better.
It might however reduces code optimization as one more register will be user everywhere in the calling chain, which may huge.
** Alternative #2
The custom allocator could have a thread default allocator as a global variable (thread attribute). It seems to be a better solution, more scalable.
The custom allocator will need to be able to switch quickly between scrach arena and permanent arena. This seems to be easy.
However switching allocators puts a burden to the user:
the user needs to properly be sure to call the destructor with the custom allocator set to be the same as when it was created.
This might not even be possible in case of exceptions.
* TODO #31 : Uniformize parametrization options of containers :ENHANCEMENT:
** State
Currently in order to pass some parametrization of the containers,
the parameters are passed to the generation macros through:
- extra parameters of the macros,
- special operators of the contained object oplist.
The extra parameters of some generation macros break the uniformity
of the interface.
It is conceptually wrong to put some special operators of the contained object
that have only meanings for the container object.
In case of adding new options, it break the existing interface or put another
operator in the contained object oplist.
The following parameters are used:
- Fixed size of a queue (buffer / B+Tree),
- Policy of a queue (buffer),
- Type of the container, IT type of the container, Ref type of the iterator
The following operators are used:
- NEW / DEL / REALLOC / FREE
- MEMPOOL / MEMPOOL_LINKAGE
The following new options is planned:
- Interface (See #28)
- Extra parameter for memory allocation function
** Plan
The M\_*\_DEF\_AS macros shall be changed like this:
M_*_DEF_AS(name, cont_oplist, type[, type_oplist])
with cont\_oplist being the container oplist that gives parametrization to the container definition.
The following operators are planned:
- TYPE: to define the type of the container,
- IT\_TYPE: to define the IT type of the container,
- SUBTYPE: to define the type pointed by the iterator,
- FIXED\_SIZE: to define the fixed size of a container,
- POLICY: to define the policy of a container,
- NEW / DEL / REALLOC / FREE: to define the memory interface,
- MEMPOOL / MEMPOOL_LINKAGE: to request a definition of a specialized memory interface,
- INTERFACE: to request a pure header definition, an implementation or both (see #28),
- CONTEXT: to request an extra context parameter to add in the container.
All operators should be optional.
The NEW / DEL / REALLOC / FREE / MEMPOOL / MEMPOOL_LINKAGE won't have any effects anymore if defined in the contained object oplist.
** Pro:
- Uniformize interface
** Con:
- API breakage. It can be mitigated by providing an adaptating layer by prividing the new interface for M\_*\_DEF\_AS only
and *\_DEF\_AS will provide a translation layer.
* TODO #30 : Serialization rework :ENHANCEMENT:
** State
Currently, each container supports 3 serialization methods:
- old format to FILE
- old format to string
- generic serialization
Generic serialiation connect the container format to the serialization object constraints.
It is done through a vtable. As such there is a performance penality and it avoids proper inlining.
It is however quite flexible and decouple the data structure from the serialization format.
** Evolution
Old format should be deprecated and the functions implemented it shall use the generic serialization interface.
Serialization object shall provide a special OPLIST of serialization.
For example M-CORE will provide OLD Format oplist and M-SERIAL-JSON will provide JSON format oplist.
Each oplist will provide the suffix needed for the serialization, and the interface
(see already existing interface).
Then a container will generate specialized serialization methods for each provided oplist.
** Pros:
- Faster
- In the M*LIB philosphy: much like other oplist usage.
** Cons:
- compatibility breakage.
- increase code complexity.
** Open point:
- how can a user add a new serialization object? ==> See solution implemented by M-GENERIC.
- Can we make a generic serialization object to support migration path? Seems possible.
** Example
serial-json provides
#define M_SERIAL_1() (NAME(_json), READ_INT(...), WRITE_INT(...) )
containers provides
For i in 1 100 ; do
if do exist M_SERIAL_ ## i ()
expand(SERIAL_METHOD with M_SERIAL_1())
SERIAL_METHOD(name, ....)
static inline m_serial_return_code_t \
M_C(M_F(name, _out_serial),M_GET_NAME(serial_op) )(M_GET_TYPE(serial_op) f, const list_t list) \
* DONE #29 : Support of partial initialized object :ANOMALY:
** State
In case of exception during the construction of an object,
if the object is partially constructed, there is no destructor to call.
Therefore if there is some partially constructed object
(with some fields already fully constructed), there will be some links.
This is the same problem with C++
** Proposition
Provide new macros to perform the partial initialization before doing the remaining taf.
Something like:
M_CHAIN_INIT( init_code, clear_code)
or
x is { string_t s; string_t d; int num; }
void struct_init_set(struct_t x, const struct_t y)
{
// First thing is Chained Initialization
M_CHAIN_INIT( string_init_set(x->s, y->s), string_clear(x->s) );
M_CHAIN_INIT( string_init_set(x->d, y->d), string_clear(x->d) );
// End of chain init
x->num = y->num;
}
M_CHAIN_INIT will push the clean code pretty much like M_LET.
Everything else is done in M_LET: when the constructor fully returns,
the index of the clean code to call is reset to its value before calling
the constructor. It means that the partially initialized code will be removed
from this stack before the clean method of the structure itself is pushed.
If an exception is raised after a M_CHAIN_INIT, only the clean methods
of the M_CHAIN_INIT are called.
* TODO #28 : Separate generation of interface to implementation :ENHANCEMENT:
** State
Enable support for generating an interface only for the headers
and an implementation only for the source code.
** Analysis
Try to keep API compatibility
==> Only modify renamed macro with M_ prefix by giving a new mandatory
argument for such generation.
This argument is a generation parametric argument.
oplist is a parameter that define the property of the contained type.
It could be used to pass this information but it will be an abuse of interface.
Another argument is needed.
Used arguments to parameter generation not related to the contained type:
- memory interface (NEW / DEL / ...)
- queue policy
- fixed size for queue
- fixed node size for B+Tree
- generate static inline, public (extern) or private (define) functions.
- name of the type.
- name of the iterator
- name of the iterated element.
- ...
Use another oplist as argument for M_.._DEF_AS (See #31)
M_BUFFER_DEF_AS(name, (TYPE(toto), FIXED_SIZE(10), POLICY(STACK), INTERFACE(header)), int, M_BASIC_OPLIST)
* TODO #27 : Uniformization of m-buffer interface :ENHANCEMENT:
** State
The m-buffer interface requires special argument to work:
- size & policy for BUFFER,
- policy only for the others.
@@ -19,10 +327,12 @@ This is not uniform with the others generation macros and not uniform with each
Moreover the others are the only one which can be used in a shared memory, so
a fixed size will be good to have too for such interface.
==> Uniformize the interface. How?
==> Provide static size for all QUEUE.
** Plan
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
- Uniformization of the interface (See #31)
- Provide static size for all QUEUE.
* DONE #26 : Hash-Map for huge objects :ENHANCEMENT:
Specialize Hashmap for huge objects on supported hardware:
@@ -55,18 +365,27 @@ unsigned get_small_hash(int64_t x) {
}
#+END_SRC
Can also (should?) use SIMD to test for several hash entries at the same times
In which case a complete new implementation will be needed
Note: SIMD doesn't seem to be a win if not handled properly.
Since the first guess of a good hashtable shall give the right entry > 90% of the time.
So 90% of the times SIMD will pay the cost of reading not needed memory.
It might still be a win, but proper tuning needs to be done.
* TODO #25 : Support of error return model for error handling. :ENHANCEMENT:
** State
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".
** Analysis
Any service that returns void shall return a "int" (or another type).
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.
Services returning already something shall not be modified but returns the error code embedded (like a NULL pointer).
This model should be applied at the container level only and not globally.
Different containers may need different levels of error handling.
@@ -118,26 +437,30 @@ RETCODE/RETCODE
If really needed, the macro can be avoided and code can be hand written.
Open points:
** Open points:
- How to handle warnings on unused labels?
- What about M_LET/ M_EACH?
- What about M_LET / M_EACH? Maybe only supports those.
* TODO #24 : New MIN-MAX-HEAP container :ENHANCEMENT:
* TODO #24 : New: Add MIN-MAX-HEAP container :ENHANCEMENT:
** State
See https://en.wikipedia.org/wiki/Min-max_heap
as DPRIORITY_QUEUE_DEF ?
** Analysis
NOTE: Needs for such container?
On hold until a user needs it.
* DONE #23 : Strict MOVE semantic to clarify :ENHANCEMENT:
* TODO #23: Strict MOVE semantic to clarify :ENHANCEMENT:
** State
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
** ARRAY container constraint
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
@@ -146,7 +469,7 @@ 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:
** MOVE in OPLIST
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
@@ -160,12 +483,11 @@ For example for tuple, it shall
** DO_INIT_MOVE operator
DO_INIT_MOVE macro is not also fully working for structure
DO_INIT_MOVE macro is 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).
operators as it uses MOVE_DEFAULT which is not compatible.
==> Analyse limitation and possible constraint usages.
Being able to define a correct default for INIT_MOVE will be really good
(compatible with trivial move copy).
In such case, all INIT_MOVE & MOVE operators can be removed from oplist
@@ -176,13 +498,14 @@ If the type is typedef T t[1] then passing such an argument to a function,
will transform the argument to T*, and the type of the argument doesn't match
what is expecting resulting in a move of the pointers, not a move of the design data.
Defining a TYPE to this macro seems mandatory, but within restricting to C11
(by using _Generic to detect such case),
I don't see any way to define the macro.
Defining this type seems possible with C11 _Generic and a TYPE in the oplist,
but without C11 _Generic I don't see any way to define such macro
and we still need to target C99 for such basic feature.
Without a way to write such a macro, the ticket seems pretty much a dead end.
* TODO #20: New: Bucket priority queue :ENHANCEMENT:
* TODO #20 : New: Bucket priority queue :ENHANCEMENT:
** State
Add a new kind of priority queue.
See https://en.wikipedia.org/wiki/Bucket_queue
@@ -197,22 +520,33 @@ 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.
** Analysis
NOTE: Needs for such container?
On hold until a user needs it.
* TODO #19: New: Intrusive Red Black Tree :ENHANCEMENT:
* CANC #19 : New: Intrusive Red Black Tree :ENHANCEMENT:
** State
Add intrusive red black tree.
Look also for AVL tree (NOTE: Is there a performance difference between the two?)
** Analysis
Only needed for unmovable objects for which B+Tree cannot do the job.
But standard Read/Black Tree will do the job just fine.
There is really no need for it.
==> Cancelled
* TODO #18: Missing methods :ENHANCEMENT:
* TODO #18 : Missing methods :ENHANCEMENT:
** State
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
** Analysis
Analyzed each missing methods and fill in the gap
* TODO #17: New: Ressource handler :ENHANCEMENT:
* TODO #17 : New: Ressource handler :ENHANCEMENT:
** State
A global '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
@@ -238,11 +572,11 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
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.
** Analysis
- use of variant: Pro : easy. Con: 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: Con: more work, API more complex. Pro: Memory usage seems better.
* TODO #16: New: Lock Free List :ENHANCEMENT:
* TODO #16 : New: 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.
@@ -265,8 +599,9 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
- needs to be logically deleted : needs a previous field
(NULL if not logically deleted) ? TBC
NOTE: m-c-mempool doesn't seem to be fully robust. random failure of the test cases appear (more notably with Visual C++, but it is still quite rare).
* TODO #14: Memory allocation enhancement :ENHANCEMENT:
* DONE #14 : Memory allocation enhancement :ENHANCEMENT:
Enhancement of the memory allocation scheme to find way to deal properly with advanced allocators:
@@ -300,9 +635,10 @@ Needs some formal operator in the oplist to support it fully and an example.
It is a kind of object inheritance where the container inherits some extra data from its base.
Duplicate with #32 which is more generic ==> Closed
* TODO #12: New: Atomic shared pointer :ENHANCEMENT:
* TODO #12 : New: Atomic shared pointer :ENHANCEMENT:
** State
Add an extension to the SHARED_PTR API:
- New type atomic_shared_ptr
@@ -375,10 +711,8 @@ Other alternative solution is to use the bit 0 to mark the pointer as being upda
Other implementation seems to have it hard to be lock-free: cf. https://github.com/llvm-mirror/libcxx/commit/5fec82dc0db3623546038e4a86baa44f749e554f
* TODO #5: New: Concurrent dictionary Container :ENHANCEMENT:
* TODO #5 : New: Concurrent dictionary Container :ENHANCEMENT:
** State
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
@@ -423,7 +757,6 @@ It needs before lock-free list: http://www.cse.yorku.ca/~ruppert/papers/lfll.pdf
Atomic update is done by allocating a new node, update it, and atomicaly store it in the dict, putting the old one as logically deleted and to reclaim later.
(Other threads may still read its data so we cannot free immediatly).
* DONE #22: Enhanced services for SPSC Queue :ENHANCEMENT:
Add services:
+1
View File
@@ -6,6 +6,7 @@ param2=""
while test $# -gt 0 ; do
case $1 in
-c) param2="$param2 -c" ;;
-o) param2="$param2 -o $2" ; shift ;;
*\.c) param1="$param1 $1" ;;
*) param1="$param1 $1" ; param2="$param2 $1" ;;
esac
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 106 KiB

+18 -5
View File
@@ -21,7 +21,7 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 -g
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 -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -g
GMP_DIR=
MPFR_DIR=$(GMP_DIR)
CPPFLAGS=-I..
@@ -31,20 +31,33 @@ RM=rm -f
.SUFFIXES: .c .exe
.c.exe:
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ `cat config` $(LDFLAGS)
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ `cat config` `cat config.curl` $(LDFLAGS)
.c.o:
@$(MAKE) config
$(CC) $(CFLAGS) $(CPPFLAGS) -O2 `cat config.link` -DNDEBUG -g $< -c -o $@
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
$(MAKE) exm.exe exn.exe
exm.exe: exm-lib.o exm-main.o
$(CC) `cat config.link` -g $^ -o $@
exn.exe: exn-lib.o exn-main.o
$(CC) `cat config.link` -g $^ -o $@
config:
printf "#include <gmp.h>\nint main(void){mpz_t z; mpz_init(z); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) -I$(GMP_DIR)/include -L$(GMP_DIR)/lib config-test.c -lgmp -o config-test.exe $(LDFLAGS) 2> /dev/null && echo "-DHAVE_GMP=1 -DHAVE_MPFR=0 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp" > config || echo "-DHAVE_GMP=0 -DHAVE_MPFR=0 " > config
printf "#include <mpfr.h>\nint main(void){mpfr_t z; mpfr_init(z); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) -I$(GMP_DIR)/include -I$(MPFR_DIR)/include -L$(GMP_DIR)/lib -L$(MPFR_DIR)/lib config-test.c -lmpfr -lgmp -o config-test.exe $(LDFLAGS) 2> /dev/null && echo "-DHAVE_GMP=1 -DHAVE_MPFR=1 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -I$(MPFR_DIR)/include -L$(MPFR_DIR)/lib -lmpfr -lgmp" > config || echo "MPFR not available"
printf "#include <curl/curl.h>\nint main(void){curl_global_init(CURL_GLOBAL_DEFAULT);}" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) config-test.c `curl-config --cflags --libs` -o config-test.exe 2> /dev/null && echo "-DHAVE_CURL=1 `curl-config --cflags --libs`" > config.curl || echo "-DHAVE_CURL=0 " > config.curl
printf "#include <stdio.h>\nint main(void){printf(\"Ok\"); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) config-test.c -ffunction-sections -fdata-sections -Wl,--gc-sections 2> /dev/null && echo "-ffunction-sections -fdata-sections -Wl,--gc-sections" > config.link || echo "" > config.link
$(RM) config-test.c config-test.exe
clean:
$(RM) *.exe *.s *~ *.o ./a.dat config
$(RM) *.exe *.s *~ *.o ./a.dat config config.link config.curl
+7 -7
View File
@@ -4,16 +4,16 @@
/* Define a dynamic array of int */
ARRAY_DEF(vector_int, int)
/* Register the oplist of this dynamic array globally */
/* Register the oplist of this dynamic array globally (using implicitely M_BASIC_OPLIST) */
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
/* Define some standards algorithms on this dynamic array */
/* Define some standards algorithms on this dynamic array. We can reuse the prefix of the dynamic array */
ALGO_DEF(vector_int, vector_int_t)
/* Define a dynamic array of bool */
ARRAY_DEF(vector_bool, bool)
/* Register the oplist of this dynamic array globally */
#define M_OPL_vector_bool_t() ARRAY_OPLIST(vector_bool, M_BOOL_OPLIST)
/* Define some standards algorithms on this dynamic array */
/* Define some standards algorithms on this dynamic array. We can reuse the prefix of the dynamic array */
ALGO_DEF(vector_bool, vector_bool_t)
/* ID function */
@@ -41,10 +41,10 @@ int main(void)
#else
for M_EACH(it, myvector, vector_int_t) {
printf(" %d", *it);
}
}
#endif
printf("\n");
}
} /* End of M_LET: myvector & tmp are automaticly cleared */
// Same thing but with booleans
M_LET(myvector, tmp, vector_bool_t) {
@@ -56,13 +56,13 @@ int main(void)
printf("myvector contains:");
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) );
}
} /* End of M_LET: myvector & tmp are automaticly cleared */
return 0;
}
+5 -6
View File
@@ -14,7 +14,7 @@ ARRAY_DEF(vector_int, int)
/* Register the oplist of this array of integer globally */
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
/* Micro macro to detect if a string starts with the given pattern */
/* Micro macro to detect if the key string starts with the given pattern */
#define start_with(pattern, item) \
string_start_with_str_p((item).key, (pattern))
@@ -26,11 +26,10 @@ int main(void)
int s = 0;
// Init keys as a vector_int_t, initialize it (and prepare its destruction)
M_LET(keys, vector_int_t)
// Initialize m as a dictionnary and initialize it with the given database
// Initialize tmp as a dictionanry
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' */
// Initialize m as a dictionnary and initialize it with the given database
// Initialize tmp as a dictionnary
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' and fills tmp with it */
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);
+2 -2
View File
@@ -14,8 +14,8 @@ int main(void)
{
// Let 's' be a string_t and initialize it.
M_LET(s, string_t)
// Let 'v' be a vector of integer and initialize it.
M_LET( (v, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), vector_int_t) {
// Let 'v' be a vector of integer and initialize it.
M_LET( (v, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), vector_int_t) {
int sum = 0, product = 0;
// Compute the sum of theses integers.
+109
View File
@@ -0,0 +1,109 @@
#include <stdio.h>
#include <stdlib.h>
////////////////////////////////////////////////////////////
static size_t allocated_size = 0;
static void* my_alloc(size_t s)
{
printf("Alloc called s=%lu!\n", s);
allocated_size += s;
return malloc(s);
}
static void my_free(void *ptr, size_t s)
{
printf("Free called s=%lu!\n", s);
allocated_size -= s;
free(ptr);
}
////////////////////////////////////////////////////////////
static void *last_alloc;
static void* my_array_alloc(size_t s, void *org, size_t n)
{
printf("Array Alloc called org=%p of n=%zu elements of size s=%lu!\n", org, n, s);
last_alloc = realloc(org, s*n);
return last_alloc;
}
static void my_array_free(void *ptr)
{
printf("Array Free called ptr=%p last_alloc=%p!\n", ptr, last_alloc);
if (ptr != last_alloc) {
printf("Somthing went wrong.\n");
exit(4);
}
free(ptr);
}
////////////////////////////////////////////////////////////
// Allocate for single object
# define M_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type))
# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr)
// Allocate for array of objects
# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n)
# define M_MEMORY_FREE(ctx, type, ptr, n) my_array_free(ptr)
#include "m-list.h"
#include "m-array.h"
////////////////////////////////////////////////////////////
LIST_DEF(list_int, int)
static void test_list(void)
{
printf("List demo\n");
list_int_t list;
list_int_init(list);
list_int_push_back(list, 34);
list_int_push_back(list, 34);
if (allocated_size == 0) {
printf("Nothing was allocated... Very strange...\n");
exit(1);
}
list_int_clear(list);
if (allocated_size != 0) {
printf("Nothing was freed (%lu)... Very strange...\n", allocated_size);
exit(2);
}
printf("Global custom allocator for list works!\n");
}
////////////////////////////////////////////////////////////
ARRAY_DEF(array_int, int)
static void test_array(void)
{
printf("Array demo\n");
array_int_t array;
array_int_init(array);
array_int_push_back(array, 34);
array_int_push_back(array, 34);
array_int_clear(array);
printf("Global custom allocator for array works!\n");
}
////////////////////////////////////////////////////////////
int main(void)
{
test_list();
test_array();
exit(0);
}
+109
View File
@@ -0,0 +1,109 @@
#include <stdio.h>
#include <stdlib.h>
#include "m-list.h"
#include "m-array.h"
////////////////////////////////////////////////////////////
static size_t allocated_size = 0;
static void* my_alloc(size_t s)
{
printf("Alloc called s=%lu!\n", s);
allocated_size += s;
return malloc(s);
}
static void my_free(void *ptr, size_t s)
{
printf("Free called s=%lu!\n", s);
allocated_size -= s;
free(ptr);
}
// Allocate for single object
# define MY_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type))
# define MY_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr)
////////////////////////////////////////////////////////////
// Override only for this container the memory allocation function
LIST_DEF(list_int, int, M_OPEXTEND(M_BASIC_OPLIST, NEW(MY_MEMORY_ALLOC), DEL(MY_MEMORY_DEL)))
static void test_list(void)
{
printf("List demo\n");
list_int_t list;
list_int_init(list);
list_int_push_back(list, 34);
list_int_push_back(list, 34);
if (allocated_size == 0) {
printf("Nothing was allocated... Very strange...\n");
exit(1);
}
list_int_clear(list);
if (allocated_size != 0) {
printf("Nothing was freed (%lu)... Very strange...\n", allocated_size);
exit(2);
}
printf("Global custom allocator for list works!\n");
}
////////////////////////////////////////////////////////////
static void *last_alloc;
static void* my_array_alloc(size_t s, void *org, size_t n)
{
printf("Array Alloc called org=%p of n=%zu elements of size s=%lu!\n", org, n, s);
last_alloc = realloc(org, s*n);
return last_alloc;
}
static void my_array_free(void *ptr)
{
printf("Array Free called ptr=%p last_alloc=%p!\n", ptr, last_alloc);
if (ptr != last_alloc) {
printf("Somthing went wrong.\n");
exit(4);
}
free(ptr);
}
// Allocate for array of objects
# define MY_MEMORY_REALLOC(ctx, type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n)
# define MY_MEMORY_FREE(ctx, type, ptr, n) my_array_free(ptr)
////////////////////////////////////////////////////////////
// Override only for this container the memory allocation function
ARRAY_DEF(array_int, int, M_OPEXTEND(M_BASIC_OPLIST, REALLOC(MY_MEMORY_REALLOC), FREE(MY_MEMORY_FREE)))
static void test_array(void)
{
printf("Array demo\n");
array_int_t array;
array_int_init(array);
array_int_push_back(array, 34);
array_int_push_back(array, 34);
array_int_clear(array);
printf("Global custom allocator for array works!\n");
}
////////////////////////////////////////////////////////////
int main(void)
{
test_list();
test_array();
exit(0);
}
+4 -2
View File
@@ -13,8 +13,10 @@ 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){
mpz_add(*d, *d, a);
}
static inline void my_mpz_sqr(mpz_t *d, const mpz_t a){
static inline bool my_mpz_sqr(mpz_t *d, const mpz_t a, void *data){
(void) data;
mpz_mul(*d, a, a);
return true;
}
int main(void)
@@ -31,7 +33,7 @@ int main(void)
}
/* Compute z = sum (a[i]^2) */
array_mpz_map_reduce (&z, a, my_mpz_inc, my_mpz_sqr);
array_mpz_transform_reduce (&z, a, my_mpz_inc, my_mpz_sqr, NULL);
gmp_printf ("Z=%Zd\n", z);
array_mpz_clear(a);
+4 -4
View File
@@ -61,11 +61,11 @@ static void astar(array_point_t path, const string_t maze, int width)
for (size_t i = 0; i < 8; i++) {
point_emplace(next, current->x + deltas[i].x, current->y + deltas[i].y, 0);
if (string_get_char(maze, point_index(next, width)) != '#') {
int *new_cost = cost_point_get(costs, current);
int new_cost = *cost_point_get(costs, current);
int *cost = cost_point_get(costs, next);
if (cost == NULL || *new_cost < *cost) {
cost_point_set_at(costs, next, *new_cost);
next->priority = *new_cost + abs(goal->x - next->x) + abs(goal->y - next->y);
if (cost == NULL || new_cost < *cost) {
cost_point_set_at(costs, next, new_cost);
next->priority = new_cost + abs(goal->x - next->x) + abs(goal->y - next->y);
prio_point_push(front, next);
step_point_set_at(from, next, current);
}
+4 -2
View File
@@ -59,10 +59,12 @@ int main(void)
}
// Print
printf("\nElisabeth has id = %d\n", *map_employee_name_get(Employees_r, STRING_CTE("Elisabeth")));
printf("\nElisabeth has id = %d\n", *map_employee_name_get(Employees_r, STRING_CTE("Elisabeth")));
// Print using emplace (we give a const char [] isntead of a const string_t, avoiding macro STRING_CTE)
printf("\nElisabeth has id = %d\n", *map_employee_name_get_emplace(Employees_r, "Elisabeth"));
} /* Employees_r is cleared beyond this point */
// But employee is not
// But employee is not since we don't use M_LET
map_employee_id_clear(Employees);
return 0;
}
+55
View File
@@ -0,0 +1,55 @@
#include <stdlib.h>
#include "m-bptree.h"
BPTREE_MULTI_DEF2(multimap, 5, int, M_BASIC_OPLIST, int, M_BASIC_OPLIST)
#define M_OPL_map_multimap_t() BPTREE_OPLIST2(multimap, M_BASIC_OPLIST, M_BASIC_OPLIST)
// Fill in with multiple value
static void
fill_in(multimap_t map)
{
for(int i = 1 ; i < 100; i++)
multimap_set_at(map, i, i);
for(int i = 1 ; i < 100; i+=2)
multimap_set_at(map, i, i*i);
for(int i = 1 ; i < 100; i+=3)
multimap_set_at(map, i, i*i*i);
}
// Count the number of occurence of a key
static int
count(multimap_t map, int key)
{
int n = 0;
multimap_it_t it;
// it_from is O(n*ln n) complexity
// it_while_p and next are O(1) complexity
for(multimap_it_from(it, map, key); multimap_it_while_p(it, key); multimap_next(it))
n++;
return n;
}
// Print the map in details
static void
print(multimap_t map)
{
for(int i = 0; i < 101;i++) {
printf("Key=%d Count=%d ", i, count(map, i));
multimap_it_t it;
for(multimap_it_from(it, map, i); multimap_it_while_p(it, i); multimap_next(it)) {
printf("Value=%d ", *multimap_ref(it)->value_ptr );
}
printf("\n");
}
}
int main(void)
{
multimap_t map;
multimap_init(map);
fill_in(map);
print(map);
multimap_clear(map);
exit(0);
}
+7 -8
View File
@@ -1,7 +1,7 @@
#include <stdio.h>
#include "m-buffer.h"
#include "m-shared.h"
#include "m-shared-ptr.h"
#include "m-thread.h"
/* Global variable to stop scheduling */
@@ -99,8 +99,8 @@ SHARED_PTR_DEF(shared_bigdata, 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
BUFFER_DEF(buffer_bigdata, shared_bigdata_t, MY_QUEUE_SIZE,
BUFFER_QUEUE|BUFFER_PUSH_INIT_POP_MOVE, SHARED_PTR_OPLIST(shared_bigdata))
BUFFER_DEF(buffer_bigdata, shared_bigdata_t *, MY_QUEUE_SIZE,
BUFFER_QUEUE|BUFFER_PUSH_INIT_POP_MOVE, SHARED_PTR_OPLIST(shared_bigdata, M_OPL_bigdata_t()))
/* Let's build a Thread synchros tree:
*
@@ -134,8 +134,7 @@ static void thread1 (void *arg)
{
(void) arg;
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
shared_bigdata_init_new (ptr);
shared_bigdata_t *ptr = shared_bigdata_new ();
perform_acquisition(*shared_bigdata_ref(ptr));
buffer_bigdata_push(buf_t1tot2, ptr);
buffer_bigdata_push(buf_t1tot3, ptr);
@@ -146,7 +145,7 @@ static void thread2 (void *arg)
{
(void) arg;
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
shared_bigdata_t *ptr;
buffer_bigdata_pop(&ptr, buf_t1tot2); // NOTE: Pop has been configured to init ptr
perform_computation2(*shared_bigdata_ref(ptr));
shared_bigdata_clear(ptr);
@@ -156,7 +155,7 @@ static void thread3 (void *arg)
{
(void) arg;
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
shared_bigdata_t *ptr;
buffer_bigdata_pop(&ptr, buf_t1tot3); // NOTE: Pop has been configured to init ptr
perform_computation3(*shared_bigdata_ref(ptr));
buffer_bigdata_push(buf_t3tot4, ptr);
@@ -167,7 +166,7 @@ static void thread4 (void *arg)
{
(void) arg;
while (atomic_load(&continue_threading_g)) {
shared_bigdata_t ptr;
shared_bigdata_t *ptr;
buffer_bigdata_pop(&ptr, buf_t3tot4); // NOTE: Pop has been configured to init ptr
perform_computation4(*shared_bigdata_ref(ptr));
shared_bigdata_clear(ptr);
+160
View File
@@ -0,0 +1,160 @@
#if HAVE_CURL
#include <curl/curl.h>
#include "m-dict.h"
#include "m-tuple.h"
#include "m-string.h"
#include "m-list.h"
#include "m-serial-json.h"
/////////////////////////////////////////
// Define container types
// First a generic map string to string
DICT_DEF2(map_str, string_t, string_t)
#define M_OPL_map_str_t() DICT_OPLIST(map_str, STRING_OPLIST, STRING_OPLIST)
// This structure matches the JSON structure returns by the used WEB server
// for the GET command.
// It can be whatever you want provided it matches what is expected by the WEB server
TUPLE_DEF2(web_data,
(args, map_str_t),
(headers, map_str_t),
(origin, string_t),
(url, string_t))
#define M_OPL_web_data_t() TUPLE_OPLIST(web_data, M_OPL_map_str_t(), M_OPL_map_str_t(), STRING_T, STRING_T)
/////////////////////////////////////////
static size_t
write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
(void) size; // Parameter not used
char *src = buffer;
string_ptr str = userp;
for(size_t i = 0; i < nmemb; i++) {
string_push_back(str, src[i]);
}
return nmemb;
}
static bool get_data(web_data_t data, const char url[])
{
m_serial_return_code_t r = M_SERIAL_FAIL;
CURLcode res;
string_t str;
CURL *handle = curl_easy_init();
if (handle) {
string_init(str);
// Perform an http GET request and fill in str with the JSON answer
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, str);
res = curl_easy_perform(handle);
curl_easy_cleanup(handle);
if (res == CURLE_OK) {
// Parse the string and fill in the object
m_serial_str_json_read_t serial;
m_serial_str_json_read_init(serial, string_get_cstr(str));
r = web_data_in_serial(data, serial);
m_serial_str_json_read_clear(serial);
}
string_clear(str);
}
return r == M_SERIAL_OK_DONE;
}
/////////////////////////////////////////
static size_t
ignore_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
(void) buffer; // Parameter not used
(void) size; // Parameter not used
(void) userp; // Parameter not used
return nmemb;
}
static bool post_data(web_data_t data, const char url[], bool display_answer)
{
m_serial_return_code_t r = M_SERIAL_FAIL;
CURLcode res = 0;
string_t str;
CURL *handle = curl_easy_init();
if (handle) {
string_init(str);
// Get a JSON string
m_serial_str_json_write_t serial;
m_serial_str_json_write_init(serial, str);
r = web_data_out_serial(serial, data);
m_serial_str_json_write_clear(serial);
if (r == M_SERIAL_OK_DONE) {
// Post JSON string
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/json");
if (headers != NULL) {
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, string_get_cstr(str));
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
if (display_answer == false) {
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ignore_data);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
}
res = curl_easy_perform(handle);
curl_slist_free_all(headers);
}
}
curl_easy_cleanup(handle);
string_clear(str);
}
return (r == M_SERIAL_OK_DONE) && (res == CURLE_OK);
}
/////////////////////////////////////////
// Remote server URL
#define URL_GET "https://httpbin.org/get"
#define URL_POST "https://httpbin.org/post"
int main(void)
{
CURLcode ret = curl_global_init(CURL_GLOBAL_DEFAULT);
if (ret != CURLE_OK) abort();
web_data_t data;
web_data_init(data);
// Test http GET
bool b = get_data(data, URL_GET);
if (b) {
// We success receiving the JSON object and fill in the C object web_data with it.
// Let's display the object content:
printf("SUCCESS. Receive data =\n");
web_data_out_str(stdout, data);
printf("\n");
} else {
printf("ERROR: Cannot GET data or get data are not in expected format\n");
}
// Test http POST
web_data_emplace_origin(data, "This is the origin");
web_data_emplace_url(data, "This is the URL");
//web_data_set_origin(data, STRING_CTE("This is the origin"));
//web_data_set_url(data, STRING_CTE("This is the URL"));
b = post_data(data, URL_POST, false);
if (b) {
printf("SUCCESS: Post\n");
} else {
printf("ERROR post\n");
}
web_data_clear(data);
curl_global_cleanup();
return 0;
}
#else
int main(void) { return 0; }
#endif
+11 -10
View File
@@ -4,44 +4,45 @@
#include "m-dict.h"
#include "m-string.h"
// Define a dictionnary of string --> int
// Define a dictionary of string --> int
// named 'dict_str'
DICT_DEF2(dict_str, string_t, STRING_OPLIST, int, M_BASIC_OPLIST)
int main(void)
{
// Declare the dictionnary 'h'
// Declare the dictionary 'h'
dict_str_t h;
int max = 1;
// Declare the string 'key'
string_t key;
// Initialize the dictionnary & the string
// Initialize the dictionary & the string
dict_str_init(h);
string_init (key);
// While there is some data in the stream
while (!feof(stdin))
while (true)
{
// Read a complete line from the stream and fill in the string 'key'
string_fgets (key, stdin, STRING_READ_PURE_LINE);
// If the 'key' exists in the dictionnary
if (feof(stdin)) break;
// If the 'key' exists in the dictionary
int *v = dict_str_get (h, key);
if (v != NULL) {
// Increment the counter in the dictionnary
// Increment the counter in the dictionary
(*v)++;
max = M_MAX(*v, max);
} else {
// Update the dictionnary with h[key] = 1
// Update the dictionary with h[key] = 1
dict_str_set_at(h, key, 1);
}
}
printf ("Max occurence = %d\n", max);
printf ("Max occurrence = %d\n", max);
// Iterate on the dictionnary
// Iterate on the dictionary
dict_str_it_t it;
for(dict_str_it(it, h); !dict_str_end_p(it); dict_str_next(it)) {
// Get a constant reference on the pair stored in the dictionnary
// Get a constant reference on the pair stored in the dictionary
const struct dict_str_pair_s *pair = dict_str_cref(it);
printf ("Key=%s Value=%d\n",
string_get_cstr(pair->key), pair->value);
+1 -1
View File
@@ -10,7 +10,7 @@
*/
static const char empty_str[] = "EMPTY";
static const char deleted_str[] = "DELETED";
static const char *oor_table[] = { empty_str, deleted_str };
static const char *const oor_table[] = { empty_str, deleted_str };
static inline bool oor_equal_p(const char *k, int n)
{
+56
View File
@@ -0,0 +1,56 @@
#include "m-i-list.h"
typedef struct data_s {
M_ILIST_INTERFACE(list1, struct data_s);
char *ptr;
int n;
} data_t;
static void
data_init(data_t *p)
{
M_ILIST_INIT_FIELD(list1, *p);
p->ptr = malloc(100);
p->n = 100;
}
static void
data_clear(data_t *p)
{
free(p->ptr);
p->n = 0;
}
static void
data_init_set(data_t *p, data_t s)
{
p->ptr = malloc(s.n);
p->n = s.n;
memcpy(p->ptr, s.ptr, s.n);
}
#define DATA_OP (INIT(API_2(data_init)), INIT_SET(API_2(data_init_set)), CLEAR(API_2(data_clear)) )
M_ILIST_DEF(list1, data_t, DATA_OP)
data_t pool[100];
int main(void)
{
list1_t l;
list1_init(l);
data_init(&pool[0]);
list1_push_back(l, &pool[0]);
data_init(&pool[1]);
list1_push_back(l, &pool[1]);
list1_it_t it;
list1_it(it, l);
data_init(&pool[2]);
list1_insert(l, it, &pool[2]);
list1_clear(l);
exit(0);
}
+22 -1
View File
@@ -8,13 +8,17 @@
#include "m-string.h"
#include "m-algo.h"
// Let's create a list of string by using the M*LIB dynamic string.
// Let's create a list of string by using the M*LIB dynamic string 'string_t'
// The prefix used for generating functions will be 'list_string'
LIST_DEF(list_string, string_t)
// Let's register the oplist globally so that other M*LIB macros
// can get the oplist without us giving it explictly to them.
// An oplist is the association of operators to the provided methods
// so that generic code can use correctly an object of such type.
// LIST_OPLIST is a macro used to generate the oplist associated to the
// list itself. It needs the prefix used for generating the list (aka list_string here)
// and the oplist of the item in the list (aka the string oplist)
#define M_OPL_list_string_t() LIST_OPLIST(list_string, STRING_OPLIST)
// Let's define some basic algorithms over this list of string.
@@ -95,6 +99,23 @@ int main(void)
}
} // Everything is cleared after this point.
// Let's create another list of string and init it.
list_string_t list;
list_string_init(list);
// Let's put some elements in it:
list_string_emplace_back (list, "A");
list_string_emplace_back (list, "B");
list_string_emplace_back (list, "C");
list_string_emplace_back (list, "D");
// Reverse it
list_string_reverse(list);
// Let's display the string by serializing the elements of the list:
printf ("List of string = ");
list_string_out_str(stdout, list);
printf ("\n");
// Don't forget to clear the list before leaving
list_string_clear(list);
return 0;
}
+5 -2
View File
@@ -208,11 +208,14 @@ dict_read_from_file(dict_mph_t dict, array_string_t arr, const char filename[])
// 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++;
}
}
}
// Now 'arr' won't change anymore and we can get reliable pointers from it.
for M_EACH(item, arr, array_string_t) {
dict_mph_set_at(dict, string_get_cstr(*item), line);
line++;
}
fclose(f);
}
+39
View File
@@ -0,0 +1,39 @@
#include <stdio.h>
#include "m-tree.h"
TREE_DEF(tree, int)
// Compute sum of the tree using iterator and a pre-order walk
static int tree_sum_iter(tree_t t)
{
int s = 0;
for(tree_it_t it = tree_it(t); !tree_end_p(it); tree_next(&it)) {
s += *tree_cref(it);
}
return s;
}
// Compute sum of the tree using recursive call
static int tree_sum_recur(tree_it_t it)
{
if (tree_end_p(it))
return 0;
if (tree_leaf_p(it))
return *tree_cref(it);
assert( tree_degree(it) == 2);
return *tree_cref(it) + tree_sum_recur(tree_down(it)) + tree_sum_recur(tree_right(tree_down(it)));
}
int main(void)
{
tree_t t;
tree_init(t);
tree_it_t r = tree_set_root(t, 1);
tree_it_t l = tree_insert_child(r, 2);
tree_insert_child(r, 5);
tree_insert_child(l, 3);
tree_insert_child(l, 4);
printf("sum = %d vs %d\n", tree_sum_iter(t), tree_sum_recur(tree_it(t)));
tree_clear(t);
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
#include "m-try.h"
#include "m-array.h"
// Define & register an array of int
ARRAY_DEF(array, int)
#define M_OPL_array_t() ARRAY_OPLIST(array, M_BASIC_OPLIST)
// This shall be defined once per program to define the global variables needed
// to handle the exceptions.
M_TRY_DEF_ONCE()
static void f(void)
{
M_TRY(f_exception) {
// Let's define a variable and do stuff:
// Using M_LET allows the register of destructors for the exception handler.
M_LET(a, array_t) {
array_push_back(a, 5);
array_push_back(a, 4);
array_push_back(a, 3);
array_push_back(a, 2);
array_push_back(a, 1);
// Throw a M_ERROR_MEMORY exception
M_THROW(M_ERROR_MEMORY);
printf("This should never be printed!\n");
}
} M_CATCH(f_exception, M_ERROR_MEMORY) {
// Exception handler than catches the M_ERROR_MEMORY errors.
// Here the destructor of variable a has been called to free memory
printf("Memory exception %d received in f function, thrown by %s:%d\n",
f_exception->error_code, f_exception->filename, f_exception->line);
}
}
static void g(void)
{
M_TRY(f_exception) {
// Let's define a variable and do stuff:
// Using M_LET allows the register of destructors for the exception handler.
M_LET(a, array_t) {
array_push_back(a, 5);
array_push_back(a, 4);
array_push_back(a, 3);
array_push_back(a, 2);
array_push_back(a, 1);
// Throw an unkown exception
M_THROW(42);
printf("This should never be printed!\n");
}
} M_CATCH(f_exception, M_ERROR_MEMORY) {
printf("Memory exception %d received in f function, thrown by %s:%d\n",
f_exception->error_code, f_exception->filename, f_exception->line);
printf("This should never be printed!\n");
}
}
int main(void)
{
// Let's do a global exception handler
M_TRY(main_exception) {
f();
g();
} M_CATCH(main_exception, 0) {
// Exception handler than catches all errors.
// Here the destructor of variable a has been called to free memory
printf("Exception %d received in main function, thrown by %s:%d\n",
main_exception->error_code, main_exception->filename, main_exception->line);
}
printf("You should run it under valgrind to check that all memory allocations are freed.\n");
exit(0);
}
+69
View File
@@ -0,0 +1,69 @@
#include <stdio.h>
#include "m-array.h"
#include "m-algo.h"
#include "m-string.h"
ARRAY_DEF(array_int, int)
#define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST)
ALGO_DEF(array_int, array_int_t)
static bool callback1(int *out, int in, void *data)
{
int *cpt = data;
bool ret = true;
if (*cpt == -1 && in == 11)
*cpt = 0; // Start when first reach 11
ret &= (*cpt >= 0); // Skip initial numbers unequal 11
ret &= (in % 2 != 0); // Collect odd numbers
(*cpt) += ret;
ret &= (*cpt <= 5); // Only take five numbers
*out = in * in; // Square each number
return ret;
}
static void callback_add_int(int *out, int in)
{
*out += in;
}
static void demo(void)
{
M_LET( (t, M_SEQ(0, 30)), vector, array_int_t) {
printf("t="); array_int_out_str(stdout, t);
int b = -1;
array_int_transform(vector, t, callback1, &b);
printf("\nvector="); array_int_out_str(stdout, vector); printf("\n");
array_int_reduce(&b, vector, callback_add_int);
printf("Sum=%d\n", b);
}
}
ARRAY_DEF(array_str, string_t)
#define M_OPL_array_str_t() ARRAY_OPLIST(array_str, M_STRING_OPLIST)
ALGO_DEF(array_str, array_str_t)
static bool callback2(string_t *out, const string_t in, void *data)
{
char *cp = data, c = *cp;
if (string_search_char(in, c) == M_STRING_FAILURE)
return false;
string_set(*out, in);
return true;
}
static void demo_split(void)
{
M_LET( (s, ("This is a sentence in C11.")), string_t)
M_LET(words, f_words, array_str_t) {
array_str_split(words, s, ' ');
array_str_transform(f_words, words, callback2, &(char){'i'});
printf("f_words="); array_str_out_str(stdout, f_words); printf("\n");
}
}
int main(void)
{
demo();
demo_split();
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
#include "m-list.h"
#include "m-generic.h"
LIST_DEF(list_uint, unsigned int)
// Register for M_LET & M_EACH
#define M_OPL_list_uint_t() LIST_OPLIST(list_uint, M_BASIC_OPLIST)
// Register for Generic:
#define M_GENERIC_ORG_1() (USER)
#define M_GENERIC_ORG_USER_COMP_1() (CORE)
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_1() M_OPL_list_uint_t()
int main(void)
{
M_LET(list, list_uint_t) {
// We can push in the list. It will call the registered method.
push(list, 42);
push(list, 17);
// We can iterate over the list
for each(item, list) {
M_PRINT("ITEM=", *item, "\n");
}
// We can print directly the list. It will use the registered method
M_PRINT("List = ", list, "\n");
}
}
+117
View File
@@ -0,0 +1,117 @@
#if HAVE_GMP
#include <stdio.h>
#include <gmp.h>
#include "m-array.h"
#include "m-list.h"
#include "m-generic.h"
// Register an organization for generic: USER
// Each index (here '1') shall be unique for the whole program
#define M_GENERIC_ORG_1() (USER)
// Register a component for the organization USER: CORE
// Each index (here '1') shall be unique for the whole organization
#define M_GENERIC_ORG_USER_COMP_1() (CORE)
// Register the oplist of a mpz_t. It is a classic oplist.
// Adding support for emplace and initialization
// Adding the ability to output its value for serialization/printing
// Adding type information for handling correctly the generic association
#define M_OPL_mpz_t() M_OPEXTEND(M_CLASSIC_OPLIST(mpz), \
INIT_WITH(mpz_init_set_ui), EMPLACE_TYPE(unsigned int), \
OUT_STR( API( mpz_out_str, VOID, ARG1, 10, ARG2)), \
GENTYPE(__mpz_struct *), TYPE(mpz_t) )
// Register the oplist as a generic type of the component CORE of the organization USER
// Each index (here '1') shall be unique for the whole component
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_1() M_OPL_mpz_t()
// Define an instance of an array of mpz_t (both type and function)
ARRAY_DEF(array_mpz, mpz_t)
// Register the oplist of the created instance of array of mpz_t
#define M_OPL_array_mpz_t() ARRAY_OPLIST(array_mpz, M_OPL_mpz_t())
// Register the oplist as a generic type of the component CORE of the organization USER
// Each index (here '10') shall be unique for the whole component
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_10() M_OPL_array_mpz_t()
// Define an instance of a list of mpz_t (both type and function)
LIST_DEF(list_mpz, mpz_t)
// Register the oplist of the created instance of array of mpz_t
#define M_OPL_list_mpz_t() LIST_OPLIST(list_mpz, M_OPL_mpz_t())
// Register the oplist as a generic type of the component CORE of the organization USER
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_11() M_OPL_list_mpz_t()
// Define an instance of a list of unsigned int (both type and function)
LIST_DEF(list_uint, unsigned)
// Register the oplist of the created instance of list of unsigned
#define M_OPL_list_uint_t() LIST_OPLIST(list_uint, M_BASIC_OPLIST)
// Register the oplist as a generic type of the component CORE of the organization USER
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_12() M_OPL_list_uint_t()
// Define an instance of a array of unsigned int (both type and function)
ARRAY_DEF(array_uint, unsigned)
// Register the oplist of the created instance of list of unsigned
#define M_OPL_array_uint_t() ARRAY_OPLIST(array_uint, M_BASIC_OPLIST)
// Register the oplist as a generic type of the component CORE of the organization USER
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_13() M_OPL_array_uint_t()
int main(void) {
print("LIST UINT = ");
// Initialize list as list_uint_t and push 17 & 42 in it.
let( (list, 17, 42), list_uint_t) {
push(list, 1742);
// Iterate over all elements of the list of unsigned int
for each (item, list) {
// Print the element
print(*item, " ");
}
// Print the full list too
print("\n","List=", list, "\n");
}
// Now list has been cleared.
print("Array UINT = ");
// Initialize list as array_uint_t and push 17 & 42 in it.
let( (list, 17, 42), array_uint_t) {
push(list, 1742);
// Iterate over all elements of the array of unsigned int
for each (item, list) {
// Print the element
print(*item, " ");
}
// Print the full array too
print("\n","List=", list, "\n");
}
// Now array has been cleared.
print("LIST MPZ = ");
// Initialize list as array_mpz_t and push_emplace mpz_t from 17 & 42 in it.
// The parenthesis around 17 in (17) means to emplace
let( (list, (17), (42)), list_mpz_t) {
list_mpz_emplace_back(list, 1742); // Generic emplace is not supported yet
// Iterate over all elements of the array of mpz_t
for each (item, list) {
// Print the element
// Since we registered the out_str of a mpz_t, we can display it directly
print(*item, " ");
}
// Print the full list too
print("\n","List=", list, "\n");
}
print("ARRAY MPZ = ");
// Same as before but for array
let( (list, (17), (42)), array_mpz_t) {
array_mpz_emplace_back(list, 1742);
for each (item, list) {
print(*item, " ");
}
print("\n","List=", list, "\n");
}
exit(0);
}
#else
int main(void) {}
#endif
+12 -10
View File
@@ -35,6 +35,7 @@ mpz_fast_hash(const mpz_t z)
#define M_OPL_mpz_t() \
(INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), \
INIT_WITH(API_1(M_INIT_WITH_THROUGH_EMPLACE_TYPE)), \
INIT_MOVE(M_COPY_A1_DEFAULT) \
EQUAL( API( mpz_cmp, EQ(0), ARG1, ARG2)), \
CMP(mpz_cmp), \
HASH(mpz_fast_hash), \
@@ -77,7 +78,8 @@ mpfr_fast_hash (const mpfr_t f)
/* Define the OPLIST of a mpfr_t and register it globally */
#define M_OPL_mpfr_t() \
(INIT(mpfr_init), INIT_SET(API(mpfr_init_set, VOID, ARG1, ARG2, MPFR_RNDN)), SET(API(mpfr_set, VOID, ARG1, ARG2, MPFR_RNDN)), CLEAR(mpfr_clear), \
INIT_WITH(API_1(M_INIT_WITH_THROUGH_EMPLACE_TYPE)), \
INIT_WITH(API_1(M_INIT_WITH_THROUGH_EMPLACE_TYPE)), \
INIT_MOVE(M_COPY_A1_DEFAULT) \
EQUAL( API( mpfr_cmp, EQ(0), ARG1, ARG2)), \
CMP(mpfr_cmp), \
HASH(mpfr_fast_hash), \
@@ -225,15 +227,15 @@ int main(void)
M_PRINT("\nThe array is equal to ", (array, my_array_t), "\n");
} /* All variables are automatically cleared beyond this point */
// Define an array of mpz_t as { 1, 2, 1 } (with auto promotion from integer / string / double)
// The auto-promotion uses _Generic feature and is available only in C11.
M_LET ( (az, (1), ("2"), (1.0)), array_mpz_t)
M_LET ( roots, array_mpfr_t) {
// Compute roots
compute_roots(roots, az);
// Display of roots:
M_PRINT("Roots of ", (az, array_mpz_t), " are ", (roots, array_mpfr_t), "\n");
}
// Define an array of mpz_t as { 1, 2, 1 } (with auto promotion from integer / string / double)
// The auto-promotion uses _Generic feature and is available only in C11.
M_LET ( (az, (1), ("2"), (1.0)), array_mpz_t)
M_LET ( roots, array_mpfr_t) {
// Compute roots
compute_roots(roots, az);
// Display of roots:
M_PRINT("Roots of ", (az, array_mpz_t), " are ", (roots, array_mpfr_t), "\n");
}
}
+28 -2
View File
@@ -14,32 +14,50 @@ RBTREE_DEF(tree_cstr, const char *, M_CSTR_OPLIST)
// It is probably the best for beginner in M*LIB to start like this.
static void classic1(void)
{
// Let's declare a tree on the stack.
// Even if the tree itself is allocated on the stack,
// the nodes will be allocated on the heap.
tree_cstr_t tree;
// Initialize the tree using the provided constructor
tree_cstr_init(tree);
// Let's fill in the tree
// Note that the object of the tree are pointer
// to array of char. Therefore the strings themselves
// are not stored in the tree but only a reference to then
// As such, the pointed objects shall not moved nor being
// destroyed, which is the case for const C string.
tree_cstr_push (tree, "Hello");
tree_cstr_push (tree, "World");
tree_cstr_push (tree, "How");
tree_cstr_push (tree, "Are");
tree_cstr_push (tree, "You");
// Let's print the tree.
// We'll use the OUT_STR method to display the tree
// without iterating ourself in the tree.
printf("\nUsing C string, the generated (sorted) tree is ");
tree_cstr_out_str(stdout, tree);
printf ("\n");
// Let's search for the key "How" in the tree.
// If it is not found a NULL pointer is returned.
// Otherwise a pointer to the key within the tree is returned.
// This pointer is valid until a modification operation on the tree is done.
const char *present = tree_cstr_get(tree, "How") == NULL ? "not " : "";
printf("The word 'How' is %spresent\n", present);
present = tree_cstr_get(tree, "Not") == NULL ? "not " : "";
printf("The word 'Not' is %spresent\n", present);
// Destroy the tree by calling the destructor.
tree_cstr_clear(tree);
}
// Let's define another Red Black Tree over the M*LIB strings
// Notice that we don't need to specify the oplist for this case.
// since the oplist of a string_t is registered globally.
RBTREE_DEF(tree_str, string_t)
// The classic C way
@@ -52,16 +70,24 @@ static void classic2(void)
// STRING_CTE enables to construct a temporary const string_t from a const char *
// usable for function call.
// Note that this time the full string is stored within the tree,
// and not only a pointer to it.
tree_str_push (tree, STRING_CTE("Hello"));
tree_str_push (tree, STRING_CTE("World"));
tree_str_push (tree, STRING_CTE("How"));
tree_str_push (tree, STRING_CTE("Are"));
tree_str_push (tree, STRING_CTE("You"));
// We may also push a const C string into a string_t by calling the emplace function
tree_str_emplace (tree, "You");
// Let's print the tree
printf("\nUsing M*LIB string_t, the generated (sorted) tree is ");
tree_str_out_str(stdout, tree);
printf ("\n");
// Let's search for the string "How"
// We convert it to a temporary string_t, and let's test
// if it is found or not by testing if the returned pointer to string_t
// is null or not.
const char *present = tree_str_get(tree, STRING_CTE("How")) == NULL ? "not " : "";
printf("The word 'How' is %spresent\n", present);
+68
View File
@@ -0,0 +1,68 @@
// Disable small name usage
#define M_USE_SMALL_NAME 0
#include "m-algo.h"
#include "m-array.h"
#include "m-atomic.h"
#include "m-bitset.h"
#include "m-bptree.h"
#include "m-bstring.h"
#include "m-buffer.h"
#include "m-deque.h"
#include "m-dict.h"
#include "m-funcobj.h"
#include "m-generic.h"
#include "m-genint.h"
#include "m-i-list.h"
#include "m-list.h"
#include "m-prioqueue.h"
#include "m-rbtree.h"
#include "m-serial-bin.h"
#include "m-serial-json.h"
#include "m-shared-ptr.h"
#include "m-snapshot.h"
#include "m-string.h"
#include "m-thread.h"
#include "m-tree.h"
#include "m-try.h"
#include "m-tuple.h"
#include "m-variant.h"
#include "m-worker.h"
M_ARRAY_DEF(array, m_string_t)
M_ALGO_DEF(array, M_ARRAY_OPLIST(array, M_STRING_OPLIST))
M_BPTREE_DEF2(bptree, 11, m_string_t, m_string_t)
M_BUFFER_DEF(queue, m_string_t, 10, M_BUFFER_QUEUE)
M_LIST_DEF(list, m_string_t)
M_LIST_DUAL_PUSH_DEF(list2, m_string_t)
M_DEQUE_DEF(deque, m_string_t)
M_DICT_DEF2(dict, m_string_t, m_string_t)
M_DICT_OA_DEF2(dict_oa, m_string_t, m_string_t)
M_DICT_SET_DEF(dict_set, m_string_t)
M_DICT_OASET_DEF(dict_oa_set, m_string_t)
M_RBTREE_DEF(rbtree, m_string_t)
M_FUNC_OBJ_INS_DEF(compare_by /* name of the instance */,
array_cmp_obj /* name of the interface */,
(a, b) /* name of the input parameters of the function like object. The type are inherited from the interface. */, {
/* code of the function object */
if (m_string_equal_cstr_p(self->sort_field, "normal"))
return m_string_cmp(a, b);
else
return -m_string_cmp(a, b);
},
/* Additional fields stored in the function object */
(sort_field, m_string_t) )
M_PRIOQUEUE_DEF(prio_queue, m_string_t)
M_SHARED_PTR_DEF(shared_string, m_string_t)
M_SNAPSHOT_SPSC_DEF(snap1, m_string_t)
M_SNAPSHOT_SPMC_DEF(snap2, m_string_t)
M_SNAPSHOT_MPMC_DEF(snap3, m_string_t)
M_TREE_DEF(tree, m_string_t)
M_TUPLE_DEF2(tuple, (a, m_string_t))
M_VARIANT_DEF2(variant, (a, m_string_t))
M_WORKER_SPAWN_DEF2(worker, (filename, m_string_t) )
int main(void)
{
exit(0);
}
+188
View File
@@ -0,0 +1,188 @@
#include <stdlib.h>
// Define the memory context.
typedef int *context_t;
// Define the realloc / free that accepts the memory context.
static void *my_realloc(void *ptr, size_t old, size_t new, size_t base, context_t context)
{
(void) context;
(void) old;
return realloc(ptr, new*base);
}
static void my_free(void *ptr, size_t old, size_t base, context_t context)
{
(void) context;
(void) old;
(void) base;
free(ptr);
}
// Request M*LIB to generate functions accepting a memory context parameter that will be passed to the memory functions
# define M_USE_CONTEXT context_t
// Overloaded memory functions to use the custom ones.
# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_realloc(ptr, o, n, sizeof (type), ctx)
# define M_MEMORY_FREE(ctx, type, ptr, o) my_free(ptr, o, sizeof (*ptr), ctx)
# define M_MEMORY_ALLOC(ctx, type) my_realloc(NULL, 0, 1, sizeof (type), ctx)
# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, 1, sizeof (*ptr), ctx)
// Now we can include M*LIB functions.
#include "m-string.h"
#include "m-bitset.h"
#include "m-bstring.h"
#include "m-genint.h"
#include "m-algo.h"
context_t my_pool = NULL;
extern int f(string_t, bitset_t b, bstring_t y);
int f(string_t x, bitset_t b, bstring_t y)
{
string_init(x);
string_set_str(my_pool, x, "Hello world");
string_clear(my_pool, x);
bitset_init(b);
bitset_push_back(my_pool, b, true);
bitset_clear(my_pool, b);
bstring_init(y);
bstring_push_back(my_pool, y, 0);
bstring_clear(my_pool, y);
return 0;
}
#include "m-array.h"
ARRAY_DEF(array, int)
ALGO_DEF(array, ARRAY_OPLIST(array, M_BASIC_OPLIST))
ARRAY_DEF(array_str, string_t)
ALGO_DEF(array_str, ARRAY_OPLIST(array_str, STRING_OPLIST))
#include "m-prioqueue.h"
PRIOQUEUE_DEF(prio, int)
PRIOQUEUE_DEF(prio_str, string_t)
ALGO_DEF(prio_str, PRIOQUEUE_OPLIST(prio_str, STRING_OPLIST))
#include "m-rbtree.h"
RBTREE_DEF(rbtree, int)
RBTREE_DEF(rbtree_str, string_t)
ALGO_DEF(rbtree_str, RBTREE_OPLIST(rbtree_str, STRING_OPLIST))
#include "m-list.h"
LIST_DEF(list, int)
ALGO_DEF(list, LIST_OPLIST(list, M_BASIC_OPLIST))
LIST_DEF(list_str, string_t)
ALGO_DEF(list_str, LIST_OPLIST(list_str, M_STRING_OPLIST))
LIST_DUAL_PUSH_DEF(list2, int)
LIST_DUAL_PUSH_DEF(list2_str, string_t)
ALGO_DEF(list2_str, LIST_OPLIST(list2_str, M_STRING_OPLIST))
#include "m-bptree.h"
BPTREE_DEF2(bptree, 5, int, int)
BPTREE_DEF2(bptree_str, 8, string_t, string_t)
ALGO_DEF(bptree_str, M_BPTREE_OPLIST2(bptree_str, M_STRING_OPLIST, M_STRING_OPLIST))
#include "m-dict.h"
DICT_DEF2(dict, int, int)
DICT_DEF2(dict_str, string_t, string_t)
ALGO_DEF(dict_str, DICT_OPLIST(dict_str, M_STRING_OPLIST, M_STRING_OPLIST))
DICT_SET_DEF(set, int)
DICT_SET_DEF(set_str, string_t)
ALGO_DEF(set_str, DICT_SET_OPLIST(set_str, M_STRING_OPLIST))
static inline bool oor_equal_p(int k, char n)
{
return k == INT_MIN + n;
}
static inline int oor_set(char n)
{
return INT_MIN + n;
}
DICT_OA_DEF2(dict_oa,
int, M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_4(oor_set))),
int, M_BASIC_OPLIST)
DICT_OA_DEF2(dict2_str, string_t, string_t)
ALGO_DEF(dict2_str, DICT_OPLIST(dict2_str, M_STRING_OPLIST, M_STRING_OPLIST))
DICT_OASET_DEF(set_oa,
int, M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_4(oor_set))) )
DICT_OASET_DEF(set2_str, string_t)
ALGO_DEF(set2_str, DICT_SET_OPLIST(set2_str, M_STRING_OPLIST))
#include "m-buffer.h"
BUFFER_DEF(buffer1, int, 10, M_BUFFER_QUEUE)
BUFFER_DEF(buffer2, int, 0, M_BUFFER_STACK)
M_QUEUE_MPMC_DEF(queue1, int, M_BUFFER_STACK)
M_QUEUE_SPSC_DEF(queue2, int, M_BUFFER_STACK)
BUFFER_DEF(buffer11, string_t, 10, M_BUFFER_QUEUE)
BUFFER_DEF(buffer22, string_t, 0, M_BUFFER_STACK)
M_QUEUE_MPMC_DEF(queue11, string_t, M_BUFFER_STACK)
M_QUEUE_SPSC_DEF(queue22, string_t, M_BUFFER_STACK)
#include "m-deque.h"
M_DEQUE_DEF(deque, int)
M_DEQUE_DEF(deque_str, string_t)
M_ALGO_DEF(deque_str, M_DEQUE_OPLIST(deque_str, M_STRING_OPLIST))
#include "m-snapshot.h"
M_SNAPSHOT_SPSC_DEF(snap1, int)
M_SNAPSHOT_SPMC_DEF(snap2, int)
M_SNAPSHOT_MPMC_DEF(snap3, string_t)
#include "m-tree.h"
M_TREE_DEF(tree, int)
M_TREE_DEF(tree_str, string_t)
#include "m-tuple.h"
M_TUPLE_DEF2(tuple1, (num, int) )
M_TUPLE_DEF2(tuple2, (num, int), (name, string_t) )
#include "m-variant.h"
M_VARIANT_DEF2(variant1, (num, int) )
M_VARIANT_DEF2(variant2, (num, int), (name, string_t) )
#include "m-worker.h"
M_WORKER_SPAWN_DEF2(worker_int, (in, int) )
M_WORKER_SPAWN_DEF2(worker_str, (str, string_t) )
#include "m-shared-ptr.h"
M_SHARED_PTR_DEF(shared_int, int)
M_SHARED_PTR_DEF(shared_str, string_t)
#include "m-queue.h"
M_QUEUE_DEF(queue_10, int, 10 )
M_STACK_DEF(stack_20, string_t, 20)
#include "m-serial-json.h"
#include "m-serial-bin.h"
int main(void)
{
return 0;
}
+108
View File
@@ -0,0 +1,108 @@
#include <stdio.h>
#include "m-variant.h"
#include "m-list.h"
// Define the variant with the given basic types
// A variant is an union of all its type, with the information needed to identify which type it holds
VARIANT_DEF2(obj,
(boolean, bool, M_BOOL_OPLIST),
(integer, int, M_BASIC_OPLIST),
(real, double, M_BASIC_OPLIST),
(cstr, const char*, M_CSTR_OPLIST))
// Associate obj_t to its oplist, so that we can only use 'obj_t'
// and M*LIB knows how to handle this type properly
// We need to extend the oplist with FIELD so that M*LIB is able to generate 'emplace' functions
#define M_OPL_obj_t() \
VARIANT_OPLIST(obj, M_OPEXTEND(M_BOOL_OPLIST, FIELD(boolean)), \
M_OPEXTEND(M_BASIC_OPLIST, FIELD(integer)), \
M_OPEXTEND(M_BASIC_OPLIST, FIELD(real)), \
M_OPEXTEND(M_CSTR_OPLIST, FIELD(cstr)))
// Define a list over the variant
LIST_DEF(list, obj_t)
// Associate list_t to its oplist, so that we can only use 'list_t'
#define M_OPL_list_t() LIST_OPLIST(list, M_OPL_obj_t())
/* Print the type of the object */
static void print_obj(obj_t o, const char name[])
{
if (obj_empty_p(o)) {
printf("Object %s is empty\n", name);
}
if (obj_boolean_p(o)) {
printf("Object %s is a boolean (%d)\n", name, *obj_get_boolean(o));
}
if (obj_integer_p(o)) {
printf("Object %s is an integer (%d)\n", name, *obj_get_integer(o));
}
if (obj_real_p(o)) {
printf("Object %s is a float (%f)\n", name, *obj_get_real(o));
}
if (obj_cstr_p(o)) {
printf("Object %s is C string (%s)\n", name, *obj_get_cstr(o));
}
}
static void f(void)
{
// Initialize & afterward clear the object
M_LET(o, obj_t) {
print_obj(o, "1st init");
obj_set_real(o, 23.4);
print_obj(o, "1st+set");
} // Now the object is out of scope and is cleared.
// Initialize, set as an integer of value '12' & afterward clear the object
// () around 12 means that we want to 'emplace' object using the input '12' to construct the object in-place,
// rather than setting it from an already existing 'obj_t'.
// It checks for the type of 12, find it is an integer, and initialize o with this value
// It won't work if there is multiple compatible type in the variant
M_LET( (o, (12)), obj_t) {
print_obj(o, "2nd init");
obj_set_cstr(o, "Hello");
print_obj(o, "2nd+set");
}
M_LET( (o, (true)), obj_t) {
// In C11, a 'true' value is an 'int' type, not a bool!
print_obj(o, "3rd init");
obj_set_boolean(o, true);
print_obj(o, "3rd+set");
// Without "()" around the source, it will set the value using the object builtin copy.
M_LET( (p, o), obj_t) {
print_obj(o, "3rd+rec");
// We can also print the object directly:
printf("OBJ=");
obj_out_str(stdout, o);
printf("\n");
}
}
}
static void g(void)
{
// Initialize a list of obj_t and clear it afterwards
M_LET(l, list_t){
// Let's emplace some type in the list
list_emplace_back_boolean(l, true);
list_emplace_back_integer(l, 12);
list_emplace_back_cstr(l, "World");
list_emplace_back_real(l, 17.42);
// We can iterate on the list
for M_EACH(item, l, list_t) {
print_obj(*item, "loop");
}
// We can also print the object directly:
printf("LIST=");
list_out_str(stdout, l);
printf("\n");
} // Clear & free the list
}
int main(void)
{
f();
g();
exit(0);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef MY_HEADER
#define MY_HEADER
// Request M*LIB to not inline functions if not the library source
#define M_USE_DECL
#include "m-string.h"
#include "m-array.h"
ARRAY_DEF(array_str, string_t)
#endif
+5
View File
@@ -0,0 +1,5 @@
// Request M*LIB to define functions
// This include both m-string functions and array_str templated functions
// Defining M_USE_EXTERN_DEF override M_USE_EXTERN_DECL definition
#define M_USE_DEF
#include "exm-header.h"
+14
View File
@@ -0,0 +1,14 @@
#include "exm-header.h"
int main(void)
{
array_str_t a;
array_str_init(a);
array_str_emplace_back(a, "Hello");
array_str_emplace_back(a, "World");
printf("a=");
array_str_out_str(stdout, a);
printf("\n");
array_str_clear(a);
return 0;
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef MY_HEADER
#define MY_HEADER
// Request M*LIB to not always inline functions
#define M_USE_FINE_GRAINED_LINKAGE
#include "m-string.h"
#include "m-array.h"
// The following functions will not be inlined:
#define M_USE_DECL
ARRAY_DEF(array_str, string_t)
#undef M_USE_DECL
#endif
+4
View File
@@ -0,0 +1,4 @@
// Request M*LIB to define functions for functions defined within M_USE_DECL.
#define M_USE_DEF
#include "exn-header.h"
+19
View File
@@ -0,0 +1,19 @@
#include "exn-header.h"
int main(void)
{
string_t s;
string_init_set_str(s, "a=");
string_fputs(stdout, s);
string_clear(s);
array_str_t a;
array_str_init(a);
array_str_emplace_back(a, "Hello");
array_str_emplace_back(a, "World");
//printf("a=");
array_str_out_str(stdout, a);
printf("\n");
array_str_clear(a);
//main2();
return 0;
}
+82 -82
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - ALGO module
*
* Copyright (c) 2017-2023, Patrick Pelissier
* Copyright (c) 2017-2025, 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:
@@ -28,7 +28,7 @@
#include "m-core.h"
/* Define different kind of basic algorithms named 'name' over the container
which oplist is 'cont_oplist', as static inline functions.
which oplist is 'cont_oplist'.
USAGE:
ALGO_DEF(algogName, containerOplist|type if oplist has been registered) */
#define M_ALGO_DEF(name, cont_oplist) \
@@ -170,26 +170,27 @@
typedef bool (*M_F(name, _test_cb_ct))(type_t const); \
typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \
typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \
typedef void (*M_F(name, _transform_cb_ct))(type_t *, type_t const); \
typedef void (*M_F(name, _apply_cb_ct))(type_t); \
typedef bool (*M_F(name, _transform_cb_ct))(type_t *, type_t const, void*); \
typedef void (*M_F(name, _reduce_cb_ct))(type_t *, type_t const); \
typedef void (*M_F(name, _apply_cb_ct))(type_t M_IF_METHOD(IT_REF, cont_oplist)( , const) );
/* Define the function objects associated to the algorithms.
* Created Function objects are part of the public interface */
#define M_ALG0_DEF_FUNCOBJ(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \
FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \
FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \
FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), void, type_t *, type_t const ) \
FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), bool, type_t *, type_t const )\
M_FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \
/* Define the sort functions with the CMP operator using the order selected */
#define M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, order, sort_name) \
\
/* Define the encapsulation function that perform the selected order */ \
static inline int M_C3(name,sort_name,_cmp)(type_t const*a,type_t const*b) \
M_INLINE int M_C3(name,sort_name,_cmp)(type_t const*a,type_t const*b) \
{ \
return order M_CALL_CMP(type_oplist, *a, *b); \
} \
@@ -225,7 +226,7 @@
#define M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, cmp_func, cmp_param, cmp_arg) \
\
/* Test if the container is sorted */ \
static inline bool \
M_INLINE bool \
M_C3(name,sort_name,_p)(const container_t l cmp_param(name)) \
{ \
it_t it1; \
@@ -253,7 +254,7 @@
/* - a selection sort */ \
M_IF(M_AND(M_TEST_METHOD_P(SORT, cont_oplist), M_EMPTY_P(cmp_arg)))( \
/******** OPTIMIZED SORT FOR CONTAINER *********/ \
static inline void M_F(name,sort_name)(container_t l) \
M_INLINE void M_F(name,sort_name)(container_t l) \
{ \
M_CALL_SORT(cont_oplist, l, M_C3(name, sort_name,_cmp)); \
} \
@@ -265,7 +266,7 @@
\
/* Split the container 'l' into near even size l1 and l2 * \
using odd/even splitting */ \
static inline void \
M_INLINE void \
M_C3(name,sort_name,_split)(container_t l1, container_t l2, container_t l) \
{ \
it_t it; \
@@ -276,14 +277,14 @@
M_CALL_SPLICE_BACK(cont_oplist, (b ? l1 : l2), l, it); \
b = !b; \
} \
/* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \
M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); \
} \
\
/* Merge in empty 'l' the sorted container 'l1' and 'l2' */ \
static inline void \
M_INLINE void \
M_C3(name,sort_name,_merge)(container_t l, container_t l1, container_t l2 cmp_param(name)) \
{ \
/* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \
M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); \
it_t it; \
it_t it1; \
it_t it2; \
@@ -320,7 +321,7 @@
} \
\
/* Sort the container 'l' */ \
static inline void \
M_INLINE void \
M_F(name,sort_name)(container_t l cmp_param(name)) \
{ \
container_t l1; \
@@ -350,6 +351,7 @@
return; \
} \
/* Container length is greater than 2: split, sort & merge */ \
M_GLOBAL_CONTEXT(); \
M_CALL_INIT(cont_oplist, l1); \
M_CALL_INIT(cont_oplist, l2); \
M_C3(name,sort_name,_split)(l1, l2, l); \
@@ -357,13 +359,16 @@
M_F(name,sort_name)(l2 cmp_arg); \
M_C3(name,sort_name,_merge)(l, l1, l2 cmp_arg); \
/* l1 & l2 shall be empty now */ \
M_ASSERT( M_CALL_EMPTY_P(cont_oplist, l1)); \
M_ASSERT( M_CALL_EMPTY_P(cont_oplist, l2)); \
/* Normally nothing is done here */ \
M_CALL_CLEAR(cont_oplist, l2); \
M_CALL_CLEAR(cont_oplist, l1); \
} \
, \
M_IF_METHOD(IT_PREVIOUS, cont_oplist)( \
/******** GENERIC INSERTION SORT *********/ \
static inline void M_F(name,sort_name)(container_t l cmp_param(name)) \
M_INLINE void M_F(name,sort_name)(container_t l cmp_param(name)) \
{ \
it_t it1; \
it_t it2; \
@@ -400,7 +405,7 @@
\
, \
/********** GENERIC SELECTION SORT ************/ \
static inline void M_F(name,sort_name)(container_t l cmp_param(name)) \
M_INLINE void M_F(name,sort_name)(container_t l cmp_param(name)) \
{ \
it_t it1; \
it_t it2; \
@@ -433,8 +438,8 @@
) /* IF SORT METHOD */ \
/* Compute the union of two ***sorted*** containers */ \
M_IF_METHOD(IT_INSERT, cont_oplist)( \
static inline void \
M_C3(name,sort_name,_union)(container_t dst, const container_t src cmp_param(name)) \
M_INLINE void \
M_C3(name,sort_name,_union)(M_P_EXPAND container_t dst, const container_t src cmp_param(name)) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -478,8 +483,8 @@
\
/* Compute the intersection of two ***sorted*** containers */ \
M_IF_METHOD(IT_REMOVE, cont_oplist)( \
static inline void \
M_C3(name,sort_name,_intersect)(container_t dst, const container_t src cmp_param(name)) \
M_INLINE void \
M_C3(name,sort_name,_intersect)(M_P_EXPAND container_t dst, const container_t src cmp_param(name)) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -517,7 +522,7 @@
#define M_ALG0_DEF_FIND(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
/* It supposes that the container is not sorted */ \
/* Find the next occurrence from it (included) of data */ \
static inline void \
M_INLINE void \
M_F(name, _find_again) (it_t it, type_t const data) \
{ \
for ( /*nothing*/ ; !M_CALL_IT_END_P(cont_oplist, it) ; \
@@ -528,7 +533,7 @@
} \
\
/* Find the first occurrence of data */ \
static inline void \
M_INLINE void \
M_F(name, _find) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_FIRST(cont_oplist, it, l); \
@@ -536,7 +541,7 @@
} \
\
/* Test if data is within the container */ \
static inline bool \
M_INLINE bool \
M_F(name, _contain_p) (container_t const l, type_t const data) \
{ \
it_t it; \
@@ -549,7 +554,7 @@
PREVIOUS & IT_LAST are defined, then search backwards */ \
M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \
( \
static inline void \
M_INLINE void \
M_F(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
for (M_CALL_IT_LAST(cont_oplist, it, l); \
@@ -562,7 +567,7 @@
} \
, \
/* Otherwise search forward, but don't stop on the first occurrence */ \
static inline void \
M_INLINE void \
M_F(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_END(cont_oplist, it, l); \
@@ -578,7 +583,7 @@
) /* End of alternative of _find_last */ \
\
/* Count the number of occurrence of data in the container */ \
static inline size_t \
M_INLINE size_t \
M_F(name, _count) (container_t const l, type_t const data) \
{ \
it_t it; \
@@ -594,7 +599,7 @@
\
\
/* Find the next mismatch between the containers */ \
static inline void \
M_INLINE void \
M_F(name, _mismatch_again) (it_t it1, it_t it2) \
{ \
for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
@@ -608,7 +613,7 @@
} \
\
/* Find the first mismatch between the containers */ \
static inline void \
M_INLINE void \
M_F(name, _mismatch) (it_t it1, it_t it2, container_t const l1, container_t const l2 ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
@@ -625,7 +630,7 @@
#define M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, test_t, eq_t, call_test, call_eq) \
\
/* Find the next occurrence that matches the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _find_again_, suffix) (it_t it, test_t func) \
{ \
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it) ; \
@@ -636,7 +641,7 @@
} \
\
/* Find the first occurrence that matches the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _find_, suffix) (it_t it, container_t l, test_t func) \
{ \
M_CALL_IT_FIRST(cont_oplist, it, l); \
@@ -644,7 +649,7 @@
} \
\
/* Count the number of occurrence that matches the condition */ \
static inline size_t \
M_INLINE size_t \
M_C3(name, _count_, suffix) (container_t const l, test_t func) \
{ \
it_t it; \
@@ -660,7 +665,7 @@
} \
\
/* Find the next mismatch between the containers according to the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _mismatch_again_, suffix) (it_t it1, it_t it2, eq_t func) \
{ \
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
@@ -674,7 +679,7 @@
} \
\
/* Find the first mismatch between the containers according to the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _mismatch_, suffix) (it_t it1, it_t it2, container_t const l1, \
container_t l2, eq_t func) \
{ \
@@ -688,9 +693,9 @@
#define M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
/* Fill all the container with value (overwritten) */ \
static inline void \
M_F(name, _fill) (container_t l, type_t const value) \
M_P(void, name, _fill, container_t l, type_t const value) \
{ \
M_UNUSED_CONTEXT(); \
for M_EACH(item, l, cont_oplist) { \
M_CALL_SET(type_oplist, *item, value); \
} \
@@ -698,8 +703,7 @@
\
/* Fill the container with exactly 'n' occurrence of 'value' */ \
M_IF_METHOD(PUSH, cont_oplist)( \
static inline void \
M_F(name, _fill_n) (container_t l, size_t n, type_t const value) \
M_P(void, name, _fill_n, container_t l, size_t n, type_t const value) \
{ \
M_CALL_RESET(cont_oplist, l); \
for(size_t i = 0; i < n; i++) { \
@@ -710,9 +714,9 @@
\
/* Fill the container with FOR('value'; 'value'+'inc') */ \
M_IF_METHOD(ADD, type_oplist)( \
static inline void \
M_F(name, _fill_a) (container_t l, type_t const value, type_t const inc) \
M_P(void, name, _fill_a, container_t l, type_t const value, type_t const inc) \
{ \
M_UNUSED_CONTEXT(); \
type_t tmp; \
M_CALL_INIT_SET(type_oplist, tmp, value); \
for M_EACH(item, l, cont_oplist) { \
@@ -724,9 +728,9 @@
\
/* Fill the container with n occurences of FOR('value'; 'value'+'inc') */ \
M_IF_METHOD(PUSH, cont_oplist)( \
static inline void \
M_F(name, _fill_an) (container_t l, size_t n, type_t const value, type_t const inc) \
M_P(void, name, _fill_an, container_t l, size_t n, type_t const value, type_t const inc) \
{ \
M_UNUSED_CONTEXT(); \
type_t tmp; \
M_CALL_INIT_SET(type_oplist, tmp, value); \
M_CALL_RESET(cont_oplist, l); \
@@ -744,7 +748,7 @@
#define M_ALG0_DEF_MAP(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
/* Apply func for all elements of the container */ \
static inline void \
M_INLINE void \
M_F(name, _for_each) (container_t l, M_F(name, _apply_cb_ct) func) \
{ \
for M_EACH(item, l, cont_oplist) { \
@@ -753,32 +757,30 @@
} \
\
M_IF_METHOD(INIT, type_oplist)( \
M_IF_METHOD(PUSH_MOVE, cont_oplist)( \
M_IF_METHOD(PUSH, cont_oplist)( \
\
/* Apply func for all elements of the container src and push the result in dst */ \
static inline void \
M_F(name, _transform) (container_t dst, \
container_t src, \
M_F(name, _transform_cb_ct) func) \
M_P(void, name, _transform, container_t dst, container_t src, M_F(name, _transform_cb_ct) func, void *data) \
{ \
M_ASSERT(dst != src); \
M_CALL_RESET(cont_oplist, dst); \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, src, cont_oplist) { \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
func(&tmp, *item); \
M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \
if (func(&tmp, *item, data)) { \
M_CALL_PUSH(cont_oplist, dst, tmp); \
} \
} \
M_CALL_CLEAR(type_oplist, tmp); \
M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \
} \
, /* END PUSH_MOVE */), /* END INIT */ ) \
\
/* Reduce all elements of the container in dst in function of func */ \
M_IF_METHOD(SET, type_oplist)( \
static inline void \
M_F(name, _reduce) (type_t *dest, container_t const l, \
M_F(name, _transform_cb_ct) func) \
M_P(bool, name, _reduce, type_t *dest, container_t const l, M_F(name, _reduce_cb_ct) func) \
{ \
M_UNUSED_CONTEXT(); \
bool initDone = false; \
for M_EACH(item, l, cont_oplist) { \
if (initDone) { \
@@ -788,30 +790,31 @@
initDone = true; \
} \
} \
return initDone; \
} \
, /* END SET */) \
\
/* Reduce all transformed elements of the container in dst in function of func */ \
M_IF_METHOD(INIT, type_oplist)( \
static inline \
void M_F(name, _map_reduce) (type_t *dest, \
const container_t l, \
M_F(name, _transform_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) mapFunc) \
M_P(bool, name, _transform_reduce, type_t *dest, const container_t l, \
M_F(name, _reduce_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) mapFunc, void *data) \
{ \
M_UNUSED_CONTEXT(); \
bool initDone = false; \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, l, cont_oplist) { \
if (initDone) { \
mapFunc(&tmp, *item); \
redFunc(dest, tmp); \
if (mapFunc(&tmp, *item, data) ) { \
redFunc(dest, tmp); \
} \
} else { \
mapFunc(dest, *item); \
initDone = true; \
initDone = mapFunc(dest, *item, data); \
} \
} \
M_CALL_CLEAR(type_oplist, tmp); \
return initDone; \
} \
, ) \
@@ -819,7 +822,7 @@
/* Define ALL_OF algorithms */
#define M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, func_t, call) \
\
static inline bool \
M_INLINE bool \
M_C4(name, _any_of, suffix, p) (container_t const l, \
func_t func ) \
{ \
@@ -830,7 +833,7 @@
return false; \
} \
\
static inline bool \
M_INLINE bool \
M_C4(name, _all_of, suffix, p) (container_t const l, \
func_t func ) \
{ \
@@ -841,7 +844,7 @@
return true; \
} \
\
static inline bool \
M_INLINE bool \
M_C4(name, _none_of, suffix, p) (container_t l, \
func_t func ) \
{ \
@@ -856,7 +859,7 @@
/* Define MIN / MAX algorithms */
#define M_ALG0_DEF_MINMAX(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
static inline type_t * \
M_INLINE type_t * \
M_F(name, _min) (const container_t l) \
{ \
type_t *min = NULL; \
@@ -869,7 +872,7 @@
return min; \
} \
\
static inline type_t * \
M_INLINE type_t * \
M_F(name, _max) (const container_t l) \
{ \
type_t *max = NULL; \
@@ -882,7 +885,7 @@
return max; \
} \
\
static inline void \
M_INLINE void \
M_F(name, _minmax) (type_t **min_p, type_t **max_p, \
const container_t l) \
{ \
@@ -905,8 +908,7 @@
/* Define functions based on remove method */
#define M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
static inline void \
M_F(name, _uniq)(container_t l) \
M_P(void, name, _uniq, container_t l) \
{ \
it_t it1; \
it_t it2; \
@@ -927,8 +929,7 @@
} \
} \
\
static inline void \
M_F(name, _remove_val)(container_t l, type_t const val) \
M_P(void, name, _remove_val, container_t l, type_t const val) \
{ \
it_t it1; \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
@@ -942,8 +943,7 @@
} \
} \
\
static inline void \
M_F(name, _remove_if)(container_t l, M_F(name, _test_cb_ct) func) \
M_P(void, name, _remove_if, container_t l, M_F(name, _test_cb_ct) func) \
{ \
it_t it1; \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
@@ -961,7 +961,7 @@
#define M_ALG0_DEF_VECTOR(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
M_IF_METHOD(ADD, type_oplist)( \
static inline void M_F(name, _add) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _add) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -979,7 +979,7 @@
, /* NO_ADD METHOD */ ) \
\
M_IF_METHOD(SUB, type_oplist)( \
static inline void M_F(name, _sub) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _sub) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -997,7 +997,7 @@
, /* NO_SUB METHOD */ ) \
\
M_IF_METHOD(MUL, type_oplist)( \
static inline void M_F(name, _mul) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _mul) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -1015,7 +1015,7 @@
, /* NO_MUL METHOD */ ) \
\
M_IF_METHOD(DIV, type_oplist)( \
static inline void M_F(name, _div) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _div) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
+249 -237
View File
File diff suppressed because it is too large. Load diff
+11 -4
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin stdatomic wrapper for C++ compatibility
*
* Copyright (c) 2017-2023, Patrick Pelissier
* Copyright (c) 2017-2025, 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,7 +25,7 @@
#ifndef MSTARLIB_ATOMIC_H
#define MSTARLIB_ATOMIC_H
/* NOTE: Due to the C++ not having recognized stdatomic.h officialy,
/* NOTE: Due to the C++ not having recognized stdatomic.h officially,
it is hard to use this header directly with a C++ compiler.
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932
clang++ has no issue with this header but if someone includes
@@ -146,7 +146,7 @@ using std::memory_order_seq_cst;
#endif
/* Non working C++ atomic header, nor working stdatomic.h found.
Write a compatible layer using mutex as slin as possible.
Write a compatible layer using mutex as slim as possible.
Supports only up to 64-bits atomic (sizeof long long to be more precise).
The locks are never properly cleared and remain active until
the end of the program.
@@ -226,7 +226,7 @@ typedef _Atomic(uintmax_t) atomic_uintmax_t;
#endif
/* (INTERNAL) Unlock the mutex and return the given value */
static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
M_INLINE long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
{
m_mutex_unlock (*lock);
return val;
@@ -332,4 +332,11 @@ M_END_PROTECTED_CODE
#endif
// C17 deprecated ATOMIC_VAR_INIT
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201710L
# define M_ATOMIC_VAR_INIT(x) (x)
#else
# define M_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
#endif
#endif
+119 -97
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - BITSET module
*
* Copyright (c) 2017-2023, Patrick Pelissier
* Copyright (c) 2017-2025, 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:
@@ -81,7 +81,7 @@ typedef struct m_bitset_it_s {
} m_bitset_it_t[1];
/* Initialize a bitset (CONSTRUCTOR) */
static inline void
M_INLINE void
m_bitset_init(m_bitset_t t)
{
M_ASSERT (t != NULL);
@@ -93,7 +93,7 @@ m_bitset_init(m_bitset_t t)
}
/* Clean a bitset */
static inline void
M_INLINE void
m_bitset_reset(m_bitset_t t)
{
M_B1TSET_CONTRACT(t);
@@ -101,11 +101,10 @@ m_bitset_reset(m_bitset_t t)
}
/* Clear a bitset (DESTRUCTOR) */
static inline void
m_bitset_clear(m_bitset_t t)
M_P(void, m_bitset, _clear, m_bitset_t t)
{
m_bitset_reset(t);
M_MEMORY_FREE(t->ptr);
M_MEMORY_FREE(m_context, m_b1tset_limb_ct, t->ptr, t->alloc);
// This is not really needed, but is safer
// This representation is invalid and will be detected by the contract.
// A C compiler should be able to optimize out theses initializations.
@@ -114,8 +113,7 @@ m_bitset_clear(m_bitset_t t)
}
/* Set a bitset to another one */
static inline void
m_bitset_set(m_bitset_t d, const m_bitset_t s)
M_P(void, m_bitset, _set, m_bitset_t d, const m_bitset_t s)
{
M_B1TSET_CONTRACT(d);
M_B1TSET_CONTRACT(s);
@@ -124,10 +122,9 @@ m_bitset_set(m_bitset_t d, const m_bitset_t s)
if (M_LIKELY (s->size > 0)) {
// Test if enough space in target
if (s->size > M_B1TSET_FROM_ALLOC (d->alloc)) {
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, d->ptr, needAlloc);
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, d->ptr, d->alloc, needAlloc);
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(needAlloc);
return ;
M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc);
}
d->ptr = ptr;
d->alloc = needAlloc;
@@ -141,16 +138,18 @@ m_bitset_set(m_bitset_t d, const m_bitset_t s)
}
/* Initialize & set a bitset to another one (CONSTRUCTOR) */
static inline void
m_bitset_init_set(m_bitset_t d, const m_bitset_t s)
M_P(void, m_bitset, _init_set, m_bitset_t d, const m_bitset_t s)
{
M_ASSERT (d != s);
// In case of exception, d remains initialized,
// but without any allocation done.
// Therefore it is safe, not to clear d on exception.
m_bitset_init(d);
m_bitset_set(d, s);
m_bitset_set M_R(d, s);
}
/* Initialize & move a bitset (CONSTRUCTOR) from another one (DESTRUCTOR) */
static inline void
M_INLINE void
m_bitset_init_move(m_bitset_t d, m_bitset_t s)
{
M_B1TSET_CONTRACT(s);
@@ -164,15 +163,14 @@ m_bitset_init_move(m_bitset_t d, m_bitset_t s)
}
/* Move a bitset from another one (DESTRUCTOR) */
static inline void
m_bitset_move(m_bitset_t d, m_bitset_t s)
M_P(void, m_bitset, _move, m_bitset_t d, m_bitset_t s)
{
m_bitset_clear(d);
m_bitset_clear M_R(d);
m_bitset_init_move (d, s);
}
/* Set the bit 'i' in the bitset to the value 'x' */
static inline void
M_INLINE void
m_bitset_set_at(m_bitset_t v, size_t i, bool x)
{
M_B1TSET_CONTRACT(v);
@@ -187,7 +185,7 @@ m_bitset_set_at(m_bitset_t v, size_t i, bool x)
}
/* Flip the bit 'i' in the bitset */
static inline void
M_INLINE void
m_bitset_flip_at(m_bitset_t v, size_t i)
{
M_B1TSET_CONTRACT(v);
@@ -200,8 +198,7 @@ m_bitset_flip_at(m_bitset_t v, size_t i)
}
/* Push back the boolean 'x' in the bitset (increasing the bitset) */
static inline void
m_bitset_push_back (m_bitset_t v, bool x)
M_P(void, m_bitset, _push_back, m_bitset_t v, bool x)
{
M_B1TSET_CONTRACT (v);
if (M_UNLIKELY (v->size >= M_B1TSET_FROM_ALLOC (v->alloc))) {
@@ -209,15 +206,13 @@ m_bitset_push_back (m_bitset_t v, bool x)
const size_t needAlloc = M_B1TSET_INC_ALLOC_SIZE(v->alloc);
// Check for integer overflow
if (M_UNLIKELY_NOMEM (needAlloc <= v->alloc)) {
M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct));
return;
M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc);
}
// Alloc memory
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, needAlloc);
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, needAlloc);
// Check if success
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct));
return;
M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc);
}
v->ptr = ptr;
v->alloc = needAlloc;
@@ -239,23 +234,20 @@ m_bitset_push_back (m_bitset_t v, bool x)
}
/* Resize the bitset to have exactly 'size' bits */
static inline void
m_bitset_resize (m_bitset_t v, size_t size)
M_P(void, m_bitset, _resize, m_bitset_t v, size_t size)
{
M_B1TSET_CONTRACT (v);
// Check for overflow
if (M_UNLIKELY_NOMEM (size >= ((size_t)-1) - M_B1TSET_LIMB_BIT)) {
M_MEMORY_FULL((size_t) -1);
return;
M_MEMORY_FULL(char, -(size_t)1);
}
// Compute the needed allocation.
size_t newAlloc = M_B1TSET_TO_ALLOC (size);
if (newAlloc > v->alloc) {
// Allocate more limbs to store the bitset.
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc);
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc);
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct));
return;
M_MEMORY_FULL(m_b1tset_limb_ct, newAlloc);
}
v->ptr = ptr;
v->alloc = newAlloc;
@@ -286,9 +278,8 @@ m_bitset_resize (m_bitset_t v, size_t size)
M_B1TSET_CONTRACT (v);
}
/* Reserve allocation in the bitset to accomodate at least 'size' bits without reallocation */
static inline void
m_bitset_reserve (m_bitset_t v, size_t alloc)
/* Reserve allocation in the bitset to accommodate at least 'size' bits without reallocation */
M_P(void, m_bitset, _reserve, m_bitset_t v, size_t alloc)
{
M_B1TSET_CONTRACT (v);
size_t oldAlloc = M_B1TSET_TO_ALLOC (v->size);
@@ -299,15 +290,14 @@ m_bitset_reserve (m_bitset_t v, size_t alloc)
}
if (M_UNLIKELY (newAlloc == 0)) {
// Free all memory used by the bitsets
M_MEMORY_FREE (v->ptr);
M_MEMORY_FREE (m_context, m_b1tset_limb_ct, v->ptr, v->alloc);
v->size = v->alloc = 0;
v->ptr = NULL;
} else {
// Allocate more memory or reduce memory usage
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc);
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc);
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct));
return;
M_MEMORY_FULL(m_b1tset_limb_ct, newAlloc);
}
v->ptr = ptr;
v->alloc = newAlloc;
@@ -319,7 +309,7 @@ m_bitset_reserve (m_bitset_t v, size_t alloc)
* NOTE: Interface is a little bit different:
* It doesn't return a pointer to the data, but the data itself.
*/
static inline bool
M_INLINE bool
m_bitset_get(const m_bitset_t v, size_t i)
{
M_B1TSET_CONTRACT(v);
@@ -334,7 +324,7 @@ m_bitset_get(const m_bitset_t v, size_t i)
#define m_bitset_cget m_bitset_get
/* Pop back the last bit in the bitset */
static inline void
M_INLINE void
m_bitset_pop_back(bool *dest, m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -354,7 +344,7 @@ m_bitset_pop_back(bool *dest, m_bitset_t v)
}
/* Return the front bit value in the bitset */
static inline bool
M_INLINE bool
m_bitset_front(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -363,7 +353,7 @@ m_bitset_front(m_bitset_t v)
}
/* Return the back bit value in the bitset */
static inline bool
M_INLINE bool
m_bitset_back(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -372,7 +362,7 @@ m_bitset_back(m_bitset_t v)
}
/* Test if the bitset is empty (no bits stored)*/
static inline bool
M_INLINE bool
m_bitset_empty_p(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -380,15 +370,15 @@ m_bitset_empty_p(m_bitset_t v)
}
/* Return the number of bits of the bitset */
static inline size_t
M_INLINE size_t
m_bitset_size(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
return v->size;
}
/* Return the capacity in limbs of the bitset */
static inline size_t
/* Return the capacity in bits of the bitset */
M_INLINE size_t
m_bitset_capacity(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -396,7 +386,7 @@ m_bitset_capacity(m_bitset_t v)
}
/* Swap the bit at index i and j of the bitset */
static inline void
M_INLINE void
m_bitset_swap_at (m_bitset_t v, size_t i, size_t j)
{
M_ASSERT_INDEX(i, v->size);
@@ -409,7 +399,7 @@ m_bitset_swap_at (m_bitset_t v, size_t i, size_t j)
}
/* Swap the bitsets */
static inline void
M_INLINE void
m_bitset_swap (m_bitset_t v1, m_bitset_t v2)
{
M_B1TSET_CONTRACT (v1);
@@ -425,7 +415,7 @@ m_bitset_swap (m_bitset_t v1, m_bitset_t v2)
* integrating the carry in the lowest position.
* Return the new carry.
*/
static inline m_b1tset_limb_ct
M_INLINE m_b1tset_limb_ct
m_b1tset_lshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
{
for(size_t i = 0; i < n; i++) {
@@ -440,7 +430,7 @@ m_b1tset_lshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
* integrating the carry in the lowest position.
* Return the new carry.
*/
static inline m_b1tset_limb_ct
M_INLINE m_b1tset_limb_ct
m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
{
for(size_t i = n - 1; i < n; i--) {
@@ -453,12 +443,11 @@ m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
/* Insert a new bit at position 'key' of value 'value' in the bitset 'set'
shifting the set accordingly */
static inline void
m_bitset_push_at(m_bitset_t set, size_t key, bool value)
M_P(void, m_bitset, _push_at, m_bitset_t set, size_t key, bool value)
{
M_B1TSET_CONTRACT (set);
// First push another value to extend the array to the right size
m_bitset_push_back(set, false);
m_bitset_push_back M_R(set, false);
M_ASSERT (set->ptr != NULL);
M_ASSERT_INDEX(key, set->size);
@@ -481,7 +470,7 @@ m_bitset_push_at(m_bitset_t set, size_t key, bool value)
/* Pop a new bit at position 'key' in the bitset
* and return in *dest its value if *dest exists */
static inline void
M_INLINE void
m_bitset_pop_at(bool *dest, m_bitset_t set, size_t key)
{
M_B1TSET_CONTRACT (set);
@@ -507,14 +496,14 @@ m_bitset_pop_at(bool *dest, m_bitset_t set, size_t key)
}
/* Test if two bitsets are equal */
static inline bool
M_INLINE bool
m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
{
M_B1TSET_CONTRACT (set1);
M_B1TSET_CONTRACT (set2);
if (set1->size != set2->size)
return false;
/* We won't compare each bit individualy,
/* We won't compare each bit individually,
but instead compare them per limb */
const size_t limbSize = (set1->size + M_B1TSET_LIMB_BIT -1) / M_B1TSET_LIMB_BIT;
for(size_t i = 0 ; i < limbSize;i++)
@@ -523,8 +512,8 @@ m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
return true;
}
/* Initialize an iterator to the first bit of the biset */
static inline void
/* Initialize an iterator to the first bit of the bitset */
M_INLINE void
m_bitset_it(m_bitset_it_t it, m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -533,7 +522,7 @@ m_bitset_it(m_bitset_it_t it, m_bitset_t set)
}
/* Initialize an iterator to reference the same bit as the given one*/
static inline void
M_INLINE void
m_bitset_it_set(m_bitset_it_t it, const m_bitset_it_t itorg)
{
M_ASSERT (it != NULL && itorg != NULL);
@@ -542,7 +531,7 @@ m_bitset_it_set(m_bitset_it_t it, const m_bitset_it_t itorg)
}
/* Initialize an iterator to reference the last bit of the bitset*/
static inline void
M_INLINE void
m_bitset_it_last(m_bitset_it_t it, m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -551,7 +540,7 @@ m_bitset_it_last(m_bitset_it_t it, m_bitset_t set)
}
/* Initialize an iterator to reference no valid bit of the bitset*/
static inline void
M_INLINE void
m_bitset_it_end(m_bitset_it_t it, m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -560,7 +549,7 @@ m_bitset_it_end(m_bitset_it_t it, m_bitset_t set)
}
/* Test if an iterator references no valid bit of the bitset anymore */
static inline bool
M_INLINE bool
m_bitset_end_p(const m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -568,7 +557,7 @@ m_bitset_end_p(const m_bitset_it_t it)
}
/* Test if an iterator references the last (or end) bit of the bitset anymore */
static inline bool
M_INLINE bool
m_bitset_last_p(const m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -578,7 +567,7 @@ m_bitset_last_p(const m_bitset_it_t it)
}
/* Test if both iterators reference the same bit */
static inline bool
M_INLINE bool
m_bitset_it_equal_p(const m_bitset_it_t it1, const m_bitset_it_t it2)
{
M_ASSERT (it1 != NULL && it2 != NULL);
@@ -586,7 +575,7 @@ m_bitset_it_equal_p(const m_bitset_it_t it1, const m_bitset_it_t it2)
}
/* Move the iterator to the next bit */
static inline void
M_INLINE void
m_bitset_next(m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -594,7 +583,7 @@ m_bitset_next(m_bitset_it_t it)
}
/* Move the iterator to the previous bit */
static inline void
M_INLINE void
m_bitset_previous(m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -605,7 +594,7 @@ m_bitset_previous(m_bitset_it_t it)
/* Return a pointer to the bit referenced by the iterator
* Only one reference is possible at a time per iterator */
static inline const bool *
M_INLINE const bool *
m_bitset_cref(m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -614,7 +603,7 @@ m_bitset_cref(m_bitset_it_t it)
}
/* Output the bitset as a formatted text in a FILE */
static inline void
M_INLINE void
m_bitset_out_str(FILE *file, const m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -629,8 +618,7 @@ m_bitset_out_str(FILE *file, const m_bitset_t set)
}
/* Input the bitset from a formatted text in a FILE */
static inline bool
m_bitset_in_str(m_bitset_t set, FILE *file)
M_P(bool, m_bitset, _in_str, m_bitset_t set, FILE *file)
{
M_B1TSET_CONTRACT (set);
M_ASSERT(file != NULL);
@@ -640,7 +628,7 @@ m_bitset_in_str(m_bitset_t set, FILE *file)
c = fgetc(file);
while (c == '0' || c == '1') {
const bool b = (c == '1');
m_bitset_push_back (set, b);
m_bitset_push_back M_R(set, b);
c = fgetc(file);
}
M_B1TSET_CONTRACT (set);
@@ -648,8 +636,7 @@ m_bitset_in_str(m_bitset_t set, FILE *file)
}
/* Parse the bitset from a formatted text in a C string */
static inline bool
m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
M_P(bool, m_bitset, _parse_str, m_bitset_t set, const char str[], const char **endptr)
{
M_B1TSET_CONTRACT (set);
M_ASSERT(str != NULL);
@@ -661,7 +648,7 @@ m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
do {
if (M_UNLIKELY(c != '0' && c != '1')) goto exit;
const bool b = (c == '1');
m_bitset_push_back (set, b);
m_bitset_push_back M_R(set, b);
c = *str++;
} while (c != ']' && c != 0);
M_B1TSET_CONTRACT (set);
@@ -672,15 +659,14 @@ m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
}
/* Set the bitset from a formatted text in a C string */
static inline bool
m_bitset_set_str(m_bitset_t dest, const char str[])
M_P(bool, m_bitset, _set_str, m_bitset_t dest, const char str[])
{
return m_bitset_parse_str(dest, str, NULL);
return m_bitset_parse_str M_R(dest, str, NULL);
}
/* Perform an AND operation between the bitsets,
* up to the minimum size of both bitsets */
static inline void
M_INLINE void
m_bitset_and(m_bitset_t dest, const m_bitset_t src)
{
M_B1TSET_CONTRACT(dest);
@@ -696,7 +682,7 @@ m_bitset_and(m_bitset_t dest, const m_bitset_t src)
/* Perform an OR operation between the bitsets,
* up to the minimum size of both bitsets */
static inline void
M_INLINE void
m_bitset_or(m_bitset_t dest, const m_bitset_t src)
{
M_B1TSET_CONTRACT(dest);
@@ -712,7 +698,7 @@ m_bitset_or(m_bitset_t dest, const m_bitset_t src)
/* Perform an XOR operation between the bitsets,
* up to the minimum size of both bitsets */
static inline void
M_INLINE void
m_bitset_xor(m_bitset_t dest, const m_bitset_t src)
{
M_B1TSET_CONTRACT(dest);
@@ -733,7 +719,7 @@ m_bitset_xor(m_bitset_t dest, const m_bitset_t src)
}
/* Perform a NOT operation of the bitset */
static inline void
M_INLINE void
m_bitset_not(m_bitset_t dest)
{
M_B1TSET_CONTRACT(dest);
@@ -750,8 +736,8 @@ m_bitset_not(m_bitset_t dest)
M_B1TSET_CONTRACT(dest);
}
/* Copute a hash of the bitset */
static inline size_t
/* Compute a hash of the bitset */
M_INLINE size_t
m_bitset_hash(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -764,7 +750,7 @@ m_bitset_hash(const m_bitset_t set)
}
/* Count the number of leading zero */
static inline size_t
M_INLINE size_t
m_bitset_clz(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -792,7 +778,7 @@ m_bitset_clz(const m_bitset_t set)
}
/* Count the number of trailing zero */
static inline size_t
M_INLINE size_t
m_bitset_ctz(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -819,14 +805,14 @@ m_bitset_ctz(const m_bitset_t set)
// For GCC or CLANG or ICC
#if defined(__GNUC__)
static inline size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
{
return (size_t) __builtin_popcountll(limb);
}
#else
// MSVC __popcnt64 may not exist on the target architecture (no emulation layer)
// Use emulation layer: https://en.wikipedia.org/wiki/Hamming_weight
static inline size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
{
limb = limb - ((limb >> 1) & 0x5555555555555555ULL);
limb = (limb & 0x3333333333333333ULL) + ((limb >> 2) & 0x3333333333333333ULL);
@@ -836,7 +822,7 @@ static inline size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
#endif
/* Count the number of 1 */
static inline size_t
M_INLINE size_t
m_bitset_popcount(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -848,6 +834,7 @@ m_bitset_popcount(const m_bitset_t set)
}
/* Oplist for a bitset */
#ifndef M_USE_CONTEXT
#define M_BITSET_OPLIST \
(INIT(m_bitset_init) \
,INIT_SET(m_bitset_init_set) \
@@ -882,6 +869,42 @@ m_bitset_popcount(const m_bitset_t set)
,IN_STR(m_bitset_in_str) \
,EQUAL(m_bitset_equal_p) \
)
#else
#define M_BITSET_OPLIST \
(INIT(m_bitset_init) \
,INIT_SET(API_0P(m_bitset_init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
,SET(API_0P(m_bitset_set)) \
,CLEAR(API_0P(m_bitset_clear)) \
,INIT_MOVE(m_bitset_init_move) \
,MOVE(API_0P(m_bitset_move)) \
,SWAP(m_bitset_swap) \
,TYPE(m_bitset_t) \
,SUBTYPE(bool) \
,EMPTY_P(m_bitset_empty_p), \
,GET_SIZE(m_bitset_size) \
,IT_TYPE(m_bitset_it_t) \
,IT_FIRST(m_bitset_it) \
,IT_SET(m_bitset_it_set) \
,IT_LAST(m_bitset_it_last) \
,IT_END(m_bitset_it_end) \
,IT_END_P(m_bitset_end_p) \
,IT_LAST_P(m_bitset_last_p) \
,IT_EQUAL_P(m_bitset_it_equal_p) \
,IT_NEXT(m_bitset_next) \
,IT_PREVIOUS(m_bitset_previous) \
,IT_CREF(m_bitset_cref) \
,RESET(m_bitset_reset) \
,PUSH(API_0P(m_bitset_push_back)) \
,POP(m_bitset_pop_back) \
,HASH(m_bitset_hash) \
,GET_STR(API_0P(m_bitset_get_str)) \
,OUT_STR(m_bitset_out_str) \
,PARSE_STR(API_0P(m_bitset_parse_str)) \
,IN_STR(API_0P(m_bitset_in_str)) \
,EQUAL(m_bitset_equal_p) \
)
#endif
/* Register the OPLIST as a global one */
#define M_OPL_m_bitset_t() M_BITSET_OPLIST
@@ -961,19 +984,18 @@ M_END_PROTECTED_CODE
M_BEGIN_PROTECTED_CODE
/* Output to a m_string_t 'str' the formatted text representation of the bitset 'set'
or append it to the strinf (append=true) */
static inline void
m_bitset_get_str(m_string_t str, const m_bitset_t set, bool append)
or append it to the string (append=true) */
M_P(void, m_bitset, _get_str, m_string_t str, const m_bitset_t set, bool append)
{
M_B1TSET_CONTRACT (set);
M_ASSERT(str != NULL);
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "[");
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "[");
for(size_t i = 0; i < set->size; i++) {
const bool b = m_bitset_get (set, i);
const char c = b ? '1' : '0';
m_string_push_back (str, c);
m_string_push_back M_R(str, c);
}
m_string_push_back (str, ']');
m_string_push_back M_R(str, ']');
}
M_END_PROTECTED_CODE
+239 -187
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - B+TREE module
*
* Copyright (c) 2017-2023, Patrick Pelissier
* Copyright (c) 2017-2025, 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:
@@ -147,7 +147,7 @@
so that all arguments are evaluated before further expansion */
#define M_BPTR33_KEY_OPLIST_P1(arg) M_BPTR33_KEY_OPLIST_P2 arg
/* Validation of the given oplists */
/* Validation of the given oplist */
#define M_BPTR33_KEY_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_BPTR33_KEY_OPLIST_P3, M_BPTR33_KEY_OPLIST_FAILURE)(name, oplist)
@@ -156,6 +156,7 @@
((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* OPLIST definition of a b+tree (global tree) */
#ifndef M_USE_CONTEXT
#define M_BPTR33_KEY_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)), \
INIT_SET(M_F(name, _init_set)), \
@@ -166,7 +167,7 @@
MOVE(M_F(name, _move)), \
SWAP(M_F(name, _swap)), \
NAME(name), \
TYPE(M_F(name,_ct)), \
TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
SUBTYPE(M_F(name, _subtype_ct)), \
IT_TYPE(M_F(name, _it_ct)), \
IT_FIRST(M_F(name,_it)), \
@@ -188,11 +189,41 @@
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),), \
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_F(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),) \
)
#else
#define M_BPTR33_KEY_OPLIST_P3(name, oplist) \
(INIT(API_0P(M_F(name, _init))), \
INIT_SET(API_0P(M_F(name, _init_set))), \
INIT_WITH(API_1(M_INIT_VAI)), \
SET(API_0P(M_F(name, _set))), \
CLEAR(API_0P(M_F(name, _clear))), \
INIT_MOVE(M_F(name, _init_move)), \
MOVE(API_0P(M_F(name, _move))), \
SWAP(M_F(name, _swap)), \
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
SUBTYPE(M_F(name, _subtype_ct)), \
IT_TYPE(M_F(name, _it_ct)), \
IT_FIRST(M_F(name,_it)), \
IT_SET(M_F(name,_it_set)), \
IT_END(M_F(name,_it_end)), \
IT_END_P(M_F(name,_end_p)), \
IT_EQUAL_P(M_F(name,_it_equal_p)), \
IT_NEXT(M_F(name,_next)), \
IT_CREF(M_F(name,_cref)), \
RESET(API_0P(M_F(name,_reset))), \
PUSH(API_0P(M_F(name,_push))), \
GET_MIN(M_F(name,_min)), \
GET_MAX(M_F(name,_max)), \
M_IF_METHOD(GET_STR, oplist)(API_0P(GET_STR(M_F(name, _get_str))),), \
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),), \
M_IF_METHOD(IN_STR, oplist)(IN_STR(API_0P(M_F(name, _in_str))),), \
M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
)
#endif
/* Deferred evaluation */
#define M_BPTR33_OPLIST2_P1(arg) M_BPTR33_OPLIST2_P2 arg
@@ -208,6 +239,7 @@
((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, key_oplist, value_oplist)))
/* Final definition of the oplist (associative array) */
#ifndef M_USE_CONTEXT
#define M_BPTR33_OPLIST2_P4(name, key_oplist, value_oplist) \
(INIT(M_F(name, _init)), \
INIT_SET(M_F(name, _init_set)), \
@@ -218,7 +250,7 @@
MOVE(M_F(name, _move)), \
SWAP(M_F(name, _swap)), \
NAME(name), \
TYPE(M_F(name,_ct)), \
TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
SUBTYPE(M_F(name, _subtype_ct)), \
EMPTY_P(M_F(name,_empty_p)), \
IT_TYPE(M_F(name, _it_ct)), \
@@ -236,7 +268,7 @@
VALUE_TYPE(M_F(name, _value_ct)), \
SET_KEY(M_F(name, _set_at)), \
GET_KEY(M_F(name, _get)), \
SAFE_GET_KEY(M_F(name, _safe_get)) \
SAFE_GET_KEY(M_F(name, _safe_get)), \
ERASE_KEY(M_F(name, _erase)), \
KEY_OPLIST(key_oplist), \
VALUE_OPLIST(value_oplist), \
@@ -247,11 +279,48 @@
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_F(name, _in_serial)),), \
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_F(name, _hash)),) \
,M_IF_METHOD(NEW, key_oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(REALLOC, key_oplist)(REALLOC(M_GET_REALLOC oplist),) \
,M_IF_METHOD(DEL, key_oplist)(DEL(M_GET_DEL oplist),) \
)
#else
#define M_BPTR33_OPLIST2_P4(name, key_oplist, value_oplist) \
(INIT(API_0P(M_F(name, _init))), \
INIT_SET(API_0P(M_F(name, _init_set))), \
INIT_WITH(API_1(M_INIT_KEY_VAI)), \
SET(API_0P(M_F(name, _set))), \
CLEAR(API_0P(M_F(name, _clear))), \
INIT_MOVE(M_F(name, _init_move)), \
MOVE(API_0P(M_F(name, _move))), \
SWAP(M_F(name, _swap)), \
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
SUBTYPE(M_F(name, _subtype_ct)), \
EMPTY_P(M_F(name,_empty_p)), \
IT_TYPE(M_F(name, _it_ct)), \
IT_FIRST(M_F(name,_it)), \
IT_SET(M_F(name,_it_set)), \
IT_END(M_F(name,_it_end)), \
IT_END_P(M_F(name,_end_p)), \
IT_EQUAL_P(M_F(name,_it_equal_p)), \
IT_NEXT(M_F(name,_next)), \
IT_CREF(M_F(name,_cref)), \
RESET(API_0P(M_F(name,_reset))), \
GET_MIN(M_F(name,_min)), \
GET_MAX(M_F(name,_max)), \
KEY_TYPE(M_F(name, _key_ct)), \
VALUE_TYPE(M_F(name, _value_ct)), \
SET_KEY(API_0P(M_F(name, _set_at))), \
GET_KEY(M_F(name, _get)), \
SAFE_GET_KEY(API_0P(M_F(name, _safe_get))), \
ERASE_KEY(API_0P(M_F(name, _erase))), \
KEY_OPLIST(key_oplist), \
VALUE_OPLIST(value_oplist), \
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_F(name, _out_str)),), \
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(API_0P(M_F(name, _in_str))),), \
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_F(name, _hash)),) \
)
#endif
/******************************** INTERNAL ***********************************/
@@ -349,6 +418,7 @@
M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(M_INV(isMap), name, tree_t, key_oplist, value_oplist)
/* TODO: Check if key type has not disabled INIT_MOVE */
/* Define the types of a B+Tree */
#define M_BPTR33_DEF_TYPE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
@@ -412,28 +482,27 @@
\
/* Allocate a new node */ \
/* TODO: Can be specialized to alloc for leaf or for non leaf */ \
static inline node_t M_F(name, _new_node)(void) \
M_INLINE node_t M_F(name, _new_node)(M_P_EXPAND_void) \
{ \
M_STATIC_ASSERT(N >= 2, M_LIB_ILLEGAL_PARAM, \
"Number of items per node shall be >= 2."); \
node_t n = M_CALL_NEW(key_oplist, struct M_F(name, _node_s)); \
if (M_UNLIKELY_NOMEM (n == NULL)) { \
M_MEMORY_FULL(sizeof (node_t)); \
M_ASSERT (0); \
M_MEMORY_FULL(node_t, 1); \
} \
n->next = NULL; \
n->num = 0; \
return n; \
} \
\
static inline void M_F(name, _init)(tree_t b) \
M_P(void, name, _init, tree_t b) \
{ \
b->root = M_F(name, _new_node)(); \
b->root = M_F(name, _new_node)(M_R_EXPAND_void); \
b->size = 0; \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline bool M_F(name, _is_leaf)(const node_t n) \
M_INLINE bool M_F(name, _is_leaf)(const node_t n) \
{ \
/* We consider the empty node as a leaf */ \
/* Only the root node can be empty */ \
@@ -441,7 +510,7 @@
} \
\
/* Return the number of keys of the node */ \
static inline int M_F(name, _get_num)(const node_t n) \
M_INLINE int M_F(name, _get_num)(const node_t n) \
{ \
int num = n->num; \
num = num < 0 ? -num : num; \
@@ -449,7 +518,7 @@
return num; \
} \
\
static inline void M_F(name, _reset)(tree_t b) \
M_P(void, name, _reset, tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
node_t next, n = b->root; \
@@ -492,19 +561,19 @@
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void M_F(name, _clear)(tree_t b) \
M_P(void, name, _clear, tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
M_F(name, _reset)(b); \
M_F(name, _reset)M_R(b); \
/* Once the tree is clean, only the root remains */ \
M_CALL_DEL(key_oplist, b->root); \
b->root = NULL; \
} \
\
/* Copy recursively the node 'o' of root node 'root' */ \
static inline node_t M_F(name, _copy_node)(const node_t o, const node_t root) \
M_P(node_t, name, _copy_node, const node_t o, const node_t root) \
{ \
node_t n = M_F(name, _new_node)(); \
node_t n = M_F(name, _new_node)(M_R_EXPAND_void); \
/* Set default number of keys and type to copy */ \
n->num = o->num; \
/* By default it is not linked to its brother. \
@@ -526,7 +595,7 @@
/* Copy recursively the associated nodes if it is not a leaf */ \
for(int i = 0; i <= num; i++) { \
M_ASSERT(o->kind.node[i] != root); \
n->kind.node[i] = M_F(name, _copy_node)(o->kind.node[i], root); \
n->kind.node[i] = M_F(name, _copy_node)M_R(o->kind.node[i], root); \
} \
/* The copied nodes don't have their next field correct */ \
/* Fix the next field for the copied nodes */ \
@@ -548,38 +617,38 @@
return n; \
} \
\
static inline void M_F(name, _init_set)(tree_t b, const tree_t o) \
M_P(void, name, _init_set, tree_t b, const tree_t o) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, o); \
M_ASSERT (b != NULL); \
/* Just copy recursively the root node */ \
b->root = M_F(name, _copy_node)(o->root, o->root); \
b->root = M_F(name, _copy_node)M_R(o->root, o->root); \
b->size = o->size; \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void M_F(name, _set)(tree_t b, const tree_t o) \
M_P(void, name, _set, tree_t b, const tree_t o) \
{ \
/* NOTE: We could reuse the already allocated nodes of 'b'. \
Not sure if it worth the effort */ \
M_F(name, _clear)(b); \
M_F(name, _init_set)(b, o); \
M_F(name, _clear)M_R(b); \
M_F(name, _init_set)M_R(b, o); \
} \
\
static inline bool M_F(name, _empty_p)(const tree_t b) \
M_INLINE bool M_F(name, _empty_p)(const tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
/* root shall be an empty leaf */ \
return b->size == 0; \
} \
\
static inline size_t M_F(name, _size)(const tree_t b) \
M_INLINE size_t M_F(name, _size)(const tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
return b->size; \
} \
\
static inline node_t M_F(name, _search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
M_INLINE node_t M_F(name, _i_search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
{ \
node_t n = b->root; \
int np = 0; \
@@ -608,12 +677,12 @@
return n; \
} \
\
static inline value_t *M_F(name, _get)(const tree_t b, key_t const key) \
M_INLINE value_t *M_F(name, _get)(const tree_t b, key_t const key) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
pit_t pit; \
/* Get the leaf node where the key can be */ \
node_t n = M_F(name, _search_for_leaf)(pit, b, key); \
node_t n = M_F(name, _i_search_for_leaf)(pit, b, key); \
int cmp = 0; \
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
/* Search in the leaf for key */ \
@@ -628,16 +697,16 @@
return NULL; \
} \
\
static inline value_t const *M_F(name, _cget)(const tree_t b, key_t const key) \
M_INLINE value_t const *M_F(name, _cget)(const tree_t b, key_t const key) \
{ \
return M_CONST_CAST(value_t, M_F(name, _get)(b, key)); \
} \
\
static inline int \
M_F(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
M_P(int, name, _i_search_and_insert_in_leaf, node_t n, key_t const key \
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
{ \
M_ASSERT (M_F(name, _is_leaf)(n)); \
M_UNUSED_CONTEXT(); \
int i, num = M_F(name, _get_num)(n); \
M_ASSERT (num <= N); \
/* Search for the key in the node n (a leaf) for insertion */ \
@@ -665,10 +734,10 @@
return i; \
} \
\
static inline int \
M_F(name, _search_and_insert_in_node)(node_t n, node_t l, key_t key) \
M_P(int, name, _i_search_and_insert_in_node, node_t n, node_t l, key_t key) \
{ \
M_ASSERT (!M_F(name, _is_leaf)(n)); \
M_UNUSED_CONTEXT(); \
int i, num = M_F(name, _get_num)(n); \
M_ASSERT (num <= N); \
/* Search for the key in the node n (not a leaf) for insertion */ \
@@ -688,16 +757,16 @@
return i; \
} \
\
static inline void \
M_IF(isMap)(M_F(name, _set_at),M_F(name,_push))(tree_t b, key_t const key \
M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
M_IF(isMap)( \
M_P(void, name, _set_at, tree_t b, key_t const key, value_t const value) , \
M_P(void, name, _push, tree_t b, key_t const key) ) \
{ \
pit_t pit; \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \
node_t leaf = M_F(name, _i_search_for_leaf)(pit, b, key); \
/* Insert key into the leaf.*/ \
/* NOTE: Even if there is N elements, we can still add one more.*/ \
int i = M_F(name, _search_and_insert_in_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
int i = M_F(name, _i_search_and_insert_in_leaf)M_R(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
if (i < 0) { \
/* Nothing to do anymore. key already exists in the tree. \
value has been updated if needed */ \
@@ -719,7 +788,7 @@
/* leaf is full: need to slip the leaf in two */ \
int nnum = (N + 1) / 2; \
num = N + 1 - nnum; \
node_t nleaf = M_F(name, _new_node)(); \
node_t nleaf = M_F(name, _new_node)(M_R_EXPAND_void); \
/* Move half objects to the new node */ \
memmove(&nleaf->key[0], &leaf->key[num], sizeof(key_t)*(unsigned int)nnum); \
M_IF(isMap)(memmove(&nleaf->kind.value[0], &leaf->kind.value[num], sizeof(value_t)*(unsigned int)nnum);,) \
@@ -734,7 +803,7 @@
while (true) { \
if (pit->num == 0) { \
/* We reach root ==> Need to increase the height of the tree.*/ \
node_t parent = M_F(name, _new_node)(); \
node_t parent = M_F(name, _new_node)(M_R_EXPAND_void); \
parent->num = 1; \
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
@@ -747,7 +816,7 @@
/* Non root node. Get the parent node */ \
node_t parent = pit->parent[--pit->num]; \
/* Insert into parent (It is big enough to receive temporary one more) */ \
i = M_F(name, _search_and_insert_in_node)(parent, leaf, *key_ptr); \
i = M_F(name, _i_search_and_insert_in_node)M_R(parent, leaf, *key_ptr); \
parent->kind.node[i] = leaf; \
parent->kind.node[i+1] = nleaf; \
/* Test if parent node is full? */ \
@@ -760,7 +829,7 @@
int nnp = N / 2; \
int np = N - nnp; \
M_ASSERT (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
node_t nparent = M_F(name, _new_node)(); \
node_t nparent = M_F(name, _new_node)(M_R_EXPAND_void); \
/* Move half items to new node (Like a classic B-TREE) \
and the median key to the grand-parent*/ \
memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*(unsigned int)nnp); \
@@ -778,7 +847,7 @@
} \
} \
\
static inline value_t *M_F(name, _safe_get)(tree_t b, key_t const key) \
M_P(value_t *, name, _safe_get, tree_t b, key_t const key) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
/* Not optimized implementation */ \
@@ -787,7 +856,7 @@
M_IF(isMap)( \
value_t v; \
M_CALL_INIT(value_oplist, v); \
M_F(name, _set_at)(b, key, v); \
M_F(name, _set_at)M_R(b, key, v); \
M_CALL_CLEAR(value_oplist, v); \
, \
M_F(name, _push)(b, key); \
@@ -797,10 +866,10 @@
return ret; \
} \
\
static inline int \
M_F(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \
M_P(int, name, _i_search_and_remove_in_leaf, node_t n, key_t const key) \
{ \
M_ASSERT(M_F(name, _is_leaf)(n)); \
M_UNUSED_CONTEXT(); \
const int num = M_F(name, _get_num)(n); \
for(int i = 0; i < num; i++) { \
const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
@@ -817,10 +886,11 @@
return -1; /* Not found */ \
} \
\
static inline void M_F(name, _left_shift)(node_t parent, int k) \
M_P(void, name, _i_left_shift, node_t parent, int k) \
{ \
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \
M_UNUSED_CONTEXT(); \
node_t left = parent->kind.node[k]; \
node_t right = parent->kind.node[k+1]; \
M_ASSERT (left != NULL && right != NULL); \
@@ -852,10 +922,11 @@
M_ASSERT (left->num != 0); \
} \
\
static inline void M_F(name, _right_shift)(node_t parent, int k) \
M_P(void, name, _i_right_shift, node_t parent, int k) \
{ \
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \
M_UNUSED_CONTEXT(); \
node_t left = parent->kind.node[k]; \
node_t right = parent->kind.node[k+1]; \
M_ASSERT (left != NULL && right != NULL); \
@@ -886,7 +957,7 @@
M_ASSERT (left->num != 0); \
} \
\
static inline void M_F(name, _merge_node)(node_t parent, int k, bool leaf) \
M_P(void, name, _i_merge_node, node_t parent, int k, bool leaf) \
{ \
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
M_ASSERT (0 <= k && k < M_F(name, _get_num(parent))); \
@@ -921,8 +992,8 @@
\
/* We can also cache the index when we descend the tree. \
TODO: Bench if this is worth the effort.*/ \
static inline int \
M_F(name, _search_for_node)(node_t parent, node_t child) \
M_INLINE int \
M_F(name, _i_search_for_node)(node_t parent, node_t child) \
{ \
M_ASSERT (!M_F(name, _is_leaf)(parent)); \
int i = 0; \
@@ -934,12 +1005,12 @@
} \
} \
\
static inline bool M_F(name, _erase)(tree_t b, key_t const key) \
M_P(bool, name, _erase, tree_t b, key_t const key) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
pit_t pit; \
node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \
int k = M_F(name, _search_and_remove_in_leaf)(leaf, key); \
node_t leaf = M_F(name, _i_search_for_leaf)(pit, b, key); \
int k = M_F(name, _i_search_and_remove_in_leaf)M_R(leaf, key); \
/* If key is not found ==> erase failed */ \
if (k < 0) return false; \
/* Remove one item from the B+TREE */ \
@@ -955,15 +1026,15 @@
/* Search for node 'leaf' in parent */ \
node_t parent = pit->parent[--pit->num]; \
M_ASSERT (parent != NULL); \
k = M_F(name, _search_for_node)(parent, leaf); \
/* Look for the neighboor of the removed key. */ \
k = M_F(name, _i_search_for_node)(parent, leaf); \
/* Look for the neighbour of the removed key. */ \
/* if we can steal one key from them to keep our node balanced */ \
if (k > 0 && M_F(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
M_F(name, _left_shift)(parent, k-1); \
M_F(name, _i_left_shift)M_R(parent, k-1); \
return true; \
} else if (k < M_F(name, _get_num)(parent) \
&& M_F(name, _get_num)(parent->kind.node[k+1]) > N/2) { \
M_F(name, _right_shift)(parent, k); \
M_F(name, _i_right_shift)M_R(parent, k); \
return true; \
} \
/* Merge both nodes, removing 'k' from parent */ \
@@ -971,7 +1042,7 @@
k--; \
M_ASSERT(k >= 0 && k < M_F(name, _get_num)(parent)); \
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
M_F(name, _merge_node)(parent, k, pass1); \
M_F(name, _i_merge_node)M_R(parent, k, pass1); \
/* Check if we need to continue */ \
if (M_F(name, _get_num)(parent) >= N/2) \
return true; \
@@ -990,19 +1061,20 @@
} \
} \
\
static inline bool M_F(name, _pop_at)(value_t *ptr, tree_t b, key_t const key) \
M_P(bool, name, _pop_at, value_t *ptr, tree_t b, key_t const key) \
{ \
if (ptr != NULL) { \
/* Not optimized tree parsing */ \
value_t *ref = M_F(name, _get)(b, key); \
if (ref == NULL) { \
return false; \
} \
M_CALL_SET(value_oplist, *ptr, *ref); \
} \
return M_F(name, _erase)(b, key); \
return M_F(name, _erase)M_R(b, key); \
} \
\
static inline value_t * \
M_INLINE value_t * \
M_F(name, _min)(const tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
@@ -1015,7 +1087,7 @@
return &n->M_IF(isMap)(kind.value, key)[0]; \
} \
\
static inline value_t * \
M_INLINE value_t * \
M_F(name, _max)(const tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
@@ -1028,19 +1100,19 @@
return &n->M_IF(isMap)(kind.value, key)[-n->num-1]; \
} \
\
static inline value_t const * \
M_INLINE value_t const * \
M_F(name, _cmin)(const tree_t tree) \
{ \
return M_CONST_CAST(value_t, M_F(name, _min)(tree)); \
} \
\
static inline value_t const * \
M_INLINE value_t const * \
M_F(name, _cmax)(const tree_t tree) \
{ \
return M_CONST_CAST(value_t, M_F(name, _max)(tree)); \
} \
\
static inline void \
M_INLINE void \
M_F(name, _init_move)(tree_t b, tree_t ref) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \
@@ -1048,21 +1120,21 @@
b->size = ref->size; \
b->root = ref->root; \
ref->root = NULL; \
ref->size = 1; \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void \
M_F(name, _move)(tree_t b, tree_t ref) \
M_P(void, name, _move, tree_t b, tree_t ref) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \
M_ASSERT (b != ref); \
M_F(name,_clear)(b); \
M_F(name,_clear)M_R(b); \
M_F(name,_init_move)(b, ref); \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
static inline void \
M_INLINE void \
M_F(name, _swap)(tree_t tree1, tree_t tree2) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree1); \
@@ -1076,7 +1148,7 @@
/* Define iterator functions. */
#define M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
\
static inline void \
M_INLINE void \
M_F(name, _it)(it_t it, const tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
@@ -1090,7 +1162,7 @@
it->idx = 0; \
} \
\
static inline void \
M_INLINE void \
M_F(name, _it_end)(it_t it, const tree_t b) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
@@ -1104,7 +1176,7 @@
it->idx = -n->num; \
} \
\
static inline void \
M_INLINE void \
M_F(name, _it_set)(it_t itd, const it_t its) \
{ \
M_ASSERT (itd != NULL && its != NULL); \
@@ -1112,7 +1184,7 @@
itd->idx = its->idx; \
} \
\
static inline bool \
M_INLINE bool \
M_F(name, _end_p)(it_t it) \
{ \
M_ASSERT (it != NULL && it->node != NULL); \
@@ -1120,13 +1192,13 @@
return it->node->next ==NULL && it->idx >= -it->node->num; \
} \
\
static inline bool \
M_INLINE bool \
M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \
{ \
return it1->node == it2->node && it1->idx == it2->idx; \
} \
\
static inline void \
M_INLINE void \
M_F(name, _next)(it_t it) \
{ \
M_ASSERT (it != NULL && it->node != NULL); \
@@ -1138,7 +1210,7 @@
} \
} \
\
static inline subtype_t * \
M_INLINE subtype_t * \
M_F(name, _ref)(it_t it) \
{ \
M_ASSERT (it != NULL && it->node != NULL); \
@@ -1153,20 +1225,20 @@
); \
} \
\
static inline subtype_t const * \
M_INLINE subtype_t const * \
M_F(name, _cref)(it_t it) \
{ \
return M_CONST_CAST(subtype_t, M_F(name, _ref)(it)); \
} \
\
\
static inline void \
M_INLINE void \
M_F(name, _it_from)(it_t it, const tree_t b, key_t const key) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
M_ASSERT (it != NULL); \
pit_t pit; \
node_t n = M_F(name, _search_for_leaf)(pit, b, key); \
node_t n = M_F(name, _i_search_for_leaf)(pit, b, key); \
it->node = n; \
int i; \
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
@@ -1181,7 +1253,7 @@
it->idx = i; \
} \
\
static inline bool \
M_INLINE bool \
M_F(name, _it_until_p)(it_t it, key_t const key) \
{ \
M_ASSERT (it != NULL); \
@@ -1191,7 +1263,7 @@
return (cmp >= 0); \
} \
\
static inline bool \
M_INLINE bool \
M_F(name, _it_while_p)(it_t it, key_t const key) \
{ \
M_ASSERT (it != NULL); \
@@ -1206,7 +1278,7 @@
#define M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
\
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)( \
static inline bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \
M_INLINE bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t2); \
if (t1->size != t2->size) return false; \
@@ -1215,7 +1287,7 @@
it_t it1; \
it_t it2; \
/* NOTE: We can't compare two trees directly as they can be \
structuraly different but functionnaly equal (you get this by \
structurally different but functionally equal (you get this by \
constructing the tree in a different way). We have to \
compare the ordered value within the tree. */ \
M_F(name, _it)(it1, t1); \
@@ -1242,7 +1314,7 @@
, /* NO EQUAL METHOD */ ) \
\
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)( \
static inline size_t M_F(name,_hash)(const tree_t t1) { \
M_INLINE size_t M_F(name,_hash)(const tree_t t1) { \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_HASH_DECL(hash); \
/* NOTE: We can't compute the hash directly for the same reason \
@@ -1264,35 +1336,34 @@
, /* NO HASH METHOD */ ) \
\
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
static inline void M_F(name, _get_str)(m_string_t str, \
const tree_t t1, bool append) { \
M_P(void, name, _get_str, m_string_t str, const tree_t t1, bool append) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_ASSERT(str != NULL); \
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
/* NOTE: The print is really naive, and not really efficient */ \
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
bool commaToPrint = false; \
it_t it; \
for (M_F(name, _it)(it, t1) ; \
!M_F(name, _end_p)(it); \
M_F(name, _next)(it)) { \
if (commaToPrint) \
m_string_push_back (str, M_GET_SEPARATOR key_oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR key_oplist); \
commaToPrint = true; \
subtype_t const *ref1 = M_F(name, _cref)(it); \
M_IF(isMap)( \
M_CALL_GET_STR(key_oplist, str, *ref1->key_ptr, true); \
m_string_cat_cstr(str, ":"); \
m_string_cat_cstr M_R(str, ":"); \
M_CALL_GET_STR(value_oplist,str, *ref1->value_ptr, true) \
, \
M_CALL_GET_STR(key_oplist, str, *ref1, true); \
); \
} \
m_string_push_back (str, ']'); \
m_string_push_back M_R(str, ']'); \
} \
, /* NO GET_STR */ ) \
\
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)( \
static inline void \
M_INLINE void \
M_F(name, _out_str)(FILE *file, tree_t const t1) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
@@ -1320,88 +1391,74 @@
, /* no out_str */ ) \
\
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)( \
static inline bool \
M_F(name, _parse_str)(tree_t t1, const char str[], const char **endp) \
M_P(bool, name, _parse_str, tree_t t1, const char str[], const char **endp) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_ASSERT (str != NULL); \
M_F(name,_reset)(t1); \
bool success = false; \
M_F(name,_reset)M_R(t1); \
int c = *str++; \
if (M_UNLIKELY (c != '[')) goto exit; \
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
c = *str++; \
if (M_UNLIKELY (c == ']')) { success = true; goto exit;} \
if (M_UNLIKELY (c == 0)) goto exit; \
if (M_UNLIKELY (c == ']' || c == 0)) goto exit; \
str--; \
key_t key; \
M_CALL_INIT(key_oplist, key); \
M_IF(isMap)(value_t value; \
M_CALL_INIT(value_oplist, value); \
, /* No isMap */) \
do { \
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
do { c = *str++; } while (isspace(c)); \
if (b == false) goto exit_clear; \
M_IF(isMap)(if (c != ':') goto exit_clear; \
b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \
do { c = *str++; } while (isspace(c)); \
if (b == false || c == 0) goto exit_clear; \
M_F(name, _set_at)(t1, key, value); \
, \
M_F(name, _push)(t1, key); \
) \
} while (c == M_GET_SEPARATOR key_oplist); \
success = (c == ']'); \
exit_clear: \
M_CALL_CLEAR(key_oplist, key); \
M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); , /* No isMap */ ) \
M_QLET(1, key, key_t, key_oplist) \
M_IF(isMap)(M_QLET(2, value, value_t, value_oplist), /* No isMap */) { \
do { \
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
c = m_core_str_nospace(&str); \
if (b == false) { c = 0; break; } \
M_IF(isMap)(if (c != ':') { c = 0; break; } \
b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \
c = m_core_str_nospace(&str); \
if (b == false || c == 0) { c = 0; break; } \
M_F(name, _set_at)M_R(t1, key, value); \
, \
M_F(name, _push)M_R(t1, key); \
) \
} while (c == M_GET_SEPARATOR key_oplist); \
} \
exit: \
if (endp) *endp = str; \
return success; \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
return (c == ']'); \
} \
, /* no parse_str */ ) \
\
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \
static inline bool \
M_F(name, _in_str)(tree_t t1, FILE *file) \
M_P(bool, name, _in_str, tree_t t1, FILE *file) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_ASSERT (file != NULL); \
M_F(name,_reset)(t1); \
M_F(name,_reset)M_R(t1); \
int c = fgetc(file); \
if (M_UNLIKELY (c != '[')) return false; \
c = fgetc(file); \
if (M_UNLIKELY (c == ']')) return true; \
if (M_UNLIKELY (c == EOF)) return false; \
ungetc(c, file); \
key_t key; \
M_CALL_INIT (key_oplist, key); \
M_IF(isMap)(value_t value; \
M_CALL_INIT (value_oplist, value); \
,) \
do { \
bool b = M_CALL_IN_STR(key_oplist, key, file); \
do { c = fgetc(file); } while (isspace(c)); \
if (b == false) break; \
M_IF(isMap)(if (c!=':') break; \
b = M_CALL_IN_STR(value_oplist,value, file); \
do { c = fgetc(file); } while (isspace(c)); \
if (b == false || c == EOF) break; \
M_F(name, _set_at)(t1, key, value) \
, \
M_F(name, _push)(t1, key) \
); \
} while (c == M_GET_SEPARATOR key_oplist); \
M_CALL_CLEAR(key_oplist, key); \
M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); \
,) \
M_QLET(1, key, key_t, key_oplist) \
M_IF(isMap)(M_QLET(2, value, value_t, value_oplist) , /*nothing*/) { \
do { \
bool b = M_CALL_IN_STR(key_oplist, key, file); \
c = m_core_fgetc_nospace(file); \
if (b == false) { c = 0; break; } \
M_IF(isMap)(if (c!=':') { c = 0; break; } \
b = M_CALL_IN_STR(value_oplist,value, file); \
c = m_core_fgetc_nospace(file); \
if (b == false || c == EOF) { c = 0; break; } \
M_F(name, _set_at)M_R(t1, key, value) \
, \
M_F(name, _push)M_R(t1, key) \
); \
} while (c == M_GET_SEPARATOR key_oplist); \
} \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
return c == ']'; \
} \
, /* no in_str */ ) \
\
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
static inline m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, tree_t const t1) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
@@ -1413,76 +1470,71 @@
/* Format is different between associative container \
& set container */ \
M_IF(isMap)( \
ret = f->m_interface->write_map_start(local, f, t1->size); \
ret = f->m_interface->write_map_start M_R(local, f, t1->size); \
for (M_F(name, _it)(it, t1) ; \
!M_F(name, _end_p)(it); \
M_F(name, _next)(it)){ \
item = M_F(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_map_next(local, f); \
ret |= f->m_interface->write_map_next M_R(local, f); \
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item->key_ptr); \
ret |= f->m_interface->write_map_value(local, f); \
ret |= f->m_interface->write_map_value M_R(local, f); \
ret |= M_CALL_OUT_SERIAL(value_oplist, f, *item->value_ptr); \
first_done = true; \
} \
ret |= f->m_interface->write_map_end(local, f); \
ret |= f->m_interface->write_map_end M_R(local, f); \
, \
ret = f->m_interface->write_array_start(local, f, t1->size); \
ret = f->m_interface->write_array_start M_R(local, f, t1->size); \
for (M_F(name, _it)(it, t1) ; \
!M_F(name, _end_p)(it); \
M_F(name, _next)(it)){ \
item = M_F(name, _cref)(it); \
if (first_done) \
ret |= f->m_interface->write_array_next(local, f); \
ret |= f->m_interface->write_array_next M_R(local, f); \
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \
first_done = true; \
} \
ret |= f->m_interface->write_array_end(local, f); \
ret |= f->m_interface->write_array_end M_R(local, f); \
) \
return ret & M_SERIAL_FAIL; \
} \
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
static inline m_serial_return_code_t \
M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \
M_P(m_serial_return_code_t, name, _in_serial, tree_t t1, m_serial_read_t f) \
{ \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
key_t key; \
M_F(name,_reset)(t1); \
M_IF(isMap)( \
value_t value; \
M_F(name,_reset)M_R(t1); \
M_QLET(1, key, key_t, key_oplist) \
M_IF(isMap)(M_QLET(2, value, value_t, value_oplist) , /*nothing*/) { \
M_IF(isMap)( \
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(key_oplist, key); \
M_CALL_INIT (value_oplist, value); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) break; \
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
if (ret != M_SERIAL_OK_DONE) break; \
ret = f->m_interface->read_map_value(local, f); \
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
if (ret != M_SERIAL_OK_CONTINUE) break; \
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
M_F(name, _set_at)(t1, key, value); \
if (ret != M_SERIAL_OK_DONE) break; \
M_F(name, _set_at)M_R(t1, key, value); \
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(key_oplist, key); \
M_CALL_CLEAR(value_oplist, value); \
, \
, /* queue style */ \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(key_oplist, key); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) break; \
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_F(name, _push)(t1, key); \
if (ret != M_SERIAL_OK_DONE) break; \
M_F(name, _push)M_R(t1, key); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(key_oplist, key); \
) /* End of IF isMap */ \
return ret; \
) /* End of IF isMap */ \
} \
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
return ret; \
} \
, /* no in_serial */ ) \
+610
View File
@@ -0,0 +1,610 @@
/*
* M*LIB - Byte String module
*
* Copyright (c) 2017-2025, 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_BSTRING_H
#define MSTARLIB_BSTRING_H
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
/* Byte String Type definition*/
typedef struct m_bstring_s {
size_t size; // Number of effective BYTES
size_t alloc; // Size of the allocated buffer
size_t offset; // Offset where the effective BYTES are stored in the allocated buffer
uint8_t *ptr; // Pointer to the allocated buffer
} m_bstring_t[1];
// Contract of a bstring
#define M_BSTRING_CONTRACT(o) do { \
M_ASSERT( (o)->size + (o)->offset >= (o)->size ); \
M_ASSERT( (o)->alloc >= (o)->size + (o)->offset ); \
M_ASSERT( (o)->ptr != NULL || (o)->alloc == 0); \
} while (0)
M_INLINE size_t
m_bstring_size(const m_bstring_t s)
{
M_BSTRING_CONTRACT(s);
return s->size;
}
M_INLINE size_t
m_bstring_capacity(const m_bstring_t s)
{
M_BSTRING_CONTRACT(s);
return s->alloc;
}
M_INLINE uint8_t *
m_bstr1ng_cstr(const m_bstring_t v)
{
return &v->ptr[v->offset];
}
M_INLINE void
m_bstring_init(m_bstring_t s)
{
s->size = 0;
s->alloc = 0;
s->offset = 0;
s->ptr = NULL;
M_BSTRING_CONTRACT(s);
}
M_P(void, m_bstring, _clear, m_bstring_t s)
{
M_BSTRING_CONTRACT(s);
M_MEMORY_FREE(m_context, uint8_t, s->ptr, s->alloc);
s->offset = (size_t) -1;
}
M_INLINE void
m_bstring_reset(m_bstring_t s)
{
M_BSTRING_CONTRACT(s);
s->size = 0;
s->offset = 0;
M_BSTRING_CONTRACT(s);
}
M_INLINE void
m_bstring_swap(m_bstring_t s1, m_bstring_t s2)
{
M_BSTRING_CONTRACT(s1);
M_BSTRING_CONTRACT(s2);
m_bstring_t tmp;
memcpy(tmp, s1, sizeof (m_bstring_t));
memcpy(s1, s2, sizeof (m_bstring_t));
memcpy(s2, tmp, sizeof (m_bstring_t));
M_BSTRING_CONTRACT(s1);
M_BSTRING_CONTRACT(s2);
}
/* Return the selected byte-character of the string */
M_INLINE uint8_t
m_bstring_get_byte(const m_bstring_t v, size_t index)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX(index, v->size);
return m_bstr1ng_cstr(v)[index];
}
/* Set the selected byte-character of the string */
M_INLINE void
m_bstring_set_byte(m_bstring_t v, size_t index, const uint8_t c)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX(index, v->size);
m_bstr1ng_cstr(v)[index] = c;
}
/* Test if the string is empty or not */
M_INLINE bool
m_bstring_empty_p(const m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
return v->size == 0;
}
/* Internal method to fit the Byte string to the given size
Ensures that the Byte String capacity is greater than size_alloc+offset
The returned Byte String 'v' no longer has a **correct** size field.
If exact_alloc is true, it will request exactly the request size for the buffer
on reallocation otherwise it will augment the requested size to amortize the future
allocations.
Return a pointer to the writable string.
*/
M_P(uint8_t *, m_bstr1ng, _fit2size, m_bstring_t v, size_t size_alloc, bool exact_alloc)
{
// Very unlikely overflow case
if (M_UNLIKELY_NOMEM (v->offset > v->offset + size_alloc)) {
goto allocation_error;
}
M_ASSERT_INDEX (v->offset, v->offset + size_alloc);
// Note: this function may be called in context where the contract is not fulfilled.
if (M_UNLIKELY (v->offset + size_alloc > v->alloc)) {
// Insufficient current allocation to store the new string
// Check if there is a too big offset that can be reduced
if (v->offset > v->size / 8 && v->offset > 16) {
// Move back the effective bytes to the beginning of the buffer
memmove(&v->ptr[0], &v->ptr[v->offset], v->size);
v->offset = 0;
// If it is now sufficient, we can return.
if (size_alloc <= v->alloc)
return v->ptr;
}
// Alloc a new array. Compute the requested allocation size
size_t alloc = (v->offset + size_alloc + (exact_alloc ? 0 : size_alloc / 2));
alloc = M_MAX(alloc, 16);
if (M_UNLIKELY_NOMEM (alloc < size_alloc)) {
goto allocation_error;
}
uint8_t *ptr = M_MEMORY_REALLOC (m_context, uint8_t, v->ptr, v->alloc, alloc);
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
goto allocation_error;
}
// The string cannot be fully embedded anymore.
v->ptr = ptr;
v->alloc = alloc;
// Size is not set as the function is called in context
// where the caller already know the final size of the string,
// so it will update it.
return &v->ptr[v->offset];
}
return &v->ptr[v->offset];
allocation_error:
M_MEMORY_FULL(uint8_t, size_alloc);
}
/* Push the byte 'c' in the bstring 'v' */
M_P(void, m_bstring, _push_back, m_bstring_t v, uint8_t c)
{
M_BSTRING_CONTRACT (v);
const size_t size = v->size;
// To overflow size+1, we need to have exhausted all memory
// which is not possible.
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, size+1, false);
ptr[size] = c;
v->size = size + 1;
M_BSTRING_CONTRACT (v);
}
/* Push the array of bytes from 'buffer' in the bstring 'v' */
M_P(void, m_bstring, _push_back_bytes, m_bstring_t v, size_t n, const void *buffer)
{
M_BSTRING_CONTRACT (v);
const size_t size = v->size;
M_ASSERT_INDEX (size, size + n);
// If size+n overflows, it means we are giving two objects which sizes
// sum to be greater than the available memory. The memory should have exhausted before that.
// The case is not possible, or the given size of the buffer is "faked".
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, size+n, false);
memcpy(&ptr[size], buffer, n);
v->size = size + n;
M_BSTRING_CONTRACT (v);
}
M_P(void, m_bstring, _splice, m_bstring_t dst, m_bstring_t src)
{
M_BSTRING_CONTRACT (dst);
M_BSTRING_CONTRACT (src);
M_ASSERT(src != dst);
m_bstring_push_back_bytes M_R(dst, m_bstring_size(src), m_bstr1ng_cstr(src));
m_bstring_reset(src);
}
M_P(void, m_bstring, _init_set, m_bstring_t v, const m_bstring_t org)
{
m_bstring_init(v);
m_bstring_push_back_bytes M_R(v, m_bstring_size(org), m_bstr1ng_cstr(org));
}
M_P(void, m_bstring, _set, m_bstring_t v, const m_bstring_t org)
{
m_bstring_reset(v);
m_bstring_push_back_bytes M_R(v, m_bstring_size(org), m_bstr1ng_cstr(org));
}
M_INLINE void
m_bstring_init_move(m_bstring_t v, m_bstring_t org)
{
M_BSTRING_CONTRACT (org);
memcpy(v, org, sizeof (m_bstring_t));
// Make org an invalid representation
org->offset = (size_t) -1;
}
M_P(void, m_bstring, _move, m_bstring_t v, m_bstring_t org)
{
m_bstring_clear M_R(v);
m_bstring_init_move(v, org);
}
M_INLINE size_t
m_bstring_hash(const m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
return m_core_hash(m_bstr1ng_cstr(v), m_bstring_size(v));
}
M_INLINE int
m_bstring_cmp(const m_bstring_t v1, const m_bstring_t v2)
{
M_BSTRING_CONTRACT (v1);
M_BSTRING_CONTRACT (v2);
const size_t s1 = m_bstring_size(v1);
const size_t s2 = m_bstring_size(v2);
int ret = memcmp(m_bstr1ng_cstr(v1), m_bstr1ng_cstr(v2), M_MIN(s1, s2));
if (ret)
return ret;
if (s1 != s2)
return s1 < s2 ? -1 : 1;
return 0;
}
M_INLINE int
m_bstring_cmp_bytes(const m_bstring_t v1, size_t s2, const void *p2)
{
M_BSTRING_CONTRACT (v1);
const size_t s1 = m_bstring_size(v1);
int ret = memcmp(m_bstr1ng_cstr(v1), p2, M_MIN(s1, s2));
if (ret)
return ret;
if (s1 != s2)
return s1 < s2 ? -1 : 1;
return 0;
}
M_INLINE bool
m_bstring_equal_p(const m_bstring_t v1, const m_bstring_t v2)
{
M_BSTRING_CONTRACT (v1);
M_BSTRING_CONTRACT (v2);
const size_t s1 = m_bstring_size(v1);
const size_t s2 = m_bstring_size(v2);
if (s1 != s2)
return false;
return memcmp(m_bstr1ng_cstr(v1), m_bstr1ng_cstr(v2), s1) == 0;
}
M_INLINE bool
m_bstring_equal_bytes_p(const m_bstring_t v1, size_t s2, const void *buffer)
{
M_BSTRING_CONTRACT (v1);
const size_t s1 = m_bstring_size(v1);
if (s1 != s2)
return false;
return memcmp(m_bstr1ng_cstr(v1), buffer, s1) == 0;
}
M_INLINE uint8_t
m_bstring_pop_back(m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX(0, v->size);
uint8_t c = m_bstr1ng_cstr(v)[v->size-1];
v->size --;
M_BSTRING_CONTRACT (v);
return c;
}
M_INLINE void
m_bstring_pop_back_bytes(size_t n, void *buffer, m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX(n-1, v->size);
memcpy(buffer, &m_bstr1ng_cstr(v)[v->size-n], n);
v->size -= n;
M_BSTRING_CONTRACT (v);
}
M_INLINE uint8_t
m_bstring_pop_front(m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX(0, v->size);
uint8_t c = m_bstr1ng_cstr(v)[0];
v->size --;
v->offset++;
M_BSTRING_CONTRACT (v);
return c;
}
M_INLINE void
m_bstring_pop_front_bytes(size_t n, void *buffer, m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX(n-1, v->size);
memcpy(buffer, &m_bstr1ng_cstr(v)[0], n);
v->size -= n;
v->offset += n;
M_BSTRING_CONTRACT (v);
}
M_P(void, m_bstring, _push_bytes_at, m_bstring_t v, size_t pos, size_t n, const void *buffer)
{
M_BSTRING_CONTRACT (v);
const size_t size = v->size;
M_ASSERT_INDEX (pos, size+1); //pos == size ==> equivalent to _push_back_bytes
M_ASSERT_INDEX (size, size + n);
// If size+n overflows, it means we are giving two objects which sizes
// sum to be greater than the available memory. The memory should have exhausted before that.
// The case is not possible.
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, size+n, false);
memmove(&ptr[pos+n], &ptr[pos], size-pos);
memcpy(&ptr[pos], buffer, n);
v->size = size + n;
M_BSTRING_CONTRACT (v);
}
M_INLINE void
m_bstring_pop_bytes_at(size_t n, void *buffer, m_bstring_t v, size_t pos)
{
M_BSTRING_CONTRACT (v);
const size_t size = v->size;
M_ASSERT_INDEX (pos, size);
M_ASSERT_INDEX(n-1, size-pos);
uint8_t *ptr = m_bstr1ng_cstr(v);
memcpy(buffer, &ptr[pos], n);
memmove(&ptr[pos], &ptr[pos+n], size-pos-n);
v->size -= n;
M_BSTRING_CONTRACT (v);
}
M_P(void, m_bstring, _resize, m_bstring_t v, size_t n)
{
M_BSTRING_CONTRACT (v);
const size_t size = v->size;
if (n > size) {
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, n, true);
memset(&ptr[size], 0, n-size);
}
v->size = n;
M_BSTRING_CONTRACT (v);
}
M_P(void, m_bstring, _reserve, m_bstring_t v, size_t n)
{
M_BSTRING_CONTRACT (v);
const size_t size = v->size;
if ( (n != 0 || v->size != 0) && n < v->offset + size) {
n = v->offset + size;
}
if (n == 0) {
M_MEMORY_FREE(m_context, uint8_t, v->ptr, v->alloc);
v->offset = 0;
v->size = 0;
v->alloc = 0;
v->ptr = NULL;
} else {
uint8_t *ptr = M_MEMORY_REALLOC (m_context, uint8_t, v->ptr, v->alloc, n);
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(uint8_t, n);
}
v->ptr = ptr;
v->alloc = n;
}
M_BSTRING_CONTRACT (v);
}
M_INLINE const uint8_t *
m_bstring_view(const m_bstring_t v, size_t offset, size_t size_requested)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX (offset+size_requested, v->size+1);
(void) size_requested;
return &m_bstr1ng_cstr(v)[offset];
}
// Acquire / release direct write access: once acquired, user can directly wite at the returned pointer for the requested size
// All other operations on the bstring are forbidden during direct access.
// This allows passing the bstring buffer to be formatted by different API
M_INLINE uint8_t *
m_bstring_acquire_access(m_bstring_t v, size_t offset, size_t size_requested)
{
M_BSTRING_CONTRACT (v);
M_ASSERT_INDEX (offset+size_requested, v->size+1);
(void) size_requested;
// Break canonical representation (this will generate assertion if a normal API is called now)
M_SWAP(size_t, v->size, v->alloc);
return &m_bstr1ng_cstr(v)[offset];
}
M_INLINE void
m_bstring_release_access(m_bstring_t v)
{
M_ASSERT( v->size >= v->alloc + v->offset );
// Restore canonical representation
M_SWAP(size_t, v->size, v->alloc);
M_BSTRING_CONTRACT (v);
}
// No Iterator planned for this container (No added value)
#if M_USE_STDIO
M_INLINE size_t
m_bstring_fwrite(FILE *f, const m_bstring_t v)
{
M_BSTRING_CONTRACT (v);
M_ASSERT (f != NULL);
if (M_UNLIKELY(m_bstring_size(v) == 0)) {
return 0;
}
M_ASSERT(m_bstr1ng_cstr(v) != NULL);
return fwrite(m_bstr1ng_cstr(v), 1, m_bstring_size(v), f);
}
M_P(bool, m_bstring, _fread, m_bstring_t v, FILE *f, size_t num)
{
M_BSTRING_CONTRACT (v);
M_ASSERT (f != NULL);
m_bstring_reset(v);
//TODO: option to read all the file num=-1?
if (M_UNLIKELY(num == 0)) {
return true;
}
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, num, true);
size_t n = fread(ptr, 1, num, f);
M_ASSERT (n <= num);
v->size = n;
return n == num;
}
#endif // Have stdio
// No formatted string input/output. Only serializer
/* Transform the Byte String 'v2'
and output it in the given serializer
See serialization for return code.
*/
M_P(m_serial_return_code_t, m_bstring, _out_serial, m_serial_write_t serial, const m_bstring_t v)
{
M_ASSERT (serial != NULL && serial->m_interface != NULL);
M_BSTRING_CONTRACT (v);
m_serial_local_t local;
m_serial_return_code_t ret;
size_t size = m_bstring_size(v);
uint8_t *ptr = m_bstr1ng_cstr(v);
ret = serial->m_interface->write_array_start M_R(local, serial, size);
if (size > 0) {
ret |= serial->m_interface->write_integer M_R(serial, ptr[0], 1);
for(size_t i = 1; i < size; i++) {
ret |= serial->m_interface->write_array_next M_R(local, serial);
ret |= serial->m_interface->write_integer M_R(serial, ptr[i], 1);
}
}
ret |= serial->m_interface->write_array_end M_R(local, serial);
return ret;
}
/* Read from the serializer
and set the converted value in the byte string 'v'.
See serialization for return code.
*/
M_P(m_serial_return_code_t, m_bstring, _in_serial, m_bstring_t v, m_serial_read_t f)
{
M_ASSERT (f != NULL && f->m_interface != NULL);
M_BSTRING_CONTRACT (v);
m_serial_return_code_t ret;
m_serial_local_t local;
size_t estimated_size = 0, real_size = 0;
m_bstring_reset(v);
ret = f->m_interface->read_array_start(local, f, &estimated_size);
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) {
return ret;
}
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, estimated_size, true);
do {
long long val;
ret = f->m_interface->read_integer(f, &val, 1);
if (ret != M_SERIAL_OK_DONE) { break; }
ptr[real_size++] = (uint8_t) val;
ret = f->m_interface->read_array_next(local, f);
} while (ret == M_SERIAL_OK_CONTINUE);
v->size = real_size;
M_BSTRING_CONTRACT (v);
return ret;
}
/* Define the OPLIST of a BYTE STRING */
#ifndef M_USE_CONTEXT
#define M_BSTRING_OPLIST \
(INIT(m_bstring_init),INIT_SET(m_bstring_init_set), SET(m_bstring_set), \
INIT_MOVE(m_bstring_init_move), MOVE(m_bstring_move), \
SWAP(m_bstring_swap), RESET(m_bstring_reset), EMPTY_P(m_bstring_empty_p), \
SUBTYPE(uint8_t), PUSH(m_bstring_push), \
CLEAR(m_bstring_clear), HASH(m_bstring_hash), EQUAL(m_bstring_equal_p), \
CMP(m_bstring_cmp), TYPE(m_bstring_t), GENTYPE(struct m_bstring_s*), \
OUT_SERIAL(m_bstring_out_serial), IN_SERIAL(m_bstring_in_serial), \
)
#else
#define M_BSTRING_OPLIST \
(INIT(m_bstring_init),INIT_SET(API_0P(m_bstring_init_set)), SET(API_0P(m_bstring_set)), \
INIT_MOVE(m_bstring_init_move), MOVE(API_0P(m_bstring_move)), \
SWAP(m_bstring_swap), RESET(m_bstring_reset), EMPTY_P(m_bstring_empty_p), \
SUBTYPE(uint8_t), PUSH(API_0P(m_bstring_push)), \
CLEAR(API_0P(m_bstring_clear)), HASH(m_bstring_hash), EQUAL(m_bstring_equal_p), \
CMP(m_bstring_cmp), TYPE(m_bstring_t), GENTYPE(struct m_bstring_s*), \
OUT_SERIAL(API_0P(m_bstring_out_serial)), IN_SERIAL(API_0P(m_bstring_in_serial=) \
)
#endif
/********************************************************************************/
/* */
/* Define the small name (i.e. without the prefix) of the API provided by this */
/* header if it is needed */
/* */
/********************************************************************************/
#if M_USE_SMALL_NAME
#define bstring_t m_bstring_t
#define bstring_size m_bstring_size
#define bstring_capacity m_bstring_capacity
#define bstring_init m_bstring_init
#define bstring_clear m_bstring_clear
#define bstring_reset m_bstring_reset
#define bstring_get_byte m_bstring_get_byte
#define bstring_set_byte m_bstring_set_byte
#define bstring_empty_p m_bstring_empty_p
#define bstring_push_back m_bstring_push_back
#define bstring_push_back_bytes m_bstring_push_back_bytes
#define bstring_init_set m_bstring_init_set
#define bstring_set m_bstring_set
#define bstring_init_move m_bstring_init_move
#define bstring_move m_bstring_move
#define bstring_cmp m_bstring_cmp
#define bstring_cmp_bytes m_bstring_cmp_bytes
#define bstring_equal_p m_bstring_equal_p
#define bstring_equal_bytes_p m_bstring_equal_bytes_p
#define bstring_pop_back m_bstring_pop_back
#define bstring_pop_back_bytes m_bstring_pop_back_bytes
#define bstring_pop_front m_bstring_pop_front
#define bstring_pop_front_bytes m_bstring_pop_front_bytes
#define bstring_push_bytes_at m_bstring_push_bytes_at
#define bstring_pop_bytes_at m_bstring_pop_bytes_at
#define bstring_resize m_bstring_resize
#define bstring_reserve m_bstring_reserve
#define bstring_view m_bstring_view
#define bstring_acquire_access m_bstring_acquire_access
#define bstring_release_access m_bstring_release_access
#define bstring_hash m_bstring_hash
#define bstring_fread m_bstring_fread
#define bstring_fwrite m_bstring_fwrite
#define bstring_out_serial m_bstring_out_serial
#define bstring_in_serial m_bstring_in_serial
#define bstring_swap m_bstring_swap
#define bstring_splice m_bstring_splice
#define BSTRING_OPLIST M_BSTRING_OPLIST
#endif
M_END_PROTECTED_CODE
#endif
+235 -358
View File
File diff suppressed because it is too large. Load diff
-835
View File
@@ -1,835 +0,0 @@
/*
* M*LIB - Concurrent memory pool allocator
*
* Copyright (c) 2017-2023, 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"
M_BEGIN_PROTECTED_CODE
/* Minimum number of nodes per group of nodes */
#define M_CMEMP00L_MIN_NODE_PER_GROUP 16
#define M_C_MEMPOOL_DEF(name, type_t) \
M_BEGIN_PROTECTED_CODE \
M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \
M_END_PROTECTED_CODE
/* Classic internal Singly List without allocation */
#define M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
\
typedef struct M_F(name, _slist_node_s) { \
struct M_F(name, _slist_node_s) *next; \
type_t data; \
} M_F(name, _slist_node_ct); \
\
typedef struct M_F(name, _slist_node_s) *M_F(name, _slist_ct)[1]; \
\
static inline void \
M_F(name, _slist_init)(M_F(name, _slist_ct) list) \
{ \
*list = NULL; \
} \
\
static inline void \
M_F(name, _slist_push)(M_F(name, _slist_ct) list, \
M_F(name, _slist_node_ct) *node) \
{ \
node->next = *list; \
*list = node; \
} \
\
static inline M_F(name, _slist_node_ct) * \
M_F(name, _slist_pop)(M_F(name, _slist_ct) list) \
{ \
M_ASSERT (*list != NULL); \
M_F(name, _slist_node_ct) *node = *list; \
*list = node->next; \
M_IF_DEBUG(node->next = NULL;) \
return node; \
} \
\
static inline bool \
M_F(name, _slist_empty_p)(M_F(name, _slist_ct) list) \
{ \
return *list == NULL; \
} \
\
static inline void \
M_F(name, _slist_move)(M_F(name, _slist_ct) list, \
M_F(name, _slist_ct) src) \
{ \
*list = *src; \
*src = NULL; \
} \
\
static inline void \
M_F(name, _slist_clear)(M_F(name, _slist_ct) list) \
{ \
M_F(name, _slist_node_ct) *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 M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
\
typedef struct M_F(name, _lf_node_s) { \
M_ATTR_EXTENSION _Atomic(struct M_F(name, _lf_node_s) *) next; \
m_gc_atomic_ticket_ct cpt; \
M_F(name, _slist_ct) list; \
} M_F(name, _lf_node_t); \
\
typedef struct M_F(name, _lflist_s) { \
M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) head; \
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) tail; \
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_F(name, _lf_node_t) nil; \
} M_F(name, _lflist_ct)[1]; \
\
static inline void \
M_F(name, _lflist_init)(M_F(name, _lflist_ct) list, \
M_F(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_F(name, _lflist_empty_p)(M_F(name, _lflist_ct) list) \
{ \
return atomic_load(&list->tail) == atomic_load(&list->head); \
} \
\
static inline void \
M_F(name, _lflist_push)(M_F(name, _lflist_ct) list, \
M_F(name, _lf_node_t) *node, m_core_backoff_ct bkoff) \
{ \
M_F(name, _lf_node_t) *tail; \
M_F(name, _lf_node_t) *next; \
\
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
m_core_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_core_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_F(name, _lf_node_t) * \
M_F(name, _lflist_pop)(M_F(name, _lflist_ct) list, m_core_backoff_ct bkoff) \
{ \
M_F(name, _lf_node_t) *head; \
M_F(name, _lf_node_t) *tail; \
M_F(name, _lf_node_t) *next; \
\
/* Reinitialize backoff */ \
m_core_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_core_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_F(name, _lf_node_t) *) 0);) \
return head; \
} \
\
/* Dequeue a node if the node is old enough */ \
static inline M_F(name, _lf_node_t) * \
M_F(name, _lflist_pop_if)(M_F(name, _lflist_ct) list, \
m_gc_ticket_ct age, m_core_backoff_ct bkoff) \
{ \
M_F(name, _lf_node_t) *head; \
M_F(name, _lf_node_t) *tail; \
M_F(name, _lf_node_t) *next; \
\
m_core_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_core_backoff_wait(bkoff); \
} \
} \
} \
M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \
return head; \
} \
\
static inline void \
M_F(name, _lflist_clear)(M_F(name, _lflist_ct) list) \
{ \
m_core_backoff_ct bkoff; \
m_core_backoff_init(bkoff); \
while (true) { \
M_F(name, _lf_node_t) *node = M_F(name, _lflist_pop)(list, bkoff); \
if (node == NULL) break; \
M_F(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
memory_order_relaxed); \
M_F(name, _slist_clear)(node->list); \
M_MEMORY_DEL(node); \
node = next; \
} \
/* Dummy node to free too */ \
M_F(name, _lf_node_t) *dummy; \
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
M_F(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 M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
\
static inline M_F(name, _lf_node_t) * \
M_F(name, _alloc_node)(unsigned int initial) \
{ \
M_F(name, _lf_node_t) * node; \
node = M_MEMORY_ALLOC(M_F(name, _lf_node_t)); \
if (M_UNLIKELY_NOMEM (node == NULL)) { \
M_MEMORY_FULL(sizeof(M_F(name, _lf_node_t))); \
return NULL; \
} \
atomic_init(&node->next, (M_F(name, _lf_node_t) *) 0); \
atomic_init(&node->cpt, 0UL); \
M_F(name, _slist_init)(node->list); \
for(unsigned i = 0; i < initial; i++) { \
M_F(name, _slist_node_ct) *n; \
n = M_MEMORY_ALLOC(M_F(name, _slist_node_ct)); \
if (M_UNLIKELY_NOMEM (n == NULL)) { \
M_MEMORY_FULL(sizeof(M_F(name, _lf_node_t))); \
return NULL; \
} \
M_F(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 M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
\
typedef struct M_F(name, _lfmp_thread_s) { \
M_F(name, _slist_ct) free; \
M_F(name, _slist_ct) to_be_reclaimed; \
M_CACHELINE_ALIGN(align1, M_F(name, _slist_ct), M_F(name, _slist_ct)); \
} M_F(name, _lfmp_thread_ct); \
\
static inline void \
M_F(name, _lfmp_thread_init)(M_F(name, _lfmp_thread_ct) *t) \
{ \
M_F(name, _slist_init)(t->free); \
M_F(name, _slist_init)(t->to_be_reclaimed); \
} \
\
static inline void \
M_F(name, _lfmp_thread_clear)(M_F(name, _lfmp_thread_ct) *t) \
{ \
M_ASSERT(M_F(name, _slist_empty_p)(t->to_be_reclaimed)); \
M_F(name, _slist_clear)(t->free); \
M_F(name, _slist_clear)(t->to_be_reclaimed); \
} \
/* NOTE: once a node is deleted, its data are kept readable until the future GC */
#define M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \
\
typedef struct M_F(name, _s) { \
unsigned initial; \
M_F(name, _lfmp_thread_ct) *thread_data; \
M_F(name, _lflist_ct) free; \
M_F(name, _lflist_ct) to_be_reclaimed; \
M_F(name, _lflist_ct) empty; \
m_cmemp00l_list_ct mempool_node; \
struct m_gc_s *gc_mem; \
} M_F(name, _t)[1]; \
\
/* Garbage collect of the nodes of the mempool on sleep */ \
static inline void \
M_C3(m_cmemp00l_,name,_gc_on_sleep)(m_gc_t gc_mem, m_cmemp00l_list_ct *data, \
m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket) \
{ \
/* Get back the mempool from the node */ \
struct M_F(name, _s) *mempool = \
M_TYPE_FROM_FIELD(struct M_F(name, _s), data, m_cmemp00l_list_ct, mempool_node); \
\
/* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \
if (!M_F(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
M_F(name, _lf_node_t) *node; \
/* Get a new empty group of nodes */ \
node = M_F(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_F(name, _alloc_node)(0); \
M_ASSERT(node != NULL); \
} \
M_ASSERT(M_F(name, _slist_empty_p)(node->list)); \
M_F(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \
M_F(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_F(name, _lf_node_t) *node; \
node = M_F(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
min_ticket, gc_mem->thread_data[id].bkoff); \
if (node == NULL) break; \
M_F(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_F(name, _init)(M_F(name, _t) mem, m_gc_t gc_mem, \
unsigned init_node_count, unsigned init_group_count) \
{ \
const size_t max_thread = gc_mem->max_thread; \
/* Initialize the thread data of the mempool */ \
mem->thread_data = M_MEMORY_REALLOC(M_F(name, _lfmp_thread_ct), NULL, max_thread); \
if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) { \
M_MEMORY_FULL(max_thread * sizeof(M_F(name, _lfmp_thread_ct))); \
return; \
} \
for(unsigned i = 0; i < max_thread;i++) { \
M_F(name, _lfmp_thread_init)(&mem->thread_data[i]); \
} \
/* Preallocate some group of nodes for the mempool */ \
mem->initial = M_MAX(M_CMEMP00L_MIN_NODE_PER_GROUP, init_node_count); \
M_F(name, _lflist_init)(mem->free, M_F(name, _alloc_node)(init_node_count)); \
M_F(name, _lflist_init)(mem->to_be_reclaimed, M_F(name, _alloc_node)(init_node_count)); \
M_F(name, _lflist_init)(mem->empty, M_F(name, _alloc_node)(0)); \
for(unsigned i = 1; i < init_group_count; i++) { \
M_F(name, _lflist_push)(mem->free, M_F(name, _alloc_node)(init_node_count), \
gc_mem->thread_data[0].bkoff); \
M_F(name, _lflist_push)(mem->empty, M_F(name, _alloc_node)(0), \
gc_mem->thread_data[0].bkoff); \
} \
/* Register the mempool in the GC */ \
mem->mempool_node.gc_on_sleep = M_C3(m_cmemp00l_,name,_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_F(name, _clear)(M_F(name, _t) mem) \
{ \
const unsigned max_thread = mem->gc_mem->max_thread; \
for(unsigned i = 0; i < max_thread;i++) { \
M_F(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
} \
M_MEMORY_FREE(mem->thread_data); \
mem->thread_data = NULL; \
M_F(name, _lflist_clear)(mem->empty); \
M_F(name, _lflist_clear)(mem->free); \
M_ASSERT(M_F(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
M_F(name, _lflist_clear)(mem->to_be_reclaimed); \
/* TODO: Unregister from the GC? */ \
} \
\
static inline type_t * \
M_F(name, _new)(M_F(name, _t) mem, m_gc_tid_t id) \
{ \
M_F(name, _slist_node_ct) *snode; \
M_F(name, _lf_node_t) *node; \
while (true) { \
/* Fast & likely path where we access the thread pool of nodes */ \
if (M_LIKELY(!M_F(name, _slist_empty_p)(mem->thread_data[id].free))) { \
snode = M_F(name, _slist_pop)(mem->thread_data[id].free); \
return &snode->data; \
} \
/* Request a group node to the freelist of groups */ \
node = M_F(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 */ \
M_ASSERT(mem->initial > 0); \
node = M_F(name, _alloc_node)(mem->initial); \
M_ASSERT(node != NULL); \
M_ASSERT(!M_F(name, _slist_empty_p)(node->list)); \
} \
M_F(name, _slist_move)(mem->thread_data[id].free, node->list); \
/* Push back the empty group */ \
M_ASSERT (M_F(name, _slist_empty_p)(node->list)); \
M_F(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_F(name, _del)(M_F(name, _t) mem, type_t *d, m_gc_tid_t id) \
{ \
M_F(name, _slist_node_ct) *snode; \
M_ASSERT( d != NULL); \
snode = M_TYPE_FROM_FIELD(M_F(name, _slist_node_ct), d, type_t, data); \
M_F(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
} \
/***********************************************************************/
/* Define the ID of a thread */
typedef unsigned 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_ct;
typedef atomic_ulong m_gc_atomic_ticket_ct;
/* Define the Linked List of mempools that are registered in the GC */
struct m_gc_s;
typedef struct m_cmemp00l_list_s {
struct m_cmemp00l_list_s *next;
void (*gc_on_sleep)(struct m_gc_s *gc_mem,
struct m_cmemp00l_list_s *data, m_gc_tid_t id,
m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket);
void *data;
} m_cmemp00l_list_ct;
/* Define the Garbage collector thread data */
typedef struct m_gc_lfmp_thread_s {
m_gc_atomic_ticket_ct ticket;
m_core_backoff_ct bkoff;
M_CACHELINE_ALIGN(align1, atomic_ulong, m_core_backoff_ct);
} m_gc_lfmp_thread_ct;
/* Define the Garbage collector coordinator */
typedef struct m_gc_s {
m_gc_atomic_ticket_ct ticket;
m_gc_tid_t max_thread;
m_genint_t thread_alloc;
m_gc_lfmp_thread_ct *thread_data;
m_cmemp00l_list_ct *mempool_list;
} m_gc_t[1];
static inline void
m_gc_init(m_gc_t gc_mem, size_t max_thread)
{
M_ASSERT(gc_mem != NULL);
M_ASSERT(max_thread > 0 && max_thread < INT_MAX);
atomic_init(&gc_mem->ticket, 0UL);
m_genint_init(gc_mem->thread_alloc, (unsigned int) max_thread);
gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_ct, NULL, max_thread);
if (M_UNLIKELY_NOMEM (gc_mem->thread_data == NULL)) {
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_ct));
return;
}
for(unsigned i = 0; i < max_thread;i++) {
atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX);
m_core_backoff_init(gc_mem->thread_data[i].bkoff);
}
gc_mem->max_thread = (unsigned int) max_thread;
gc_mem->mempool_list = NULL;
}
static inline void
m_gc_clear(m_gc_t gc_mem)
{
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) {
m_core_backoff_clear(gc_mem->thread_data[i].bkoff);
}
M_MEMORY_FREE(gc_mem->thread_data);
gc_mem->thread_data = NULL;
m_genint_clear(gc_mem->thread_alloc);
}
static inline m_gc_tid_t
m_gc_attach_thread(m_gc_t gc_mem)
{
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
unsigned id = m_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)
{
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
M_ASSERT(id < gc_mem->max_thread);
M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
m_genint_push(gc_mem->thread_alloc, id);
}
static inline void
m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
{
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
M_ASSERT(id < gc_mem->max_thread);
M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1;
atomic_store(&gc_mem->thread_data[id].ticket, t);
}
static inline m_gc_ticket_ct
m_cmemp00l_gc_min_ticket(m_gc_t gc_mem)
{
m_gc_ticket_ct 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_ct 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_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL);
atomic_store(&gc_mem->thread_data[id].ticket, t+1);
const m_gc_ticket_ct min_ticket = m_cmemp00l_gc_min_ticket(gc_mem);
/* Iterate over all registered mempools */
m_cmemp00l_list_ct *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);
}
/***********************************************************************/
/* */
/* Variable Length Array MEMPOOL */
/* */
/***********************************************************************/
M_CMEMP00L_DEF_SINGLY_LIST(m_vlapool, char)
M_CMEMP00L_DEF_LF_QUEUE(m_vlapool, char)
M_CMEMP00L_DEF_SYSTEM_ALLOC(m_vlapool, char)
typedef struct m_vlapool_lfmp_thread_s {
m_vlapool_slist_ct to_be_reclaimed;
M_CACHELINE_ALIGN(align1, m_vlapool_slist_ct);
} m_vlapool_lfmp_thread_ct;
static inline void
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_ct *t)
{
m_vlapool_slist_init(t->to_be_reclaimed);
}
static inline void
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_ct *t)
{
M_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_ct to_be_reclaimed;
m_vlapool_lflist_ct empty;
m_vlapool_lfmp_thread_ct *thread_data;
m_cmemp00l_list_ct 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_cmemp00l_vlapool_on_sleep(m_gc_t gc_mem, m_cmemp00l_list_ct *data,
m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket)
{
/* Get back the mempool from the node */
struct m_vlapool_s *vlapool =
M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_cmemp00l_list_ct, 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);
M_ASSERT(node != NULL);
}
M_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 size_t max_thread = gc_mem->max_thread;
/* Initialize the thread data of the vlapool */
mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_ct, NULL, max_thread);
if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) {
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_ct));
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_cmemp00l_vlapool_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);
M_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)
{
M_ASSERT(mem != NULL && mem->gc_mem != NULL);
M_ASSERT(id < mem->gc_mem->max_thread);
M_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)
{
M_ASSERT(mem != NULL && mem->gc_mem != NULL);
M_ASSERT(id < mem->gc_mem->max_thread);
M_ASSERT(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
M_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_ct *snode = M_ASSIGN_CAST(m_vlapool_slist_node_ct *, 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);
}
M_END_PROTECTED_CODE
#if M_USE_SMALL_NAME
#define C_MEMPOOL_DEF M_C_MEMPOOL_DEF
#endif
#endif
-928
View File
@@ -1,928 +0,0 @@
/*
* M*LIB - Basic Protected Concurrent module over container.
*
* Copyright (c) 2017-2023, 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-thread.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 M_CONCURRENT_DEF(name, ...) \
M_CONCURRENT_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define a protected concurrent container and its associated functions
based on the given container as the given name name_t
USAGE: CONCURRENT_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
#define M_CONCURRENT_DEF_AS(name, name_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_C0NCURRENT_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
(name, __VA_ARGS__, name_t ))) \
M_END_PROTECTED_CODE
/* 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 M_CONCURRENT_RP_DEF(name, ...) \
M_CONCURRENT_RP_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define a protected concurrent container and its associated functions
as the given name name_t
based on its given container. Operations that perform only read of the container
can be done in parallel.
USAGE: CONCURRENT_RP_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
#define M_CONCURRENT_RP_DEF_AS(name, name_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_C0NCURRENT_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
(name, __VA_ARGS__, name_t ))) \
M_END_PROTECTED_CODE
/* Define the oplist of a protected concurrent container given its name and its oplist.
USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */
#define M_CONCURRENT_OPLIST(...) \
M_C0NCURRENT_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_BASIC_OPLIST), \
(__VA_ARGS__ )))
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
/*****************************************************************************/
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define M_C0NCURRENT_OPLIST_P1(arg) M_C0NCURRENT_OPLIST_P2 arg
/* Validation of the given oplist */
#define M_C0NCURRENT_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_C0NCURRENT_OPLIST_P3, M_C0NCURRENT_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define M_C0NCURRENT_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 M_C0NCURRENT_OPLIST_P3(name, oplist) \
(M_IF_METHOD(INIT, oplist)(INIT(M_F(name, _init)),) \
,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_F(name, _init_set)),) \
,M_IF_METHOD(SET, oplist)(SET(M_F(name, _set)),) \
,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_F(name, _clear)),) \
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \
,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \
,M_IF_METHOD(SWAP,oplist)(SWAP(M_F(name, _swap)),) \
,NAME(name) \
,TYPE(M_F(name,_ct)) \
,SUBTYPE(M_F(name, _subtype_ct)) \
,OPLIST(oplist) \
,M_IF_METHOD(EMPTY_P, oplist)(EMPTY_P(M_F(name,_empty_p)),) \
,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_F(name,_size)),) \
,M_IF_METHOD(RESET, oplist)(RESET(M_F(name,_reset)),) \
,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_F(name, _set_at)),) \
,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_F(name, _erase)),) \
,M_IF_METHOD(PUSH, oplist)(PUSH(M_F(name,_push)),) \
,M_IF_METHOD(POP, oplist)(POP(M_F(name,_pop)),) \
,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_F(name,_push_move)),) \
,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_F(name,_pop_move)),) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_F(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 ***********************************/
/* Internal contract
NOTE: Can't check too much without locking the container itself
*/
#define M_C0NCURRENT_CONTRACT(c) do { \
M_ASSERT ((c) != NULL); \
M_ASSERT ((c)->self == (c)); \
} while (0)
/* Deferred evaluation for the concurrent definition,
so that all arguments are evaluated before further expansion */
#define M_C0NCURRENT_DEF_P1(arg) M_ID( M_C0NCURRENT_DEF_P2 arg )
/* Validate the value oplist before going further */
#define M_C0NCURRENT_DEF_P2(name, type, oplist, concurrent_t) \
M_IF_OPLIST(oplist)(M_C0NCURRENT_DEF_P3, M_C0NCURRENT_DEF_FAILURE)(name, type, oplist, concurrent_t)
/* Stop processing with a compilation failure */
#define M_C0NCURRENT_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_F(name, _t) [ type of the container ]
*/
#define M_C0NCURRENT_DEF_P3(name, type, oplist, concurrent_t) \
M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \
M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t)
/* Define the type of a concurrent container */
#define M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \
\
/* Define a concurrent container using a lock */ \
typedef struct M_F(name, _s) { \
struct M_F(name, _s) *self; \
m_mutex_t lock; \
m_cond_t there_is_data; /* condition raised when there is data */ \
type data; \
} concurrent_t[1]; \
\
/* Define alias for pointer types */ \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
/* Internal types for oplist */ \
typedef concurrent_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
\
/* Cannot define iterator as it cannot be reliable in a concurrent type */ \
/* Define the internal services used for the lock strategy */
#define M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \
\
/* Initial the fields of the concurrent object not associated to the \
sub-container. */ \
static inline void \
M_F(name, _internal_init)(concurrent_t out) \
{ \
m_mutex_init(out->lock); \
m_cond_init(out->there_is_data); \
out->self = out; \
M_C0NCURRENT_CONTRACT(out); \
} \
\
/* Clear the fields of the concurrent object not associated to the \
sub-container. */ \
static inline void \
M_F(name, _internal_clear)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_mutex_clear(out->lock); \
m_cond_clear(out->there_is_data); \
out->self = NULL; \
} \
\
/* Get the read lock. Multiple threads can get it, but only for reading. \
write lock is exclusive. \
NOTE: This instance doesn't implement the read/write strategy, \
and only get the lock */ \
static inline void \
M_F(name, _read_lock)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_mutex_lock (out->self->lock); \
} \
\
/* Free the read lock. See above. \
NOTE: This instance doesn't implement the read/write strategy, \
and only get the lock */ \
static inline void \
M_F(name, _read_unlock)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_mutex_unlock (out->self->lock); \
} \
\
/* Wait for a thread pushing some data in the container. \
CONSTRAINT: the read lock shall be get before calling this service */ \
static inline void \
M_F(name, _read_wait)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_cond_wait(out->self->there_is_data, out->self->lock); \
} \
\
/* Get the write lock. Only one threads can get it, and no other threads \
can get the read lock too. \
NOTE: This instance doesn't implement the read/write strategy, \
and only get the lock */ \
static inline void \
M_F(name, _write_lock)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_mutex_lock (out->lock); \
} \
\
/* Free the write lock. \
NOTE: This instance doesn't implement the read/write strategy, \
and only get the lock */ \
static inline void \
M_F(name, _write_unlock)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_mutex_unlock (out->lock); \
} \
\
/* Wait for a thread pushing some data in the container. \
CONSTRAINT: the write lock shall be get before calling this service */ \
static inline void \
M_F(name, _write_wait)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_cond_wait(out->self->there_is_data, out->self->lock); \
} \
\
/* Wait to all threads that some data are available in the container. \
CONSTRAINT: the write lock shall be get before calling this service */ \
static inline void \
M_F(name, _write_signal)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
/* We need to signal this to ALL waiting threads as multiple threads \
may wait on a some data of this container. */ \
m_cond_broadcast(out->there_is_data); \
} \
/* 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_F(name, _t) [ type of the container ]
A function is defined only if the underlying container exports the needed
services. It is usually one service declared per service exported.
*/
#define M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_F(name, _init)(concurrent_t out) \
{ \
M_F(name, _internal_init)(out); \
M_CALL_INIT(oplist, out->data); \
M_C0NCURRENT_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(INIT_SET, oplist)( \
static inline void \
M_F(name, _init_set)(concurrent_t out, concurrent_t const src) \
{ \
M_C0NCURRENT_CONTRACT(src); \
M_ASSERT (out != src); \
M_F(name, _internal_init)(out); \
M_F(name, _read_lock)(src); \
M_CALL_INIT_SET(oplist, out->data, src->data); \
M_F(name, _read_unlock)(src); \
M_C0NCURRENT_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(SET, oplist)( \
static inline void \
M_F(name, _set)(concurrent_t out, concurrent_t const src) \
{ \
M_C0NCURRENT_CONTRACT(out); \
if (M_UNLIKELY (out == src)) return; \
/* Need to order the locks in a total way to avoid lock deadlock. \
Indeed, two call to _set can be done in two threads with : \
T1: A := B \
T2: B := A \
If we lock first the mutex of out, then the src, it could be possible \
in the previous scenario that both mutexs are locked: T1 has locked A \
and T2 has locked B, and T1 is waiting for locking B, and T2 is waiting \
for locking A, resulting in a deadlock. \
To avoid this problem, we **always** lock the mutex which address is \
the lowest. */ \
if (out < src) { \
M_F(name, _write_lock)(out); \
M_F(name, _read_lock)(src); \
} else { \
M_F(name, _read_lock)(src); \
M_F(name, _write_lock)(out); \
} \
M_CALL_SET(oplist, out->data, src->data); \
if (out < src) { \
M_F(name, _read_lock)(src); \
M_F(name, _write_unlock)(out); \
} else { \
M_F(name, _write_unlock)(out); \
M_F(name, _read_unlock)(src); \
} \
M_C0NCURRENT_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(CLEAR, oplist)( \
static inline void \
M_F(name, _clear)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
/* No need to lock. A clear is supposed to be called when all operations \
of the container in other threads are terminated */ \
M_CALL_CLEAR(oplist, out->data); \
M_F(name, _internal_clear)(out); \
} \
,) \
\
M_IF_METHOD(INIT_MOVE, oplist)( \
static inline void \
M_F(name, _init_move)(concurrent_t out, concurrent_t src) \
{ \
M_C0NCURRENT_CONTRACT(src); \
M_ASSERT (out != src); \
/* No need to lock 'src' ? */ \
M_F(name, _internal_init)(out); \
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
M_F(name, _internal_clear)(src); \
M_C0NCURRENT_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(MOVE, oplist)( \
static inline void \
M_F(name, _move)(concurrent_t out, concurrent_t src) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_C0NCURRENT_CONTRACT(src); \
/* No need to lock 'src' ? */ \
M_F(name, _write_lock)(out); \
M_CALL_MOVE(oplist, out->data, src->data); \
M_F(name, _write_unlock)(out); \
M_F(name, _internal_clear)(src); \
M_C0NCURRENT_CONTRACT(out); \
} \
,) \
\
M_IF_METHOD(SWAP, oplist)( \
static inline void \
M_F(name, _swap)(concurrent_t out, concurrent_t src) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_C0NCURRENT_CONTRACT(src); \
if (M_UNLIKELY (out == src)) return; \
/* See comment above */ \
if (out < src) { \
M_F(name, _write_lock)(out); \
M_F(name, _write_lock)(src); \
} else { \
M_F(name, _write_lock)(src); \
M_F(name, _write_lock)(out); \
} \
M_CALL_SWAP(oplist, out->data, src->data); \
if (out < src) { \
M_F(name, _write_unlock)(src); \
M_F(name, _write_unlock)(out); \
} else { \
M_F(name, _write_unlock)(out); \
M_F(name, _write_unlock)(src); \
} \
} \
,) \
\
M_IF_METHOD(RESET, oplist)( \
static inline void \
M_F(name, _reset)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
M_CALL_RESET(oplist, out->data); \
M_F(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(EMPTY_P, oplist)( \
static inline bool \
M_F(name, _empty_p)(concurrent_t const out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _read_lock)(out); \
bool b = M_CALL_EMPTY_P(oplist, out->data); \
M_F(name, _read_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(GET_SIZE, oplist)( \
static inline size_t \
M_F(name, _size)(concurrent_t const out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _read_lock)(out); \
size_t r = M_CALL_GET_SIZE(oplist, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
,) \
\
M_IF_METHOD(SET_KEY, oplist)( \
static inline void \
M_F(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
M_CALL_SET_KEY(oplist, out->data, key, data); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(GET_KEY, oplist)( \
static inline bool \
M_F(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT (out_data != NULL); \
M_F(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_F(name, _read_unlock)(out); \
return p != NULL; \
} \
,) \
\
M_IF_METHOD(SAFE_GET_KEY, oplist)( \
static inline void \
M_F(name, _safe_get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT (out_data != NULL); \
M_F(name, _write_lock)(out); \
M_GET_VALUE_TYPE oplist *p = M_CALL_SAFE_GET_KEY(oplist, out->data, key); \
M_ASSERT (p != NULL); \
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
M_F(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(ERASE_KEY, oplist)( \
static inline bool \
M_F(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
/* We suppose that the container has 'infinite' capacity, so \
we won't signal that a free space has been created */ \
M_F(name, _write_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(PUSH, oplist)( \
static inline void \
M_F(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
M_CALL_PUSH(oplist, out->data, data); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
} \
\
M_EMPLACE_QUEUE_DEF(name, concurrent_t, M_F(name, _emplace), M_GET_OPLIST oplist, M_EMPLACE_QUEUE_GENE) \
,) \
\
M_IF_METHOD(POP, oplist)( \
static inline void \
M_F(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
M_CALL_POP(oplist, p, out->data); \
/* See comment above */ \
M_F(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(PUSH_MOVE, oplist)( \
static inline void \
M_F(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
M_CALL_PUSH_MOVE(oplist, out->data, data); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(POP_MOVE, oplist)( \
static inline void \
M_F(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
M_CALL_POP_MOVE(oplist, p, out->data); \
/* See comment above */ \
M_F(name, _write_unlock)(out); \
} \
,) \
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_F(name, _get_str)(m_string_t str, concurrent_t const out, bool a) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _read_lock)(out); \
M_CALL_GET_STR(oplist, str, out->data, a); \
M_F(name, _read_unlock)(out); \
} \
,) \
\
M_IF_METHOD(OUT_STR, oplist)( \
static inline void \
M_F(name, _out_str)(FILE *f, concurrent_t const out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _read_lock)(out); \
M_CALL_OUT_STR(oplist, f, out->data); \
M_F(name, _read_unlock)(out); \
} \
,) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
static inline bool \
M_F(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(IN_STR, oplist)( \
static inline bool \
M_F(name, _in_str)(concurrent_t out, FILE *f) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
bool b = M_CALL_IN_STR(oplist, out->data, f); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
return b; \
} \
,) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _read_lock)(out); \
m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
,) \
\
M_IF_METHOD(IN_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_F(name, _in_serial)(concurrent_t out, m_serial_read_t f) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _write_lock)(out); \
m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
return r; \
} \
,) \
\
M_IF_METHOD(EQUAL, oplist)( \
static inline bool \
M_F(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \
{ \
M_C0NCURRENT_CONTRACT(out1); \
M_C0NCURRENT_CONTRACT(out2); \
if (M_UNLIKELY (out1 == out2)) return true; \
/* See comment above on mutal mutexs */ \
if (out1 < out2) { \
M_F(name, _read_lock)(out1); \
M_F(name, _read_lock)(out2); \
} else { \
M_F(name, _read_lock)(out2); \
M_F(name, _read_lock)(out1); \
} \
bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \
if (out1 < out2) { \
M_F(name, _read_unlock)(out2); \
M_F(name, _read_unlock)(out1); \
} else { \
M_F(name, _read_unlock)(out1); \
M_F(name, _read_unlock)(out2); \
} \
return b; \
} \
,) \
\
M_IF_METHOD(GET_KEY, oplist)( \
static inline bool \
M_F(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT (out_data != NULL); \
bool ret = false; \
M_F(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; \
/* No data: wait for a write to signal some data */ \
M_F(name, _read_wait)(out); \
} \
M_F(name, _read_unlock)(out); \
return ret; \
} \
,) \
\
M_IF_METHOD2(POP, EMPTY_P, oplist)( \
static inline bool \
M_F(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT (p != NULL); \
bool ret = false; \
M_F(name, _write_lock)(out); \
while (true) { \
if (!M_CALL_EMPTY_P(oplist, out->data)) { \
M_CALL_POP(oplist, p, out->data); \
ret = true; \
break; \
} \
if (blocking == false) break; \
/* No data: wait for a write to signal some data */ \
M_F(name, _write_wait)(out); \
} \
M_F(name, _write_unlock)(out); \
return ret; \
} \
,) \
\
M_IF_METHOD2(POP_MOVE, EMPTY_P, oplist)( \
static inline bool \
M_F(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT (p != NULL); \
bool ret = false; \
M_F(name, _write_lock)(out); \
while (true) { \
if (!M_CALL_EMPTY_P(oplist, out->data)) { \
M_CALL_POP_MOVE(oplist, p, out->data); \
ret = true; \
break; \
} \
if (blocking == false) break; \
/* No data: wait for a write to signal some data */ \
M_F(name, _write_wait)(out); \
} \
M_F(name, _write_unlock)(out); \
return ret; \
} \
,) \
\
M_IF_METHOD(HASH, oplist)( \
static inline size_t \
M_F(name, _hash)(concurrent_t const out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_F(name, _read_lock)(out); \
size_t h = M_CALL_HASH(oplist, out->data); \
M_F(name, _read_unlock)(out); \
/* The hash is unchanged by the concurrent container */ \
return h; \
} \
,) \
/******************************** INTERNAL ***********************************/
/* Deferred evaluation for the RP concurrent definition,
so that all arguments are evaluated before further expansion */
#define M_C0NCURRENT_RP_DEF_P1(arg) M_ID( M_C0NCURRENT_RP_DEF_P2 arg )
/* Validate the value oplist before going further */
#define M_C0NCURRENT_RP_DEF_P2(name, type, oplist, concurrent_t) \
M_IF_OPLIST(oplist)(M_C0NCURRENT_RP_DEF_P3, M_C0NCURRENT_RP_DEF_FAILURE)(name, type, oplist, concurrent_t)
/* Stop processing with a compilation failure */
#define M_C0NCURRENT_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_F(name, _t) [ type of the container ]
*/
#define M_C0NCURRENT_RP_DEF_P3(name, type, oplist, concurrent_t) \
M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \
M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t)
/* Define the type of a RP concurrent container */
#define M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \
\
typedef struct M_F(name, _s) { \
struct M_F(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_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
typedef type M_F(name, _subtype_ct); \
/* Define the internal services for the lock strategy of a RP container */
#define M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \
\
static inline void \
M_F(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; \
M_C0NCURRENT_CONTRACT(out); \
} \
\
static inline void \
M_F(name, _internal_clear)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(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_F(name, _read_lock)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
struct M_F(name, _s) *self = out->self; \
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_F(name, _read_unlock)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
struct M_F(name, _s) *self = out->self; \
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_F(name, _write_lock)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(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_F(name, _write_unlock)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(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_F(name, _read_wait)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
struct M_F(name, _s) *self = out->self; \
M_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_F(name, _write_wait)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(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_F(name, _write_signal)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_mutex_lock (out->lock); \
m_cond_broadcast(out->there_is_data); \
m_mutex_unlock (out->lock); \
} \
/******************************** INTERNAL ***********************************/
#if M_USE_SMALL_NAME
#define CONCURRENT_DEF M_CONCURRENT_DEF
#define CONCURRENT_DEF_AS M_CONCURRENT_DEF_AS
#define CONCURRENT_RP_DEF M_CONCURRENT_RP_DEF
#define CONCURRENT_RP_DEF_AS M_CONCURRENT_RP_DEF_AS
#define CONCURRENT_OPLIST M_CONCURRENT_OPLIST
#endif
#endif
Loaded 100 of 193 files, more files were not shown because too many files have changed in this diff. Show more