182 Commits
Author SHA1 Message Date
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
120 changed files with 8241 additions and 7125 deletions

No files matched your search

+68 -1
View File
@@ -141,13 +141,80 @@ jobs:
name: Build on aarch64
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2.5.0
- 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 -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
+5 -5
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2024, 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.3
VERSION=0.8.0
# Define the contain of the distribution tarball
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.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 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
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 tests/test-mtry.c tests/tgen-mmap.c tests/tgen-try.c tests/tgen-tuple.c tests/test-mgeneric.c
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
+1462 -718
View File
File diff suppressed because it is too large. Load diff
+23 -1
View File
@@ -85,4 +85,26 @@ 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
+3 -3
View File
@@ -1,7 +1,7 @@
*Long term issues*
==========================================================
* TODO #34 : Pass old size arguments to memory allocator :ENHANCEMENT:
* DONE #34 : Pass old size arguments to memory allocator :ENHANCEMENT:
** State
@@ -53,7 +53,7 @@ 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.
* TODO #32 : User specialization fields in the container :ENHANCEMENT:
* DONE #32 : User specialization fields in the container :ENHANCEMENT:
** State
Some methods of a contained object may need to have some options that
@@ -452,7 +452,7 @@ as DPRIORITY_QUEUE_DEF ?
NOTE: Needs for such container?
On hold until a user needs it.
* TODO #23 : Strict MOVE semantic to clarify :ENHANCEMENT:
* DONE #23 : Strict MOVE semantic to clarify :ENHANCEMENT:
** State
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 106 KiB

+4 -4
View File
@@ -43,12 +43,12 @@ static void my_array_free(void *ptr)
////////////////////////////////////////////////////////////
// Allocate for single object
# define M_MEMORY_ALLOC(type) my_alloc (sizeof (type))
# define M_MEMORY_DEL(ptr) my_free(ptr, sizeof *ptr)
# 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(type, ptr, n) my_array_alloc (sizeof (type), ptr, n)
# define M_MEMORY_FREE(ptr) my_array_free(ptr)
# 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"
+4 -4
View File
@@ -23,8 +23,8 @@ static void my_free(void *ptr, size_t s)
}
// Allocate for single object
# define MY_MEMORY_ALLOC(type) my_alloc (sizeof (type))
# define MY_MEMORY_DEL(ptr) my_free(ptr, sizeof *ptr)
# define MY_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type))
# define MY_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr)
////////////////////////////////////////////////////////////
@@ -76,8 +76,8 @@ static void my_array_free(void *ptr)
}
// Allocate for array of objects
# define MY_MEMORY_REALLOC(type, ptr, n) my_array_alloc (sizeof (type), ptr, n)
# define MY_MEMORY_FREE(ptr) my_array_free(ptr)
# 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)
////////////////////////////////////////////////////////////
+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);
}
+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);
+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);
}
+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);
}
+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");
}
}
+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);
}
+30 -33
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - ALGO module
*
* Copyright (c) 2017-2024, 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:
@@ -171,18 +171,18 @@
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 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), void, 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 */
@@ -276,14 +276,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' */ \
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; \
@@ -350,6 +350,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,6 +358,9 @@
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); \
} \
@@ -434,7 +438,7 @@
/* Compute the union of two ***sorted*** containers */ \
M_IF_METHOD(IT_INSERT, cont_oplist)( \
M_INLINE void \
M_C3(name,sort_name,_union)(container_t dst, const container_t src cmp_param(name)) \
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; \
@@ -479,7 +483,7 @@
/* Compute the intersection of two ***sorted*** containers */ \
M_IF_METHOD(IT_REMOVE, cont_oplist)( \
M_INLINE void \
M_C3(name,sort_name,_intersect)(container_t dst, const container_t src cmp_param(name)) \
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; \
@@ -688,9 +692,9 @@
#define M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
/* Fill all the container with value (overwritten) */ \
M_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 +702,7 @@
\
/* Fill the container with exactly 'n' occurrence of 'value' */ \
M_IF_METHOD(PUSH, cont_oplist)( \
M_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 +713,9 @@
\
/* Fill the container with FOR('value'; 'value'+'inc') */ \
M_IF_METHOD(ADD, type_oplist)( \
M_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 +727,9 @@
\
/* Fill the container with n occurences of FOR('value'; 'value'+'inc') */ \
M_IF_METHOD(PUSH, cont_oplist)( \
M_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); \
@@ -756,10 +759,7 @@
M_IF_METHOD(PUSH_MOVE, cont_oplist)( \
\
/* Apply func for all elements of the container src and push the result in dst */ \
M_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) \
{ \
M_ASSERT(dst != src); \
M_CALL_RESET(cont_oplist, dst); \
@@ -775,10 +775,10 @@
\
/* Reduce all elements of the container in dst in function of func */ \
M_IF_METHOD(SET, type_oplist)( \
M_INLINE void \
M_F(name, _reduce) (type_t *dest, container_t const l, \
M_P(void, name, _reduce, type_t *dest, container_t const l, \
M_F(name, _transform_cb_ct) func) \
{ \
M_UNUSED_CONTEXT(); \
bool initDone = false; \
for M_EACH(item, l, cont_oplist) { \
if (initDone) { \
@@ -793,12 +793,12 @@
\
/* Reduce all transformed elements of the container in dst in function of func */ \
M_IF_METHOD(INIT, type_oplist)( \
M_INLINE \
void M_F(name, _map_reduce) (type_t *dest, \
M_P(void, 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_UNUSED_CONTEXT(); \
bool initDone = false; \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
@@ -905,8 +905,7 @@
/* Define functions based on remove method */
#define M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
M_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 +926,7 @@
} \
} \
\
M_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 +940,7 @@
} \
} \
\
M_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); \
+164 -159
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic ARRAY module
*
* Copyright (c) 2017-2024, 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:
@@ -80,6 +80,7 @@
/* OPLIST definition of a dynamic array */
/* FIXME: Do we want to export some methods as they are slow and
are not fit to be used for building other methods (like _it_remove)? */
#ifndef M_USE_CONTEXT
#define M_ARRA4_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)) \
,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(M_F(name, _init_set)),) \
@@ -106,7 +107,7 @@
,IT_REF(M_F(name,_ref)) \
,IT_CREF(M_F(name,_cref)) \
,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(M_F(name,_insert)) ,) \
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(IT_REMOVE(M_F(name,_remove)),) \
,IT_REMOVE(M_F(name,_remove)) \
,RESET(M_F(name,_reset)) \
,KEY_TYPE(size_t) \
,VALUE_TYPE(M_F(name, _subtype_ct)) \
@@ -115,12 +116,12 @@
,M_IF_METHOD(SET, oplist)(SET_KEY(M_F(name, _set_at)) ,) \
,GET_KEY(M_F(name, _get)) \
,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(M_F(name, _safe_get)) ,) \
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(ERASE_KEY(M_F(name, _erase)),) \
,ERASE_KEY(M_F(name, _erase)) \
,GET_SIZE(M_F(name, _size)) \
,M_IF_METHOD(INIT_SET, oplist)(PUSH(M_F(name,_push_back)) ,) \
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(POP(M_F(name,_pop_back)) ,) \
,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(PUSH_MOVE(M_F(name,_push_move)) ,) \
,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(POP_MOVE(M_F(name,_pop_move)) ,) \
,POP(M_F(name,_pop_back)) \
,PUSH_MOVE(M_F(name,_push_move)) \
,POP_MOVE(M_F(name,_pop_move)) \
,OPLIST(oplist) \
,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \
@@ -132,11 +133,65 @@
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
)
#else
#define M_ARRA4_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)) \
,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(API_0P(M_F(name, _init_set))),) \
,M_IF_METHOD(INIT_SET, oplist)(INIT_WITH(API_1(M_INIT_WITH_VAI)),) \
,M_IF_METHOD2(INIT_SET,SET, oplist)(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)) \
,TYPE(M_F(name,_ct)) , GENTYPE(struct M_F(name,_s)*) \
,NAME(name) \
,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_LAST(M_F(name,_it_last)) \
,IT_END(M_F(name,_it_end)) \
,IT_SET(M_F(name,_it_set)) \
,IT_END_P(M_F(name,_end_p)) \
,IT_LAST_P(M_F(name,_last_p)) \
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
,IT_NEXT(M_F(name,_next)) \
,IT_PREVIOUS(M_F(name,_previous)) \
,IT_REF(M_F(name,_ref)) \
,IT_CREF(M_F(name,_cref)) \
,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(API_0P(M_F(name,_insert))) ,) \
,IT_REMOVE(API_0P(M_F(name,_remove))) \
,RESET(API_0P(M_F(name,_reset))) \
,KEY_TYPE(size_t) \
,VALUE_TYPE(M_F(name, _subtype_ct)) \
,KEY_OPLIST(M_BASIC_OPLIST) \
,VALUE_OPLIST(oplist) \
,M_IF_METHOD(SET, oplist)(SET_KEY(API_0P(M_F(name, _set_at))) ,) \
,GET_KEY(M_F(name, _get)) \
,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(API_0P(M_F(name, _safe_get))) ,) \
,ERASE_KEY(API_0P(M_F(name, _erase))) \
,GET_SIZE(M_F(name, _size)) \
,M_IF_METHOD(INIT_SET, oplist)(PUSH(API_0P(M_F(name,_push_back))) ,) \
,POP(API_0P(M_F(name,_pop_back))) \
,PUSH_MOVE(API_0P(M_F(name,_push_move))) \
,POP_MOVE(M_F(name,_pop_move)) \
,OPLIST(oplist) \
,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(API_0P(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
/********************************** INTERNAL *********************************/
/* Define the internal contract of an array */
//TODO: Can be simplified in release build to speed up compilation speed.
#define M_ARRA4_CONTRACT(a) do { \
M_ASSERT (a != NULL); \
M_ASSERT (a->size <= a->alloc); \
@@ -173,7 +228,7 @@
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \
M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF)
M_EMPLACE_QUEUE_DEF(name, array_t, _emplace_back, oplist, M_ARRA4_EMPLACE_DEF)
/* Define the types */
#define M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \
@@ -212,40 +267,37 @@
M_ARRA4_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _reset)(array_t v) \
M_P(void, name, _reset, array_t v) \
{ \
M_ARRA4_CONTRACT(v); \
M_UNUSED_CONTEXT(); \
for(size_t i = 0; i < v->size; i++) \
M_CALL_CLEAR(oplist, v->ptr[i]); \
v->size = 0; \
M_ARRA4_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _clear)(array_t v) \
M_P(void, name, _clear, array_t v) \
{ \
M_ARRA4_CONTRACT(v); \
M_F(name, _reset)(v); \
M_CALL_FREE(oplist, v->ptr); \
M_F(name, _reset) M_R(v); \
M_CALL_FREE(oplist, type, v->ptr, v->alloc); \
/* This is so reusing the object implies an assertion failure */ \
v->alloc = 1; \
v->ptr = NULL; \
} \
\
M_IF_METHOD2(INIT_SET, SET, oplist)( \
M_INLINE void \
M_F(name, _set)(array_t d, const array_t s) \
M_P(void, name, _set, array_t d, const array_t s) \
{ \
M_ARRA4_CONTRACT(d); \
M_ARRA4_CONTRACT(s); \
if (M_UNLIKELY (d == s)) return; \
if (s->size > d->alloc) { \
const size_t alloc = s->size; \
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, d->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
M_MEMORY_FULL(type, alloc); \
} \
d->ptr = ptr; \
d->alloc = alloc; \
@@ -264,13 +316,12 @@
M_ARRA4_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _init_set)(array_t d, const array_t s) \
M_P(void, name, _init_set, array_t d, const array_t s) \
{ \
M_ASSERT (d != s); \
M_ON_EXCEPTION(M_F(name, _clear)(d) ) { \
M_ON_EXCEPTION(M_F(name, _clear) M_R(d) ) { \
M_F(name, _init)(d); \
M_F(name, _set)(d, s); \
M_F(name, _set) M_R(d, s); \
} \
} \
, /* No SET & INIT_SET */) \
@@ -289,19 +340,18 @@
M_ARRA4_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _move)(array_t d, array_t s) \
M_P(void, name, _move, array_t d, array_t s) \
{ \
M_ASSERT (d != s); \
M_F(name, _clear)(d); \
M_F(name, _clear) M_R(d); \
M_F(name, _init_move)(d, s); \
} \
\
M_IF_METHOD(SET, oplist)( \
M_INLINE void \
M_F(name, _set_at)(array_t v, size_t i, type const x) \
M_P(void, name, _set_at, array_t v, size_t i, type const x) \
{ \
M_ARRA4_CONTRACT(v); \
M_UNUSED_CONTEXT(); \
M_ASSERT(v->size > 0 && v->ptr != NULL); \
M_ASSERT_INDEX(i, v->size); \
M_CALL_SET(oplist, v->ptr[i], x); \
@@ -317,22 +367,19 @@
return &v->ptr[v->size-1]; \
} \
\
M_INLINE type * \
M_F(name, _push_raw)(array_t v) \
M_P(type *, name, _push_raw, array_t v) \
{ \
M_ARRA4_CONTRACT(v); \
if (M_UNLIKELY (v->size >= v->alloc)) { \
M_ASSERT(v->size == v->alloc); \
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return NULL; \
M_MEMORY_FULL(type, -(size_t)1); \
} \
M_ASSERT (alloc > v->size); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return NULL; \
M_MEMORY_FULL(type, alloc); \
} \
v->ptr = ptr; \
v->alloc = alloc; \
@@ -346,10 +393,9 @@
} \
\
M_IF_METHOD(INIT_SET, oplist)( \
M_INLINE void \
M_F(name, _push_back)(array_t v, type const x) \
M_P(void, name, _push_back, array_t v, type const x) \
{ \
type *data = M_F(name, _push_raw)(v); \
type *data = M_F(name, _push_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION( v->size --); \
@@ -359,10 +405,9 @@
, /* No INIT_SET */ ) \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _push_new)(array_t v) \
M_P(type *, name, _push_new, array_t v) \
{ \
type *data = M_F(name, _push_raw)(v); \
type *data = M_F(name, _push_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return NULL; \
M_IF_EXCEPTION( v->size --); \
@@ -372,21 +417,17 @@
} \
, /* No INIT */ ) \
\
M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \
M_INLINE void \
M_F(name, _push_move)(array_t v, type *x) \
M_P(void, name, _push_move, array_t v, type *x) \
{ \
M_ASSERT (x != NULL); \
type *data = M_F(name, _push_raw)(v); \
type *data = M_F(name, _push_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_DO_INIT_MOVE (oplist, *data, *x); \
M_CALL_INIT_MOVE (oplist, *data, *x); \
} \
, /* INIT_SET | INIT_MOVE */ ) \
\
M_IF_METHOD(INIT_SET, oplist)( \
M_INLINE void \
M_F(name, _push_at)(array_t v, size_t key, type const x) \
M_P(void, name, _push_at, array_t v, size_t key, type const x) \
{ \
M_ARRA4_CONTRACT(v); \
M_ASSERT_INDEX(key, v->size+1); \
@@ -394,14 +435,12 @@
M_ASSERT(v->size == v->alloc); \
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
M_MEMORY_FULL(type, -(size_t)1); \
} \
M_ASSERT (alloc > v->size); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
M_MEMORY_FULL(type, alloc); \
} \
v->ptr = ptr; \
v->alloc = alloc; \
@@ -417,8 +456,7 @@
, /* No INIT_SET */ ) \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE void \
M_F(name, _resize)(array_t v, size_t size) \
M_P(void, name, _resize, array_t v, size_t size) \
{ \
M_ARRA4_CONTRACT(v); \
if (v->size > size) { \
@@ -430,10 +468,9 @@
/* Increase size of array */ \
if (size > v->alloc) { \
size_t alloc = size ; \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
M_MEMORY_FULL(type, alloc); \
} \
v->ptr = ptr; \
v->alloc = alloc; \
@@ -448,8 +485,7 @@
} \
, /* No INIT */ ) \
\
M_INLINE void \
M_F(name, _reserve)(array_t v, size_t alloc) \
M_P(void, name, _reserve, array_t v, size_t alloc) \
{ \
M_ARRA4_CONTRACT(v); \
/* NOTE: Reserve below needed size to perform a shrink to fit */ \
@@ -457,14 +493,13 @@
alloc = v->size; \
} \
if (M_UNLIKELY (alloc == 0)) { \
M_CALL_FREE(oplist, v->ptr); \
M_CALL_FREE(oplist, type, v->ptr, v->alloc); \
v->size = v->alloc = 0; \
v->ptr = NULL; \
} else { \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
M_MEMORY_FULL(type, alloc); \
} \
v->ptr = ptr; \
v->alloc = alloc; \
@@ -473,8 +508,7 @@
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _safe_get)(array_t v, size_t idx) \
M_P(type *, name, _safe_get, array_t v, size_t idx) \
{ \
M_ARRA4_CONTRACT(v); \
const size_t size = idx + 1; \
@@ -485,13 +519,11 @@
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
/* In case of overflow of size_t */ \
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return NULL; \
M_MEMORY_FULL(type, -(size_t)1); \
} \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return NULL; \
M_MEMORY_FULL(type, alloc); \
} \
v->ptr = ptr; \
v->alloc = alloc; \
@@ -508,11 +540,10 @@
} \
, /* No INIT */) \
\
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
M_INLINE void \
M_F(name, _pop_back)(type *dest, array_t v) \
M_P(void, name, _pop_back, type *dest, array_t v) \
{ \
M_ARRA4_CONTRACT(v); \
M_UNUSED_CONTEXT(); \
M_ASSERT (v->ptr != NULL); \
M_ASSERT_INDEX(0, v->size); \
v->size--; \
@@ -523,9 +554,7 @@
} \
M_ARRA4_CONTRACT(v); \
} \
, /* SET | INIT_MOVE */ ) \
\
M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \
M_INLINE void \
M_F(name, _pop_move)(type *dest, array_t v) \
{ \
@@ -534,19 +563,18 @@
M_ASSERT_INDEX(0, v->size); \
M_ASSERT (dest != NULL); \
v->size--; \
M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
M_CALL_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
M_ARRA4_CONTRACT(v); \
} \
, /* INIT_SET | INIT_MOVE */ ) \
\
/* TODO: Factorize all INIT dependent methods within the same IF*/ \
M_IF_METHOD(INIT, oplist)( \
M_INLINE void \
M_F(name, _pop_until)(array_t v, it_t pos) \
M_P(void, name, _pop_until, array_t v, it_t pos) \
{ \
M_ARRA4_CONTRACT(v); \
M_ASSERT (v == pos->array); \
M_ASSERT_INDEX(pos->index, v->size+1); \
M_F(name, _resize)(v, pos->index); \
M_F(name, _resize) M_R(v, pos->index); \
} \
, /* No INIT */ ) \
\
@@ -571,11 +599,10 @@
return v->alloc; \
} \
\
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
M_INLINE void \
M_F(name, _pop_at)(type *dest, array_t v, size_t i) \
M_P(void, name, _pop_at, type *dest, array_t v, size_t i) \
{ \
M_ARRA4_CONTRACT(v); \
M_UNUSED_CONTEXT(); \
M_ASSERT (v->size > 0 && v->ptr != NULL); \
M_ASSERT_INDEX(i, v->size); \
if (dest) \
@@ -587,19 +614,16 @@
M_ARRA4_CONTRACT(v); \
} \
\
M_INLINE bool \
M_F(name, _erase)(array_t a, size_t i) \
M_P(bool, name, _erase, array_t a, size_t i) \
{ \
M_ARRA4_CONTRACT(a); \
if (i >= a->size) return false; \
M_F(name, _pop_at)(NULL, a, i); \
M_F(name, _pop_at) M_R(NULL, a, i); \
return true; \
} \
, /* SET | INIT_MOVE */ ) \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE void \
M_F(name, _insert_v)(array_t v, size_t i, size_t num) \
M_P(void, name, _insert_v, array_t v, size_t i, size_t num) \
{ \
M_ARRA4_CONTRACT(v); \
M_ASSERT_INDEX(i, v->size+1); \
@@ -608,20 +632,17 @@
if (M_UNLIKELY_NOMEM (size <= v->size)) { \
/* Unlikely case of nothing to do */ \
if (num == 0) return; \
M_MEMORY_FULL(sizeof (type) * v->size); \
return ; \
M_MEMORY_FULL(type, v->size); \
} \
/* Test if alloc array is sufficient */ \
if (size > v->alloc) { \
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return ; \
M_MEMORY_FULL(type, -(size_t)1); \
} \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * alloc); \
return; \
M_MEMORY_FULL(type, alloc); \
} \
v->ptr = ptr; \
v->alloc = alloc; \
@@ -637,10 +658,10 @@
} \
, /* No INIT */) \
\
M_INLINE void \
M_F(name, _remove_v)(array_t v, size_t i, size_t j) \
M_P(void, name, _remove_v, array_t v, size_t i, size_t j) \
{ \
M_ARRA4_CONTRACT(v); \
M_UNUSED_CONTEXT(); \
M_ASSERT(i < j && v->ptr != NULL); \
M_ASSERT_INDEX(i, v->size); \
M_ASSERT_INDEX(j, v->size+1); \
@@ -663,7 +684,6 @@
M_ARRA4_CONTRACT(v2); \
} \
\
M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist) ( \
M_INLINE void \
M_F(name, _swap_at)(array_t v, size_t i, size_t j) \
{ \
@@ -672,12 +692,11 @@
M_ASSERT_INDEX(i, v->size); \
M_ASSERT_INDEX(j, v->size); \
type tmp; \
M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]); \
M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
M_DO_INIT_MOVE(oplist, v->ptr[j], tmp); \
M_CALL_INIT_MOVE(oplist, tmp, v->ptr[i]); \
M_CALL_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
M_CALL_INIT_MOVE(oplist, v->ptr[j], tmp); \
M_ARRA4_CONTRACT(v); \
} \
, /* INIT_SET | INIT_MOVE */ ) \
\
M_INLINE type * \
M_F(name, _get)(const array_t v, size_t i) \
@@ -797,22 +816,20 @@
} \
\
M_IF_METHOD(INIT_SET, oplist)( \
M_INLINE void \
M_F(name, _insert)(array_t a, it_t it, type const x) \
M_P(void, name, _insert, array_t a, it_t it, type const x) \
{ \
M_ASSERT (it != NULL && a == it->array); \
size_t index = M_F(name, _end_p)(it) ? 0 : it->index+1; \
M_F(name, _push_at)(a, index, x); \
M_F(name, _push_at)M_R(a, index, x); \
it->index = index; \
} \
, /* End of INIT_SET */ ) \
\
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
M_INLINE void \
M_F(name, _remove)(array_t a, it_t it) \
M_P(void, name, _remove, array_t a, it_t it) \
{ \
M_ASSERT (it != NULL && a == it->array); \
M_F(name, _pop_at)(NULL, a, it->index); \
M_F(name, _pop_at) M_R(NULL, a, it->index); \
/* NOTE: it->index will naturally point to the next element */ \
} \
, /* End of SET | INIT_SET */ ) \
@@ -870,25 +887,23 @@
k += n1+n2; \
for (;;) { \
if (M_CALL_CMP(oplist, *el1, *el2) <= 0) { \
M_DO_INIT_MOVE(oplist, *dest, *el1); \
M_CALL_INIT_MOVE(oplist, *dest, *el1); \
dest++; \
el1++; \
if (M_UNLIKELY(-- n1 == 0)) { \
if (n2 > 0) { \
memcpy (dest, el2, n2 * sizeof (type)); \
dest += n2; \
} \
M_ASSERT (n2 > 0); \
memcpy (dest, el2, n2 * sizeof (type)); \
dest += n2; \
break; \
} \
} else { \
M_DO_INIT_MOVE(oplist, *dest, *el2); \
M_CALL_INIT_MOVE(oplist, *dest, *el2); \
dest++; \
el2++; \
if (M_UNLIKELY(-- n2 == 0)) { \
if (n1 > 0) { \
memcpy (dest, el1, n1 * sizeof (type)); \
dest += n1; \
} \
M_ASSERT (n1 > 0); \
memcpy (dest, el1, n1 * sizeof (type)); \
dest += n1; \
break; \
} \
} \
@@ -902,19 +917,17 @@
M_ASSERT (org_tab == tab); \
} \
\
M_INLINE void \
M_F(name, _special_stable_sort)(array_t l) \
M_P(void, name, _special_stable_sort, array_t l) \
{ \
if (M_UNLIKELY (l->size < 2)) \
return; \
/* NOTE: if size is <= 4, no need to perform an allocation */ \
type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \
type *temp = M_CALL_REALLOC(oplist, type, NULL, 0, l->size); \
if (M_UNLIKELY_NOMEM (temp == NULL)) { \
M_MEMORY_FULL(sizeof (type) * l->size); \
return ; \
M_MEMORY_FULL(type, l->size); \
} \
M_C3(m_arra4_,name,_stable_sort_noalloc)(l->ptr, l->size, temp); \
M_CALL_FREE(oplist, temp); \
M_CALL_FREE(oplist, type, temp, l->size); \
} \
,) /* IF SWAP & SET methods */ \
\
@@ -953,11 +966,11 @@
} \
, /* no HASH */ ) \
\
M_INLINE void \
M_F(name, _splice)(array_t a1, array_t a2) \
M_P(void, name, _splice, array_t a1, array_t a2) \
{ \
M_ARRA4_CONTRACT(a1); \
M_ARRA4_CONTRACT(a2); \
M_ASSERT(a1 != a2); \
if (M_LIKELY (a2->size > 0)) { \
size_t newSize = a1->size + a2->size; \
/* To overflow newSize, we need to a1 and a2 a little bit above \
@@ -965,9 +978,9 @@
should have exhausted all memory before reaching such sizes. */ \
M_ASSERT_INDEX(a1->size, newSize); \
if (newSize > a1->alloc) { \
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, a1->alloc, newSize); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (type) * newSize); \
M_MEMORY_FULL(type, newSize); \
} \
a1->ptr = ptr; \
a1->alloc = newSize; \
@@ -986,12 +999,10 @@
#define M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \
\
M_IF_METHOD(GET_STR, oplist)( \
M_INLINE void \
M_F(name, _get_str)(m_string_t str, array_t const array, \
bool append) \
M_P(void, name, _get_str, m_string_t str, array_t const array, bool append) \
{ \
M_ARRA4_CONTRACT(array); \
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
it_t it; \
for (M_F(name, _it)(it, array) ; \
!M_F(name, _end_p)(it); \
@@ -999,9 +1010,9 @@
type const *item = M_F(name, _cref)(it); \
M_CALL_GET_STR(oplist, str, *item, true); \
if (!M_F(name, _last_p)(it)) \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
} \
m_string_push_back (str, ']'); \
m_string_push_back M_R(str, ']'); \
} \
, /* no GET_STR */ ) \
\
@@ -1023,12 +1034,11 @@
, /* no OUT_STR */ ) \
\
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
M_INLINE bool \
M_F(name, _parse_str)(array_t array, const char str[], const char**endp) \
M_P(bool, name, _parse_str, array_t array, const char str[], const char**endp) \
{ \
M_ARRA4_CONTRACT(array); \
M_ASSERT (str != NULL); \
M_F(name,_reset)(array); \
M_F(name,_reset)M_R(array); \
int c = *str++; \
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
c = m_core_str_nospace(&str); \
@@ -1040,7 +1050,7 @@
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
c = m_core_str_nospace(&str); \
if (b == false || c == 0) { c = 0; break; } \
M_F(name, _push_back)(array, item); \
M_F(name, _push_back) M_R(array, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
exit: \
@@ -1051,12 +1061,11 @@
, /* no PARSE_STR & INIT */ ) \
\
M_IF_METHOD2(IN_STR, INIT, oplist)( \
M_INLINE bool \
M_F(name, _in_str)(array_t array, FILE *file) \
M_P(bool, name, _in_str, array_t array, FILE *file) \
{ \
M_ARRA4_CONTRACT(array); \
M_ASSERT (file != NULL); \
M_F(name,_reset)(array); \
M_F(name,_reset)M_R(array); \
int c = fgetc(file); \
if (M_UNLIKELY (c != '[')) return false; \
c = fgetc(file); \
@@ -1068,7 +1077,7 @@
bool b = M_CALL_IN_STR(oplist, item, file); \
c = m_core_fgetc_nospace(file); \
if (b == false || c == EOF) { c = 0; break; } \
M_F(name, _push_back)(array, item); \
M_F(name, _push_back) M_R(array, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
M_ARRA4_CONTRACT(array); \
@@ -1077,46 +1086,44 @@
, /* no IN_STR & INIT */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, const array_t array) \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const array_t array) \
{ \
M_ARRA4_CONTRACT(array); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
ret = f->m_interface->write_array_start(local, f, array->size); \
ret = f->m_interface->write_array_start M_R(local, f, array->size); \
for (size_t i = 0; i < array->size; i++) { \
type const *item = M_F(name, _cget)(array, i); \
if (i != 0) { \
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(oplist, f, *item); \
} \
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_METHOD2(IN_SERIAL, INIT, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(array_t array, m_serial_read_t f) \
M_P(m_serial_return_code_t, name, _in_serial, array_t array, m_serial_read_t f) \
{ \
M_ARRA4_CONTRACT(array); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
size_t estimated_size = 0; \
M_F(name,_reset)(array); \
M_F(name,_reset)M_R(array); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) { \
return ret; \
} \
M_F(name, _reserve)(array, estimated_size); \
M_F(name, _reserve) M_R(array, estimated_size); \
M_QLET(1, item, type, oplist) { \
do { \
ret = M_CALL_IN_SERIAL(oplist, item, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_F(name, _push_back)(array, item); \
M_F(name, _push_back)M_R(array, item); \
ret = f->m_interface->read_array_next(local, f); \
} while (ret == M_SERIAL_OK_CONTINUE); \
} \
@@ -1127,11 +1134,9 @@
/* Definition of the emplace_back function for arrays */
#define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_F(name, _subtype_ct) *data = M_F(name, _push_raw)(v); \
M_F(name, _subtype_ct) *data = M_F(name, _push_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION( v->size --); \
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin stdatomic wrapper for C++ compatibility
*
* Copyright (c) 2017-2024, 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:
@@ -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.
+74 -55
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - BITSET module
*
* Copyright (c) 2017-2024, 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:
@@ -101,11 +101,10 @@ m_bitset_reset(m_bitset_t t)
}
/* Clear a bitset (DESTRUCTOR) */
M_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 */
M_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,15 +138,14 @@ m_bitset_set(m_bitset_t d, const m_bitset_t s)
}
/* Initialize & set a bitset to another one (CONSTRUCTOR) */
M_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) */
@@ -167,10 +163,9 @@ m_bitset_init_move(m_bitset_t d, m_bitset_t s)
}
/* Move a bitset from another one (DESTRUCTOR) */
M_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);
}
@@ -203,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) */
M_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))) {
@@ -212,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;
@@ -242,23 +234,20 @@ m_bitset_push_back (m_bitset_t v, bool x)
}
/* Resize the bitset to have exactly 'size' bits */
M_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;
@@ -290,8 +279,7 @@ m_bitset_resize (m_bitset_t v, size_t size)
}
/* Reserve allocation in the bitset to accommodate at least 'size' bits without reallocation */
M_INLINE void
m_bitset_reserve (m_bitset_t v, size_t alloc)
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);
@@ -302,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;
@@ -390,7 +377,7 @@ m_bitset_size(m_bitset_t v)
return v->size;
}
/* Return the capacity in limbs of the bitset */
/* Return the capacity in bits of the bitset */
M_INLINE size_t
m_bitset_capacity(m_bitset_t v)
{
@@ -456,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 */
M_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);
@@ -517,7 +503,7 @@ m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
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++)
@@ -526,7 +512,7 @@ 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 */
/* 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)
{
@@ -632,8 +618,7 @@ m_bitset_out_str(FILE *file, const m_bitset_t set)
}
/* Input the bitset from a formatted text in a FILE */
M_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);
@@ -643,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);
@@ -651,8 +636,7 @@ m_bitset_in_str(m_bitset_t set, FILE *file)
}
/* Parse the bitset from a formatted text in a C string */
M_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);
@@ -664,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);
@@ -675,10 +659,9 @@ 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 */
M_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,
@@ -851,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) \
@@ -885,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
@@ -965,18 +985,17 @@ M_BEGIN_PROTECTED_CODE
/* Output to a m_string_t 'str' the formatted text representation of the bitset 'set'
or append it to the string (append=true) */
M_INLINE void
m_bitset_get_str(m_string_t str, const m_bitset_t set, bool append)
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
+159 -86
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - B+TREE module
*
* Copyright (c) 2017-2024, 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:
@@ -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)), \
@@ -189,7 +190,40 @@
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
)
#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
@@ -205,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)), \
@@ -233,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), \
@@ -245,7 +280,47 @@
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)),) \
)
#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 ***********************************/
@@ -343,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) \
@@ -406,23 +482,22 @@
\
/* Allocate a new node */ \
/* TODO: Can be specialized to alloc for leaf or for non leaf */ \
M_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; \
} \
\
M_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); \
} \
@@ -443,7 +518,7 @@
return num; \
} \
\
M_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; \
@@ -486,19 +561,19 @@
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
M_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' */ \
M_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. \
@@ -520,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 */ \
@@ -542,22 +617,22 @@
return n; \
} \
\
M_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); \
} \
\
M_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); \
} \
\
M_INLINE bool M_F(name, _empty_p)(const tree_t b) \
@@ -573,7 +648,7 @@
return b->size; \
} \
\
M_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; \
@@ -607,7 +682,7 @@
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 */ \
@@ -627,11 +702,11 @@
return M_CONST_CAST(value_t, M_F(name, _get)(b, key)); \
} \
\
M_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 */ \
@@ -659,10 +734,10 @@
return i; \
} \
\
M_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 */ \
@@ -682,16 +757,16 @@
return i; \
} \
\
M_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 */ \
@@ -713,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);,) \
@@ -728,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); \
@@ -741,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? */ \
@@ -754,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); \
@@ -772,7 +847,7 @@
} \
} \
\
M_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 */ \
@@ -781,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); \
@@ -791,10 +866,10 @@
return ret; \
} \
\
M_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]); \
@@ -811,10 +886,11 @@
return -1; /* Not found */ \
} \
\
M_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); \
@@ -846,10 +922,11 @@
M_ASSERT (left->num != 0); \
} \
\
M_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); \
@@ -880,7 +957,7 @@
M_ASSERT (left->num != 0); \
} \
\
M_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))); \
@@ -916,7 +993,7 @@
/* We can also cache the index when we descend the tree. \
TODO: Bench if this is worth the effort.*/ \
M_INLINE int \
M_F(name, _search_for_node)(node_t parent, node_t child) \
M_F(name, _i_search_for_node)(node_t parent, node_t child) \
{ \
M_ASSERT (!M_F(name, _is_leaf)(parent)); \
int i = 0; \
@@ -928,12 +1005,12 @@
} \
} \
\
M_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 */ \
@@ -949,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); \
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 */ \
@@ -965,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; \
@@ -984,16 +1061,17 @@
} \
} \
\
M_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); \
} \
\
M_INLINE value_t * \
@@ -1046,13 +1124,12 @@
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
} \
\
M_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); \
} \
@@ -1161,7 +1238,7 @@
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); \
@@ -1259,29 +1336,29 @@
, /* NO HASH METHOD */ ) \
\
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
M_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, "["); \
(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 */ ) \
\
@@ -1314,12 +1391,11 @@
, /* no out_str */ ) \
\
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)( \
M_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); \
M_F(name,_reset)M_R(t1); \
int c = *str++; \
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
c = *str++; \
@@ -1335,9 +1411,9 @@
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)(t1, key, value); \
M_F(name, _set_at)M_R(t1, key, value); \
, \
M_F(name, _push)(t1, key); \
M_F(name, _push)M_R(t1, key); \
) \
} while (c == M_GET_SEPARATOR key_oplist); \
} \
@@ -1349,12 +1425,11 @@
, /* no parse_str */ ) \
\
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \
M_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); \
@@ -1371,9 +1446,9 @@
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)(t1, key, value) \
M_F(name, _set_at)M_R(t1, key, value) \
, \
M_F(name, _push)(t1, key) \
M_F(name, _push)M_R(t1, key) \
); \
} while (c == M_GET_SEPARATOR key_oplist); \
} \
@@ -1383,8 +1458,7 @@
, /* no in_str */ ) \
\
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
M_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); \
@@ -1396,46 +1470,45 @@
/* 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)( \
M_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; \
M_F(name,_reset)(t1); \
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)( \
@@ -1448,7 +1521,7 @@
if (ret != M_SERIAL_OK_CONTINUE) break; \
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
if (ret != M_SERIAL_OK_DONE) break; \
M_F(name, _set_at)(t1, key, value); \
M_F(name, _set_at)M_R(t1, key, value); \
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
, /* queue style */ \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
@@ -1456,7 +1529,7 @@
do { \
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) break; \
M_F(name, _push)(t1, key); \
M_F(name, _push)M_R(t1, key); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
) /* End of IF isMap */ \
} \
+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
+194 -317
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface (Thread safe)
*
* Copyright (c) 2017-2024, 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:
@@ -31,20 +31,12 @@
/* Define the different kind of policy a lock-based buffer can have:
* - the buffer can be either a queue (policy is FIFO) or a stack (policy is FILO),
* - if the push method is by default blocking (waiting for the buffer to has some space) or not, *** deprecated ***
* - if the pop method is by default blocking (waiting for the buffer to has some data) or not, *** deprecated ***
* - if both methods are blocking, *** deprecated ***
* - if it shall be thread safe or not (i.e. remove the mutex lock and atomic costs),
* - if the buffer has to be init with empty elements, or if it shall init an element when it is pushed (and moved when popped),
* - if the buffer has to overwrite the last element if the buffer is full,
* - if the pop of an element is not complete until the call to pop_release (preventing push until this call).
*/
typedef enum {
M_BUFFER_QUEUE = 0, M_BUFFER_STACK = 1,
M_BUFFER_BLOCKING_PUSH = 0, M_BUFFER_UNBLOCKING_PUSH = 2,
M_BUFFER_BLOCKING_POP = 0, M_BUFFER_UNBLOCKING_POP = 4,
M_BUFFER_BLOCKING = 0, M_BUFFER_UNBLOCKING = 6,
M_BUFFER_THREAD_SAFE = 0, M_BUFFER_THREAD_UNSAFE = 8,
M_BUFFER_PUSH_INIT_POP_MOVE = 16,
M_BUFFER_PUSH_OVERWRITE = 32,
M_BUFFER_DEFERRED_POP = 64
@@ -138,80 +130,6 @@ typedef enum {
#define M_BUFF3R_POLICY_P(policy, val) \
(((policy) & (val)) != 0)
/* Handle either atomic integer or normal integer in function of the policy
parameter of the buffer BUFFER_THREAD_UNSAFE (BUFFER_THREAD_SAFE is the
default). This enables avoiding to pay the cost of atomic operations if not
applicable.
*/
typedef union m_buff3r_number_s {
unsigned int u;
atomic_uint a;
#ifdef __cplusplus
// Not sure why, but C++ needs an explicit default constructor for this union.
m_buff3r_number_s() : u(0) {};
#endif
} m_buff3r_number_ct[1];
M_INLINE void
m_buff3r_number_init(m_buff3r_number_ct n, unsigned int policy)
{
if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE))
atomic_init(&n->a, 0U);
else
n->u = 0UL;
}
M_INLINE unsigned int
m_buff3r_number_load(m_buff3r_number_ct n, unsigned int policy)
{
if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE))
// Perform a memory acquire so that further usage of the buffer
// is synchronized.
return atomic_load_explicit(&n->a, memory_order_acquire);
else
return n->u;
}
M_INLINE void
m_buff3r_number_store(m_buff3r_number_ct n, unsigned int v, unsigned int policy)
{
if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE))
// This function is used in context where a relaxed access is sufficient.
atomic_store_explicit(&n->a, v, memory_order_relaxed);
else
n->u = v;
}
M_INLINE void
m_buff3r_number_set(m_buff3r_number_ct n, m_buff3r_number_ct v, unsigned int policy)
{
if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE))
// This function is used in context where a relaxed access is sufficient.
atomic_store_explicit(&n->a, atomic_load_explicit(&v->a, memory_order_relaxed), memory_order_relaxed);
else
n->u = v->u;
}
M_INLINE unsigned int
m_buff3r_number_inc(m_buff3r_number_ct n, unsigned int policy)
{
if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE))
return atomic_fetch_add(&n->a, 1U);
else
return n->u ++;
}
M_INLINE unsigned int
m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
{
if (!M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE))
return atomic_fetch_sub(&n->a, 1U);
else
return n->u --;
}
/********************************** INTERNAL *********************************/
/* Test if the size is only run-time or build time */
#define M_BUFF3R_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size))
@@ -230,7 +148,7 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
/* Contract of a buffer within a protected section */
#define M_BUFF3R_PROTECTED_CONTRACT(policy, buffer, size) do { \
M_ASSERT (m_buff3r_number_load(buffer->number[0], policy) <= M_BUFF3R_SIZE(size)); \
M_ASSERT (atomic_load_explicit(&buffer->number[0], memory_order_acquire) <= M_BUFF3R_SIZE(size)); \
} while (0)
@@ -258,7 +176,7 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
M_BUFF3R_DEF_TYPE(name, type, m_size, policy, oplist, buffer_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_BUFF3R_DEF_CORE(name, type, m_size, policy, oplist, buffer_t) \
M_EMPLACE_QUEUE_DEF(name, buffer_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
M_EMPLACE_QUEUE_DEF(name, buffer_t, _emplace, oplist, M_BUFF3R_EMPLACE_QUEUE_GENE)
/* Define the type of a buffer */
#define M_BUFF3R_DEF_TYPE(name, type, m_size, policy, oplist, buffer_t) \
@@ -267,7 +185,7 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
by multiple writing threads. No need to align if there is no thread */ \
typedef union M_F(name, _el_s) { \
type x; \
char align[M_BUFF3R_POLICY_P(policy, M_BUFFER_THREAD_UNSAFE) ? 1 : M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
} M_F(name, _el_ct); \
\
typedef struct M_F(name, _s) { \
@@ -284,7 +202,7 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
size_t idx_cons; /* Index of the consumption threads */ \
/* number[0] := Number of elements in the buffer */ \
/* number[1] := [OPTION] Number of elements being deferred in the buffer */ \
m_buff3r_number_ct number[1 + M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)]; \
atomic_uint number[1 + M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)]; \
/* If fixed size, array of elements, otherwise pointer to element */ \
M_F(name, _el_ct) M_BUFF3R_IF_CTE_SIZE(m_size)(data[m_size], *data); \
} buffer_t[1]; \
@@ -300,29 +218,24 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
/* Define the core functionalities of a buffer */
#define M_BUFF3R_DEF_CORE(name, type, m_size, policy, oplist, buffer_t) \
\
M_INLINE void \
M_F(name, _init)(buffer_t v, size_t size) \
M_N(void, name, _init, buffer_t v, size_t size) \
{ \
M_ASSERT(size <= UINT_MAX); \
M_GLOBAL_CONTEXT(); \
M_BUFF3R_IF_CTE_SIZE(m_size)(M_ASSERT(size == m_size), v->capacity = size); \
v->idx_prod = v->idx_cons = v->overwrite = 0; \
m_buff3r_number_init (v->number[0], policy); \
atomic_init (&v->number[0], 0U); \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
m_buff3r_number_init (v->number[1], policy); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_init(v->mutexPush); \
m_mutex_init(v->mutexPop); \
m_cond_init(v->there_is_data); \
m_cond_init(v->there_is_room_for_data); \
} else { \
M_ASSERT(M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING)); \
} \
atomic_init (&v->number[1], 0U); \
m_mutex_init(v->mutexPush); \
m_mutex_init(v->mutexPop); \
m_cond_init(v->there_is_data); \
m_cond_init(v->there_is_room_for_data); \
\
M_BUFF3R_IF_CTE_SIZE(m_size)( /* Statically allocated */ , \
v->data = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, M_BUFF3R_SIZE(m_size)); \
v->data = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, 0, M_BUFF3R_SIZE(m_size)); \
if (M_UNLIKELY_NOMEM (v->data == NULL)) { \
M_MEMORY_FULL (M_BUFF3R_SIZE(m_size)*sizeof(M_F(name, _el_ct))); \
return; \
M_MEMORY_FULL (M_F(name, _el_ct), M_BUFF3R_SIZE(m_size)); \
} \
) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
@@ -334,17 +247,16 @@ M_F(name, _init)(buffer_t v, size_t size) \
} \
\
M_BUFF3R_IF_CTE_SIZE(m_size)( \
M_INLINE void \
M_C3(m_buff3r_,name,_init)(buffer_t v) \
M_N(void, name,_i_init, buffer_t v) \
{ \
M_F(name, _init)(v, m_size); \
} \
, ) \
\
M_INLINE void \
M_C3(m_buff3r_,name,_clear_obj)(buffer_t v) \
M_N(void, name, _i_clear_obj, buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
M_GLOBAL_CONTEXT(); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
for(size_t i = 0; i < M_BUFF3R_SIZE(m_size); i++) { \
M_CALL_CLEAR(oplist, v->data[i].x); \
@@ -359,73 +271,59 @@ M_F(name, _init)(buffer_t v, size_t size) \
} \
} \
v->idx_prod = v->idx_cons = 0; \
m_buff3r_number_store (v->number[0], 0U, policy); \
atomic_store_explicit (&v->number[0], 0U, memory_order_relaxed); \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
m_buff3r_number_store(v->number[1], 0U, policy); \
atomic_store_explicit (&v->number[1], 0U, memory_order_relaxed); \
M_BUFF3R_CONTRACT(v,m_size); \
} \
\
M_INLINE void \
M_F(name, _clear)(buffer_t v) \
M_N(void, name, _clear, buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
M_C3(m_buff3r_,name,_clear_obj)(v); \
M_GLOBAL_CONTEXT(); \
M_F(name,_i_clear_obj)(v); \
M_BUFF3R_IF_CTE_SIZE(m_size)( , \
M_CALL_FREE(oplist, v->data); \
M_CALL_FREE(oplist, M_F(name, _el_ct), v->data, M_BUFF3R_SIZE(m_size)); \
v->data = NULL; \
) \
v->overwrite = 0; \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_clear(v->mutexPush); \
m_mutex_clear(v->mutexPop); \
m_cond_clear(v->there_is_data); \
m_cond_clear(v->there_is_room_for_data); \
} \
m_mutex_clear(v->mutexPush); \
m_mutex_clear(v->mutexPop); \
m_cond_clear(v->there_is_data); \
m_cond_clear(v->there_is_room_for_data); \
} \
\
M_INLINE void \
M_F(name, _reset)(buffer_t v) \
M_N(void, name, _reset, buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
} \
M_GLOBAL_CONTEXT(); \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
if (M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) \
M_C3(m_buff3r_,name,_clear_obj)(v); \
M_F(name,_i_clear_obj)(v); \
v->idx_prod = v->idx_cons = 0; \
m_buff3r_number_store (v->number[0], 0U, policy); \
atomic_store_explicit (&v->number[0], 0U, memory_order_relaxed); \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
m_buff3r_number_store(v->number[1], 0U, policy); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_cond_broadcast(v->there_is_room_for_data); \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
} \
atomic_store_explicit(&v->number[1], 0U, memory_order_relaxed); \
m_cond_broadcast(v->there_is_room_for_data); \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
M_BUFF3R_CONTRACT(v,m_size); \
} \
\
M_INLINE void M_ATTR_DEPRECATED \
M_F(name, _clean)(buffer_t v) \
M_N(void, name, _init_set, buffer_t dest, const buffer_t src) \
{ \
M_F(name,_reset)(v); \
} \
\
M_INLINE void \
M_F(name, _init_set)(buffer_t dest, const buffer_t src) \
{ \
/* un-const 'src', so that we can lock it (semantically it is const) */ \
M_GLOBAL_CONTEXT(); \
/* un-const 'src', so that we can lock it (semantically it is const) */ \
M_F(name, _uptr_ct) vu; \
vu.cptr = src; \
M_F(name, _ptr) v = vu.ptr; \
M_ASSERT (dest != v); \
M_BUFF3R_CONTRACT(v,m_size); \
M_F(name, _init)(dest, M_BUFF3R_SIZE(m_size)); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
} \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
\
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
@@ -444,22 +342,20 @@ M_F(name, _init)(buffer_t v, size_t size) \
\
dest->idx_prod = v->idx_prod; \
dest->idx_cons = v->idx_cons; \
m_buff3r_number_set (dest->number[0], v->number[0], policy); \
atomic_store_explicit (&dest->number[0], atomic_load_explicit(&v->number[0], memory_order_relaxed), memory_order_relaxed); \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
m_buff3r_number_set(dest->number[1], v->number[1], policy); \
atomic_store_explicit(&dest->number[1], atomic_load_explicit(&v->number[1], memory_order_relaxed), memory_order_relaxed); \
\
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
} \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
M_BUFF3R_CONTRACT(v,m_size); \
M_BUFF3R_CONTRACT(dest, m_size); \
} \
\
M_INLINE void \
M_F(name, _set)(buffer_t dest, const buffer_t src) \
M_N(void, name, _set, buffer_t dest, const buffer_t src) \
{ \
/* un-const 'src', so that we can lock it (semantically it is const) */ \
M_GLOBAL_CONTEXT(); \
/* un-const 'src', so that we can lock it (semantically it is const) */ \
M_F(name, _uptr_ct) vu; \
vu.cptr = src; \
M_F(name, _ptr) v = vu.ptr; \
@@ -467,24 +363,22 @@ M_F(name, _init)(buffer_t v, size_t size) \
M_BUFF3R_CONTRACT(v,m_size); \
\
if (dest == v) return; \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
/* Case of deadlock: A := B, B:=C, C:=A (all in //) \
Solution: order the lock by increasing memory */ \
if (dest < v) { \
m_mutex_lock(dest->mutexPush); \
m_mutex_lock(dest->mutexPop); \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
} else { \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
m_mutex_lock(dest->mutexPush); \
m_mutex_lock(dest->mutexPop); \
} \
/* Case of deadlock: A := B, B:=C, C:=A (all in //) \
Solution: order the lock by increasing memory */ \
if (dest < v) { \
m_mutex_lock(dest->mutexPush); \
m_mutex_lock(dest->mutexPop); \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
} else { \
m_mutex_lock(v->mutexPush); \
m_mutex_lock(v->mutexPop); \
m_mutex_lock(dest->mutexPush); \
m_mutex_lock(dest->mutexPop); \
} \
\
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
M_C3(m_buff3r_,name,_clear_obj)(dest); \
M_F(name,_i_clear_obj)(dest); \
\
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
for(size_t i = 0; i < M_BUFF3R_SIZE(m_size); i++) { \
@@ -502,32 +396,29 @@ M_F(name, _init)(buffer_t v, size_t size) \
\
dest->idx_prod = v->idx_prod; \
dest->idx_cons = v->idx_cons; \
m_buff3r_number_set (dest->number[0], v->number[0], policy); \
atomic_store_explicit(&dest->number[0], atomic_load_explicit(&v->number[0], memory_order_relaxed), memory_order_relaxed); \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
m_buff3r_number_set(dest->number[1], v->number[1], policy); \
atomic_store_explicit(&dest->number[1], atomic_load_explicit(&v->number[1], memory_order_relaxed), memory_order_relaxed); \
\
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
/* It may be false, but it is not wrong! */ \
m_cond_broadcast(v->there_is_room_for_data); \
m_cond_broadcast(v->there_is_data); \
if (dest < v) { \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
m_mutex_unlock(dest->mutexPop); \
m_mutex_unlock(dest->mutexPush); \
} else { \
m_mutex_unlock(dest->mutexPop); \
m_mutex_unlock(dest->mutexPush); \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
} \
m_cond_broadcast(v->there_is_room_for_data); \
m_cond_broadcast(v->there_is_data); \
if (dest < v) { \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
m_mutex_unlock(dest->mutexPop); \
m_mutex_unlock(dest->mutexPush); \
} else { \
m_mutex_unlock(dest->mutexPop); \
m_mutex_unlock(dest->mutexPush); \
m_mutex_unlock(v->mutexPop); \
m_mutex_unlock(v->mutexPush); \
} \
M_BUFF3R_CONTRACT(v,m_size); \
M_BUFF3R_CONTRACT(dest, m_size); \
} \
\
M_INLINE bool \
M_F(name, _empty_p)(buffer_t v) \
M_F(name, _empty_p)(const buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
/* If the buffer has been configured with deferred pop \
@@ -535,45 +426,40 @@ M_F(name, _init)(buffer_t v, size_t size) \
deferred pop has reached 0, not the number of items in the \
buffer is 0. */ \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
return m_buff3r_number_load (v->number[1], policy) == 0; \
return atomic_load_explicit (&v->number[1], memory_order_relaxed) == 0; \
else \
return m_buff3r_number_load (v->number[0], policy) == 0; \
return atomic_load_explicit (&v->number[0], memory_order_relaxed) == 0; \
} \
\
M_INLINE bool \
M_F(name, _full_p)(buffer_t v) \
M_F(name, _full_p)(const buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
return m_buff3r_number_load (v->number[0], policy) \
return atomic_load_explicit (&v->number[0], memory_order_relaxed) \
== M_BUFF3R_SIZE(m_size); \
} \
\
M_INLINE size_t \
M_F(name, _size)(buffer_t v) \
M_F(name, _size)(const buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
return m_buff3r_number_load (v->number[0], policy); \
return atomic_load_explicit (&v->number[0], memory_order_relaxed); \
} \
\
M_INLINE bool \
M_F(name, _push_blocking)(buffer_t v, type const data, bool blocking) \
M_N(bool, name, _push_blocking, buffer_t v, type const data, bool blocking) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
\
M_GLOBAL_CONTEXT(); \
/* Producer Mutex lock (mutex lock performs an acquire memory barrier) */ \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_lock(v->mutexPush); \
while (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \
&& M_F(name, _full_p)(v)) { \
if (!blocking) { \
m_mutex_unlock(v->mutexPush); \
return false; \
} \
m_cond_wait(v->there_is_room_for_data, v->mutexPush); \
} \
} else if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \
&& M_F(name, _full_p)(v)) \
return false; \
m_mutex_lock(v->mutexPush); \
while (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \
&& M_F(name, _full_p)(v)) { \
if (!blocking) { \
m_mutex_unlock(v->mutexPush); \
return false; \
} \
m_cond_wait(v->there_is_room_for_data, v->mutexPush); \
} \
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
\
size_t previousSize, idx = v->idx_prod; \
@@ -611,48 +497,42 @@ M_F(name, _init)(buffer_t v, size_t size) \
thread which has the push lock. As such, it is an atomic variable \
that performs a release memory barrier. */ \
/* Increment number of elements of the buffer */ \
previousSize = m_buff3r_number_inc (v->number[0], policy); \
previousSize = atomic_fetch_add (&v->number[0], 1U); \
if (M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \
previousSize = m_buff3r_number_inc (v->number[1], policy); \
previousSize = atomic_fetch_add (&v->number[1], 1U); \
} \
/* From this point, consumer may read the data in the table */ \
} \
\
/* Producer unlock (mutex unlock performs a release memory barrier) */ \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_unlock(v->mutexPush); \
/* If the number of items in the buffer was 0, some consumer \
may be waiting. Signal to them the availability of the data \
We cannot only signal one thread. */ \
if (previousSize == 0) { \
m_mutex_lock(v->mutexPop); \
m_cond_broadcast(v->there_is_data); \
m_mutex_unlock(v->mutexPop); \
} \
m_mutex_unlock(v->mutexPush); \
/* If the number of items in the buffer was 0, some consumer \
may be waiting. Signal to them the availability of the data \
We cannot only signal one thread. */ \
if (previousSize == 0) { \
m_mutex_lock(v->mutexPop); \
m_cond_broadcast(v->there_is_data); \
m_mutex_unlock(v->mutexPop); \
} \
\
M_BUFF3R_CONTRACT(v,m_size); \
return true; \
} \
\
M_INLINE bool \
M_F(name, _pop_blocking)(type *data, buffer_t v, bool blocking) \
M_N(bool, name, _pop_blocking, type *data, buffer_t v, bool blocking) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
M_ASSERT (data != NULL); \
\
M_GLOBAL_CONTEXT(); \
/* consumer lock (mutex lock performs an acquire memory barrier) */ \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_lock(v->mutexPop); \
while (M_F(name, _empty_p)(v)) { \
if (!blocking) { \
m_mutex_unlock(v->mutexPop); \
return false; \
} \
m_cond_wait(v->there_is_data, v->mutexPop); \
} \
} else if (M_F(name, _empty_p)(v)) \
return false; \
m_mutex_lock(v->mutexPop); \
while (M_F(name, _empty_p)(v)) { \
if (!blocking) { \
m_mutex_unlock(v->mutexPop); \
return false; \
} \
m_cond_wait(v->there_is_data, v->mutexPop); \
} \
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
\
/* POP data from the buffer and update INDEX */ \
@@ -661,7 +541,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, *data, v->data[v->idx_cons].x); \
} else { \
M_DO_INIT_MOVE (oplist, *data, v->data[v->idx_cons].x); \
M_CALL_INIT_MOVE (oplist, *data, v->data[v->idx_cons].x); \
} \
v->idx_cons = (v->idx_cons == M_BUFF3R_SIZE(m_size)-1) ? 0 : (v->idx_cons + 1); \
} else { \
@@ -670,7 +550,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, *data, v->data[v->idx_prod].x); \
} else { \
M_DO_INIT_MOVE (oplist, *data, v->data[v->idx_prod].x); \
M_CALL_INIT_MOVE (oplist, *data, v->data[v->idx_prod].x); \
} \
} \
\
@@ -681,24 +561,22 @@ M_F(name, _init)(buffer_t v, size_t size) \
/* Decrement number of elements in the buffer */ \
size_t previousSize; \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \
previousSize = m_buff3r_number_dec (v->number[0], policy); \
previousSize = atomic_fetch_sub (&v->number[0], 1U); \
} else { \
m_buff3r_number_dec (v->number[1], policy); \
atomic_fetch_sub (&v->number[1], 1U); \
} \
/* Space may be reused by a producer thread from this point */ \
\
/* consumer unlock (mutex unlock performs a release memory barrier) */ \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
m_mutex_unlock(v->mutexPop); \
/* If the number of items in the buffer was the max, some producer \
may be waiting. Signal to them the availability of the free room \
We cannot only signal one thread. */ \
if ((!M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) \
&& previousSize == M_BUFF3R_SIZE(m_size)) { \
m_mutex_lock(v->mutexPush); \
m_cond_broadcast(v->there_is_room_for_data); \
m_mutex_unlock(v->mutexPush); \
} \
m_mutex_unlock(v->mutexPop); \
/* If the number of items in the buffer was the max, some producer \
may be waiting. Signal to them the availability of the free room \
We cannot only signal one thread. */ \
if ((!M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) \
&& previousSize == M_BUFF3R_SIZE(m_size)) { \
m_mutex_lock(v->mutexPush); \
m_cond_broadcast(v->there_is_room_for_data); \
m_mutex_unlock(v->mutexPush); \
} \
\
M_BUFF3R_CONTRACT(v,m_size); \
@@ -706,18 +584,18 @@ M_F(name, _init)(buffer_t v, size_t size) \
} \
\
\
M_INLINE bool \
M_F(name, _push)(buffer_t v, type const data) \
M_N(void, name, _push, buffer_t v, type const data) \
{ \
return M_F(name, _push_blocking)(v, data, \
!M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING_PUSH)); \
bool b = M_F(name, _push_blocking)(v, data, true); \
assert(b); \
(void) b; \
} \
\
M_INLINE bool \
M_F(name, _pop)(type *data, buffer_t v) \
M_N(void, name, _pop, type *data, buffer_t v) \
{ \
return M_F(name, _pop_blocking)(data, v, \
!M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING_POP)); \
bool b = M_F(name, _pop_blocking)(data, v, true); \
assert(b); \
(void) b; \
} \
\
M_INLINE size_t \
@@ -738,7 +616,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
{ \
/* Decrement the effective number of elements in the buffer */ \
if (M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \
size_t previousSize = m_buff3r_number_dec (v->number[0], policy); \
size_t previousSize = atomic_fetch_sub (&v->number[0], 1U); \
if (previousSize == M_BUFF3R_SIZE(m_size)) { \
m_mutex_lock(v->mutexPush); \
m_cond_broadcast(v->there_is_room_for_data); \
@@ -798,7 +676,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
M_QU3UE_MPMC_DEF_TYPE(name, type, policy, oplist, buffer_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_QU3UE_MPMC_DEF_CORE(name, type, policy, oplist, buffer_t) \
M_EMPLACE_QUEUE_DEF(name, buffer_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
M_EMPLACE_QUEUE_DEF(name, buffer_t, _emplace, oplist, M_BUFF3R_EMPLACE_QUEUE_GENE)
/* Define the type of a MPMC queue */
#define M_QU3UE_MPMC_DEF_TYPE(name, type, policy, oplist, buffer_t) \
@@ -833,10 +711,10 @@ M_F(name, _init)(buffer_t v, size_t size) \
/* Define the core functionalities of a MPMC queue */
#define M_QU3UE_MPMC_DEF_CORE(name, type, policy, oplist, buffer_t) \
M_INLINE bool \
M_F(name, _push)(buffer_t table, type const x) \
M_N(bool, name, _push, buffer_t table, type const x) \
{ \
M_QU3UE_MPMC_CONTRACT(table); \
M_GLOBAL_CONTEXT(); \
unsigned int idx = atomic_load_explicit(&table->ProdIdx, \
memory_order_relaxed); \
const unsigned int i = idx & (table->size -1); \
@@ -867,18 +745,18 @@ M_F(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
M_INLINE bool \
M_F(name, _pop)(type *ptr, buffer_t table) \
M_N(bool, name, _pop, type *ptr, buffer_t table) \
{ \
M_QU3UE_MPMC_CONTRACT(table); \
M_ASSERT (ptr != NULL); \
M_GLOBAL_CONTEXT(); \
unsigned int iC = atomic_load_explicit(&table->ConsoIdx, \
memory_order_relaxed); \
const unsigned int i = (iC & (table->size -1)); \
const unsigned int seq = atomic_load_explicit(&table->Tab[i].seq, \
memory_order_acquire); \
if (seq != 2 * iC) { \
/* Nothing in buffer to consumme (or unlikely preemption) */ \
/* Nothing in buffer to consume (or unlikely preemption) */ \
return false; \
} \
if (M_UNLIKELY (!atomic_compare_exchange_strong_explicit(&table->ConsoIdx, \
@@ -889,27 +767,25 @@ M_F(name, _init)(buffer_t v, size_t size) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, *ptr, table->Tab[i].x); \
} else { \
M_DO_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \
M_CALL_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \
} \
atomic_store_explicit(&table->Tab[i].seq, 2*iC + 1, memory_order_release); \
M_QU3UE_MPMC_CONTRACT(table); \
return true; \
} \
\
M_INLINE void \
M_F(name, _init)(buffer_t buffer, size_t size) \
M_N(void, name, _init, buffer_t buffer, size_t size) \
{ \
M_ASSERT (buffer != NULL); \
M_ASSERT( M_POWEROF2_P(size)); \
M_ASSERT (0 < size && size <= UINT_MAX); \
M_ASSERT(((policy) & (M_BUFFER_STACK|M_BUFFER_THREAD_UNSAFE|M_BUFFER_PUSH_OVERWRITE)) == 0); \
M_ASSERT( M_POWEROF2_P(size) && 0 < size && size <= UINT_MAX); \
M_ASSERT(((policy) & (M_BUFFER_STACK|M_BUFFER_PUSH_OVERWRITE)) == 0); \
M_GLOBAL_CONTEXT(); \
atomic_init(&buffer->ProdIdx, (unsigned int) size); \
atomic_init(&buffer->ConsoIdx, (unsigned int) size); \
buffer->size = (unsigned int) size; \
buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, size); \
buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, 0, size); \
if (M_UNLIKELY_NOMEM (buffer->Tab == NULL)) { \
M_MEMORY_FULL (size*sizeof(M_F(name, _el_ct) )); \
return; \
M_MEMORY_FULL (M_F(name, _el_ct), size); \
} \
for(unsigned int j = 0; j < (unsigned int) size; j++) { \
atomic_init(&buffer->Tab[j].seq, 2*j+1U); \
@@ -920,10 +796,10 @@ M_F(name, _init)(buffer_t v, size_t size) \
M_QU3UE_MPMC_CONTRACT(buffer); \
} \
\
M_INLINE void \
M_F(name, _clear)(buffer_t buffer) \
M_N(void, name, _clear, buffer_t buffer) \
{ \
M_QU3UE_MPMC_CONTRACT(buffer); \
M_GLOBAL_CONTEXT(); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
for(unsigned int j = 0; j < buffer->size; j++) { \
M_CALL_CLEAR(oplist, buffer->Tab[j].x); \
@@ -940,7 +816,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
j = 0; \
} \
} \
M_CALL_FREE(oplist, buffer->Tab); \
M_CALL_FREE(oplist, M_F(name, _el_ct), buffer->Tab, buffer->size); \
buffer->Tab = NULL; /* safer */ \
buffer->size = 3; \
} \
@@ -1033,7 +909,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
M_QU3UE_SPSC_DEF_TYPE(name, type, policy, oplist, buffer_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_QU3UE_SPSC_DEF_CORE(name, type, policy, oplist, buffer_t) \
M_EMPLACE_QUEUE_DEF(name, buffer_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
M_EMPLACE_QUEUE_DEF(name, buffer_t, _emplace, oplist, M_BUFF3R_EMPLACE_QUEUE_GENE)
/* Define the type of a SPSC queue */
#define M_QU3UE_SPSC_DEF_TYPE(name, type, policy, oplist, buffer_t) \
@@ -1050,7 +926,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
atomic_uint consoIdx; /* Can only increase until overflow */ \
unsigned int size; \
M_F(name, _el_ct) *Tab; \
M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_F(name, _el_ct) *); \
M_CACHELINE_ALIGN(align, atomic_uint, unsigned int, M_F(name, _el_ct) *); \
atomic_uint prodIdx; /* Can only increase until overflow */ \
} buffer_t[1]; \
\
@@ -1060,10 +936,10 @@ M_F(name, _init)(buffer_t v, size_t size) \
/* Define the core functionalities of a SPSC queue */
#define M_QU3UE_SPSC_DEF_CORE(name, type, policy, oplist, buffer_t) \
\
M_INLINE bool \
M_F(name, _push)(buffer_t table, type const x) \
M_N(bool, name, _push, buffer_t table, type const x) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_GLOBAL_CONTEXT(); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
@@ -1081,10 +957,10 @@ M_F(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
M_INLINE bool \
M_F(name, _push_move)(buffer_t table, type *x) \
M_N(bool, name, _push_move, buffer_t table, type *x) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_GLOBAL_CONTEXT(); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
@@ -1095,18 +971,18 @@ M_F(name, _init)(buffer_t v, size_t size) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
M_DO_MOVE(oplist, table->Tab[i].x, *x); \
} else { \
M_DO_INIT_MOVE(oplist, table->Tab[i].x, *x); \
M_CALL_INIT_MOVE(oplist, table->Tab[i].x, *x); \
} \
atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \
M_QU3UE_SPSC_CONTRACT(table); \
return true; \
} \
\
M_INLINE bool \
M_F(name, _pop)(type *ptr, buffer_t table) \
M_N(bool, name, _pop, type *ptr, buffer_t table) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_ASSERT (ptr != NULL); \
M_GLOBAL_CONTEXT(); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_relaxed); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -1117,19 +993,19 @@ M_F(name, _init)(buffer_t v, size_t size) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, *ptr , table->Tab[i].x); \
} else { \
M_DO_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \
M_CALL_INIT_MOVE (oplist, *ptr, table->Tab[i].x); \
} \
atomic_store_explicit(&table->consoIdx, r+1, memory_order_release); \
M_QU3UE_SPSC_CONTRACT(table); \
return true; \
} \
\
M_INLINE unsigned \
M_F(name, _push_bulk)(buffer_t table, unsigned n, type const x[]) \
M_N(unsigned, name, _push_bulk, buffer_t table, unsigned n, type const x[]) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_ASSERT (x != NULL); \
M_ASSERT (n <= table->size); \
M_GLOBAL_CONTEXT(); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
@@ -1150,12 +1026,12 @@ M_F(name, _init)(buffer_t v, size_t size) \
return max; \
} \
\
M_INLINE unsigned \
M_F(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \
M_N(unsigned, name, _pop_bulk, unsigned n, type ptr[], buffer_t table) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_ASSERT (ptr != NULL); \
M_ASSERT (n <= table->size); \
M_GLOBAL_CONTEXT(); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
memory_order_relaxed); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -1168,7 +1044,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
M_CALL_SET(oplist, ptr[k], table->Tab[i].x); \
} else { \
M_DO_INIT_MOVE (oplist, ptr[k], table->Tab[i].x); \
M_CALL_INIT_MOVE (oplist, ptr[k], table->Tab[i].x); \
} \
} \
atomic_store_explicit(&table->consoIdx, r+max, memory_order_release); \
@@ -1176,10 +1052,10 @@ M_F(name, _init)(buffer_t v, size_t size) \
return max; \
} \
\
M_INLINE void \
M_F(name, _push_force)(buffer_t table, type const x) \
M_N(void, name, _push_force, buffer_t table, type const x) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_GLOBAL_CONTEXT(); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
memory_order_relaxed); \
unsigned int w = atomic_load_explicit(&table->prodIdx, \
@@ -1238,20 +1114,18 @@ M_F(name, _init)(buffer_t v, size_t size) \
return M_F(name, _size)(v) >= v->size; \
} \
\
M_INLINE void \
M_F(name, _init)(buffer_t buffer, size_t size) \
M_N(void, name, _init, buffer_t buffer, size_t size) \
{ \
M_ASSERT (buffer != NULL); \
M_ASSERT( M_POWEROF2_P(size)); \
M_ASSERT (0 < size && size <= UINT_MAX); \
M_ASSERT(((policy) & (M_BUFFER_STACK|M_BUFFER_THREAD_UNSAFE|M_BUFFER_PUSH_OVERWRITE)) == 0); \
M_ASSERT( M_POWEROF2_P(size) && 0 < size && size <= UINT_MAX); \
M_ASSERT(((policy) & (M_BUFFER_STACK|M_BUFFER_PUSH_OVERWRITE)) == 0); \
M_GLOBAL_CONTEXT(); \
atomic_init(&buffer->prodIdx, (unsigned int) size); \
atomic_init(&buffer->consoIdx, (unsigned int) size); \
buffer->size = (unsigned int) size; \
buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, size); \
buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, 0, size); \
if (M_UNLIKELY_NOMEM (buffer->Tab == NULL)) { \
M_MEMORY_FULL (size*sizeof(M_F(name, _el_ct) )); \
return; \
M_MEMORY_FULL (M_F(name, _el_ct), size); \
} \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
for(unsigned int j = 0; j < (unsigned int) size; j++) { \
@@ -1261,10 +1135,10 @@ M_F(name, _init)(buffer_t v, size_t size) \
M_QU3UE_SPSC_CONTRACT(buffer); \
} \
\
M_INLINE void \
M_F(name, _clear)(buffer_t buffer) \
M_N(void, name, _clear, buffer_t buffer) \
{ \
M_QU3UE_SPSC_CONTRACT(buffer); \
M_GLOBAL_CONTEXT(); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
for(unsigned int j = 0; j < buffer->size; j++) { \
M_CALL_CLEAR(oplist, buffer->Tab[j].x); \
@@ -1281,7 +1155,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
j = 0; \
} \
} \
M_CALL_FREE(oplist, buffer->Tab); \
M_CALL_FREE(oplist, M_F(name, _el_ct), buffer->Tab, buffer->size); \
buffer->Tab = NULL; /* safer */ \
buffer->size = 3; \
} \
@@ -1303,7 +1177,7 @@ M_F(name, _init)(buffer_t v, size_t size) \
/* OPLIST definition of a buffer */
#define M_BUFF3R_OPLIST_P3(name, oplist) \
(INIT(M_C3(m_buff3r_,name, _init)) \
(INIT(M_F(name, _i_init)) \
,INIT_SET(M_F(name, _init_set)) \
,SET(M_F(name, _set)) \
,CLEAR(M_F(name, _clear)) \
@@ -1315,11 +1189,22 @@ M_F(name, _init)(buffer_t v, size_t size) \
,POP(M_F(name,_pop)) \
,OPLIST(oplist) \
,EMPTY_P(M_F(name, _empty_p)), \
,FULL_P(M_F(name, _full_p)), \
,GET_SIZE(M_F(name, _size)) \
)
/********************************** INTERNAL *********************************/
/* Same as M_EMPLACE_QUEUE_GENE but use M_GLOBAL_CONTEXT */
#define M_BUFF3R_EMPLACE_QUEUE_GENE(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_N(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_GET_TYPE oplist data; \
M_GLOBAL_CONTEXT(); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, data, exp_emplace_type); \
M_F(name, _push)(v, data); \
M_CALL_CLEAR(oplist, data); \
}
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define BUFFER_DEF M_BUFFER_DEF
@@ -1333,14 +1218,6 @@ M_F(name, _init)(buffer_t v, size_t size) \
#define buffer_policy_e m_buffer_policy_e
#define BUFFER_QUEUE M_BUFFER_QUEUE
#define BUFFER_STACK M_BUFFER_STACK
#define BUFFER_BLOCKING_PUSH M_BUFFER_BLOCKING_PUSH
#define BUFFER_UNBLOCKING_PUSH M_BUFFER_UNBLOCKING_PUSH
#define BUFFER_BLOCKING_POP M_BUFFER_BLOCKING_POP
#define BUFFER_UNBLOCKING_POP M_BUFFER_UNBLOCKING_POP
#define BUFFER_BLOCKING M_BUFFER_BLOCKING
#define BUFFER_UNBLOCKING M_BUFFER_UNBLOCKING
#define BUFFER_THREAD_SAFE M_BUFFER_THREAD_SAFE
#define BUFFER_THREAD_UNSAFE M_BUFFER_THREAD_UNSAFE
#define BUFFER_PUSH_INIT_POP_MOVE M_BUFFER_PUSH_INIT_POP_MOVE
#define BUFFER_PUSH_OVERWRITE M_BUFFER_PUSH_OVERWRITE
#define BUFFER_DEFERRED_POP M_BUFFER_DEFERRED_POP
-835
View File
@@ -1,835 +0,0 @@
/*
* M*LIB - Concurrent memory pool allocator
*
* Copyright (c) 2017-2024, 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]; \
\
M_INLINE void \
M_F(name, _slist_init)(M_F(name, _slist_ct) list) \
{ \
*list = NULL; \
} \
\
M_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; \
} \
\
M_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; \
} \
\
M_INLINE bool \
M_F(name, _slist_empty_p)(M_F(name, _slist_ct) list) \
{ \
return *list == NULL; \
} \
\
M_INLINE void \
M_F(name, _slist_move)(M_F(name, _slist_ct) list, \
M_F(name, _slist_ct) src) \
{ \
*list = *src; \
*src = NULL; \
} \
\
M_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 successful push of NB in Q1. NA.next is set to NB.
- Thread 2 performs a successful pop of NA in Q1
- Thread 2 performs a successful 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 alignment 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]; \
\
M_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); \
} \
\
M_INLINE bool \
M_F(name, _lflist_empty_p)(M_F(name, _lflist_ct) list) \
{ \
return atomic_load(&list->tail) == atomic_load(&list->head); \
} \
\
M_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); \
} \
\
M_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 itself, \
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 */ \
M_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 enough 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; \
} \
\
M_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) \
\
M_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 dimensioned to avoid calling the system allocator during
the normal processing.
Then each thread pushes 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 that 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); \
\
M_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); \
} \
\
M_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 */ \
M_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); \
} \
} \
\
M_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; \
} \
\
M_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? */ \
} \
\
M_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); \
} \
} \
\
M_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];
M_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;
}
M_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);
}
M_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);
}
M_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);
}
M_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);
}
M_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;
}
M_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;
M_INLINE void
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_ct *t)
{
m_vlapool_slist_init(t->to_be_reclaimed);
}
M_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 */
M_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);
}
}
M_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;
}
M_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? */
}
M_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));
}
M_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 logically 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
-907
View File
@@ -1,907 +0,0 @@
/*
* M*LIB - Basic Protected Concurrent module over container.
*
* Copyright (c) 2017-2024, 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)), GENTYPE(struct M_F(name,_s)*) \
,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)),) \
)
/******************************** 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. */ \
M_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. */ \
M_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 */ \
M_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 */ \
M_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 */ \
M_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 */ \
M_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 */ \
M_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 */ \
M_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 */ \
M_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)( \
M_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)( \
M_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)( \
M_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 mutexes 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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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)( \
M_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 mutual mutexes */ \
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)( \
M_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)( \
M_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)( \
M_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)( \
M_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; \
atomic_uint num_reader; /* number of reader threads */ \
atomic_uint num_waiting_writer; /* number of waiting writer threads */ \
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) \
\
M_INLINE void \
M_F(name, _internal_init)(concurrent_t out) \
{ \
m_mutex_init(out->lock); \
atomic_init(&out->num_reader, 0); \
atomic_init(&out->num_waiting_writer, 0); \
m_cond_init(out->there_is_data); \
out->self = out; \
M_C0NCURRENT_CONTRACT(out); \
} \
\
M_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; \
} \
\
M_INLINE void \
M_F(name, _read_lock)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
struct M_F(name, _s) *self = out->self; \
m_core_backoff_ct backoff; \
m_core_backoff_init(backoff); \
while (true) { \
unsigned int num = atomic_load(&self->num_reader); \
/* To avoid reader threads that starve writer threads \
Disable sharing of lock for reader threads if some writer thread \
waits */ \
/* Otherwise try to reuse the lock opened by another reader thread */ \
if (num != 0 \
&& atomic_load(&self->num_waiting_writer) == 0 \
&& atomic_compare_exchange_strong(&self->num_reader, &num, num+1)) { \
break; \
} \
/* Otherwise try to get the lock ourself */ \
if (m_mutex_trylock(self->lock)) { \
atomic_store(&self->num_reader, 1); \
break; \
} \
/* Perform exponential backoff to avoid monopolizing the memory bus */ \
m_core_backoff_wait(backoff); \
} \
m_core_backoff_clear(backoff); \
} \
\
M_INLINE void \
M_F(name, _read_unlock)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
struct M_F(name, _s) *self = out->self; \
unsigned int num = atomic_fetch_sub(&self->num_reader, 1); \
if (num == 1) { \
/* We are the last reader thread: release the lock */ \
m_mutex_unlock(self->lock); \
} \
} \
\
M_INLINE void \
M_F(name, _write_lock)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
atomic_fetch_add(&out->num_waiting_writer, 1); \
m_mutex_lock (out->lock); \
atomic_fetch_sub(&out->num_waiting_writer, 1); \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
} \
\
M_INLINE void \
M_F(name, _write_unlock)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
m_mutex_unlock (out->lock); \
} \
\
M_INLINE void \
M_F(name, _read_wait)(const concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
struct M_F(name, _s) *self = out->self; \
m_cond_wait(self->there_is_data, self->lock); \
} \
\
M_INLINE void \
M_F(name, _write_wait)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
m_cond_wait(out->there_is_data, out->lock); \
} \
\
M_INLINE void \
M_F(name, _write_signal)(concurrent_t out) \
{ \
M_C0NCURRENT_CONTRACT(out); \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
m_cond_broadcast(out->there_is_data); \
} \
/******************************** 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
+335 -229
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Extended Pre-processing macros module
*
* Copyright (c) 2017-2024, 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:
@@ -70,8 +70,8 @@
/* Define M*LIB version */
#define M_CORE_VERSION_MAJOR 0
#define M_CORE_VERSION_MINOR 7
#define M_CORE_VERSION_PATCHLEVEL 3
#define M_CORE_VERSION_MINOR 8
#define M_CORE_VERSION_PATCHLEVEL 0
/* M_ASSUME is equivalent to M_ASSERT, but gives hints to compiler
about how to optimize the code if NDEBUG is defined.
@@ -142,7 +142,6 @@
# define M_ATTR_COLD_FUNCTION
#endif
/* Prefetch . */
#if defined(__GNUC__)
# define M_PREFETCH(p) __builtin_prefetch((p), 0, 0)
@@ -153,6 +152,17 @@
} while (0)
#endif
/* Definition of the typeof keyword
In C23, we can use the standard keyword.
Otherwise we use some extensions of the compilers. */
#ifdef _MSC_VER
#define m_typeof(x) decltype(x)
#elif defined(__GNUC__)
#define m_typeof(x) __typeof__(x)
#else
#define m_typeof(x) typeof(x)
#endif
/* Ignore some warnings detected by some compilers in the library.
* Whatever we do, there is some warnings that cannot be fixed.
* So they are ignored in order to avoid polluting the user with
@@ -353,48 +363,122 @@ M_BEGIN_PROTECTED_CODE
explicit cast) */
/* Define allocators for object:
* void *M_MEMORY_ALLOC(type): Return a pointer to a new object of type 'type'
* void *M_MEMORY_ALLOC(ctx, type):
* Return a pointer to a new object of type 'type'
* It returns NULL in case of memory allocation failure.
* void M_MEMORY_DEL(ptr): Free the object associated to the pointer.
* void M_MEMORY_DEL(ctx, ptr):
* Free the object associated to the pointer.
* By default, 'context' does nothing.
* 'context' is the context parameter given to the memory functions to identify where to create the object.
* It exists only if M_USE_CONTEXT has been globally defined.
*/
#ifndef M_MEMORY_ALLOC
#ifdef __cplusplus
# include <cstdlib>
# define M_MEMORY_ALLOC(type) ((type*)std::malloc (sizeof (type)))
# define M_MEMORY_DEL(ptr) std::free(ptr)
# define M_MEMORY_ALLOC(ctx, type) ((type*)std::malloc (sizeof (type)))
# define M_MEMORY_DEL(ctx, ptr) std::free(ptr)
#else
# define M_MEMORY_ALLOC(type) malloc (sizeof (type))
# define M_MEMORY_DEL(ptr) free(ptr)
# define M_MEMORY_ALLOC(ctx, type) malloc (sizeof (type))
# define M_MEMORY_DEL(ctx, ptr) free(ptr)
#endif
#endif
/* Define allocators for array
* void *M_MEMORY_REALLOC(type, ptr, n): Return a pointer to a new array of 'n' object of type 'type'
* If ptr is NULL, it creates a new array.
* If ptr is not null, it reallocates the given array to the new size.
* void *M_MEMORY_REALLOC(context, type, ptr, o, n, perm):
* Return a pointer to a new array of 'n' object of type 'type'.
* If ptr is NULL, it creates a new array of type 'type' and size 'n'
* If ptr is not null, it reallocates the given array of type 'type' and size 'o' to the new size.
* It returns NULL in case of memory allocation failure.
* void M_MEMORY_FREE(ptr): Free the object associated to the array.
* void M_MEMORY_FREE(type, ptr, o):
* Free the object associated to the array of type 'type' at base 'ptr' of size 'o'.
* By default, 'o' size and 'context' do nothing.
* 'context' is the context parameter given to the memory functions to identify where to create the object.
* It exists only if M_USE_CONTEXT has been globally defined.
*/
#ifndef M_MEMORY_REALLOC
#ifdef __cplusplus
# include <cstdlib>
# define M_MEMORY_REALLOC(type, ptr, n) \
# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) \
((type*) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : std::realloc ((ptr), (n)*sizeof (type))))
# define M_MEMORY_FREE(ptr) std::free(ptr)
# define M_MEMORY_FREE(ctx, type, ptr, o) std::free(ptr)
#else
# define M_MEMORY_REALLOC(type, ptr, n) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : realloc ((ptr), (n)*sizeof (type)))
# define M_MEMORY_FREE(ptr) free(ptr)
# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) (M_UNLIKELY ((n) > SIZE_MAX / sizeof(type)) ? NULL : realloc ((ptr), (n)*sizeof (type)))
# define M_MEMORY_FREE(ctx, type, ptr, o) free(ptr)
#endif
#endif
/* This macro is called on memory allocation failure.
* By default, it raises a fatal error.
* By default, it raises a fatal error and abort execution.
* It shall never continue execution.
* NOTE: Can be overloaded by user code.
*/
#ifndef M_MEMORY_FULL
#define M_MEMORY_FULL(size) \
M_RAISE_FATAL("Cannot allocate %zu bytes of memory at (%s:%s:%d).\n", \
(size_t) (size), __FILE__, __func__, __LINE__)
#define M_MEMORY_FULL(type, number) \
M_RAISE_FATAL("Cannot allocate %zu objects of type '%s' (size '%zu') at (%s:%s:%d).\n", \
(size_t) (number), M_AS_STR(type), sizeof (type), \
__FILE__, __func__, __LINE__)
#endif
// Default global memory context is to consider the global context as '0' if it is not defined.
#ifndef M_USE_GLOBAL_CONTEXT
#define M_USE_GLOBAL_CONTEXT 0
#endif
/* In order to support local context for memory functions, the prototypes and usages of some functions are modified
* so that they can accept a first argument 'm_context' which is the memory context parameter.
*
* For some functions, there is no meaning of a local memory context parameter:
* for all data shared between threads, only a global memory context has any meaning.
* For such cases, the memory context parameter is globally requested to the user through M_USE_GLOBAL_POOL or set to 0.
*
* This is controlled through M_USE_CONTEXT parameter that define the type of the memory context parameter
* to be used globally for all M*LIB. There is no possibility to support only some containers and not others.
*
* Several macros are defined **globally** to support such features and being internally used by M*LIB containers.
* They expand to nothing if the memory context parameter is not requested:
* * M_P_EXPAND: expand to the first parameter 'm_context' of type M_USE_CONTEXT for a function that may use memory context.
* * M_P_EXPAND_void: same but assume that there is no argument in the initial function.
* * M_R_EXPAND: expand to the first parameter 'm_context' (followed by a comma)
* * M_R_EXPAND_void: same but assume that there is no argument
* * M_R: add the parameter m_context to the function call if needed,
* * M_P: define the prototype of a function that may have the parameter m_context.
* * M_N: define the prototype of a function that don't need the parameter m_context.
* * M_UNUSED_CONTEXT: avoid warning of unused m_context argument
* * M_GLOBAL_CONTEXT: define m_context to zero, meaning it is a global memory context.
*/
#ifndef M_USE_CONTEXT
// Memory context parameter not used for memory function.
# define M_P_EXPAND
# define M_P_EXPAND_void void
# define M_R_EXPAND
# define M_R_EXPAND_void
# define M_R
# define M_UNUSED_CONTEXT()
# define M_GLOBAL_CONTEXT()
#else
// Memory context parameter is used for function using memory. (local definition to the function).
// Expand to a prototype to a function that may not trigger exception
# define M_P_EXPAND M_USE_CONTEXT m_context,
# define M_P_EXPAND_void M_USE_CONTEXT m_context
# define M_R_EXPAND m_context ,
# define M_R_EXPAND_void m_context
# define M_R(...) (m_context, __VA_ARGS__)
# define M_UNUSED_CONTEXT() (void) m_context
# define M_GLOBAL_CONTEXT() M_USE_CONTEXT m_context = { M_USE_GLOBAL_CONTEXT }; (void) m_context
#endif
// M_P: Expand to a prototype of a function that may trigger exception / accept memory context
// M_N: Expand to a prototype of a function that may not trigger exception or accept memory context
// If not user defined M_F or memory context, no overloaded M_INLINE,
// simplify the definition of M_P/M_N to provide faster compile time.
// (faster build: 0.54s instead of 0.80s in extreme expanding)
#if !defined(M_USE_CONTEXT) && !defined(M_F) && !defined(M_USE_FINE_GRAINED_LINKAGE) && !defined(M_USE_DEF) && !defined(M_USE_DECL)
# define M_P(rettype, name, suffix, ...) static inline rettype name ## suffix(__VA_ARGS__)
# define M_N(rettype, name, suffix, ...) static inline rettype name ## suffix(__VA_ARGS__)
#else
# define M_P(rettype, name, suffix, ...) M_INLINE rettype M_F(name, suffix)(M_P_EXPAND __VA_ARGS__)
# define M_N(rettype, name, suffix, ...) M_INLINE rettype M_F(name, suffix)(__VA_ARGS__)
#endif
@@ -429,6 +513,7 @@ M_BEGIN_PROTECTED_CODE
* NOTE: Can be overriden by user if it needs to keep finer access
* on the assertions.
*/
//TODO: If NDEBUG, simply do nothing "no expression", to speed up compilation speed.
#ifndef M_ASSERT
#define M_ASSERT(expr) assert(expr)
#endif
@@ -439,6 +524,7 @@ M_BEGIN_PROTECTED_CODE
* NOTE: Can be overriden by user if it needs to keep finer access
* on the assertions.
*/
//TODO: If NDEBUG, simply do nothing "no expression", to speed up compilation speed.
#ifndef M_ASSERT_SLOW
# if defined(M_USE_ADDITIONAL_CHECKS) && M_USE_ADDITIONAL_CHECKS
# define M_ASSERT_SLOW(n) assert(n)
@@ -2761,7 +2847,7 @@ M_INLINE char m_core_str_nospace(const char **str)
/* Transform a C variable into a m_string_t (needs m-string.h) */
#define M_GET_STRING_ARG(str, x, append) \
(append ? m_string_cat_printf : m_string_printf) (str, M_PRINTF_FORMAT(x), M_CORE_PRINTF_ARG(x))
(append ? m_string_cat_printf : m_string_printf) M_R(str, M_PRINTF_FORMAT(x), M_CORE_PRINTF_ARG(x))
/* No use of GET_STR if no inclusion of m-string */
#define M_GET_STR_METHOD_FOR_DEFAULT_TYPE /* */
@@ -3370,87 +3456,88 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
/* Helper internal macros to make M_GET_METHOD works.
List of supported methods for an oplist */
#define M_INIT_INIT(a) ,a,
#define M_INIT_SET_INIT_SET(a) ,a,
#define M_INIT_MOVE_INIT_MOVE(a) ,a,
#define M_INIT_WITH_INIT_WITH(a) ,a,
#define M_SWAP_SWAP(a) ,a,
#define M_SET_SET(a) ,a,
#define M_MOVE_MOVE(a) ,a,
#define M_CLEAR_CLEAR(a) ,a,
#define M_HASH_HASH(a) ,a,
#define M_EQUAL_EQUAL(a) ,a,
#define M_CMP_CMP(a) ,a,
#define M_TYPE_TYPE(a) ,a,
#define M_SUBTYPE_SUBTYPE(a) ,a,
#define M_GENTYPE_GENTYPE(a) ,a,
#define M_SUBTYPE_PTR_SUBTYPE_PTR(a) ,a,
#define M_NAME_NAME(a) ,a,
#define M_OPLIST_OPLIST(a) ,a,
#define M_SORT_SORT(a) ,a,
#define M_SPLICE_BACK_SPLICE_BACK(a) ,a,
#define M_SPLICE_AT_SPLICE_AT(a) ,a,
#define M_IT_TYPE_IT_TYPE(a) ,a,
#define M_IT_FIRST_IT_FIRST(a) ,a,
#define M_IT_LAST_IT_LAST(a) ,a,
#define M_IT_END_IT_END(a) ,a,
#define M_IT_SET_IT_SET(a) ,a,
#define M_IT_END_P_IT_END_P(a) ,a,
#define M_IT_LAST_P_IT_LAST_P(a) ,a,
#define M_IT_EQUAL_P_IT_EQUAL_P(a) ,a,
#define M_IT_NEXT_IT_NEXT(a) ,a,
#define M_IT_PREVIOUS_IT_PREVIOUS(a) ,a,
#define M_IT_REF_IT_REF(a) ,a,
#define M_IT_CREF_IT_CREF(a) ,a,
#define M_IT_REMOVE_IT_REMOVE(a) ,a,
#define M_IT_INSERT_IT_INSERT(a) ,a,
#define M_EMPTY_P_EMPTY_P(a) ,a,
#define M_ADD_ADD(a) ,a,
#define M_SUB_SUB(a) ,a,
#define M_MUL_MUL(a) ,a,
#define M_DIV_DIV(a) ,a,
#define M_RESET_RESET(a) ,a,
#define M_KEY_TYPE_KEY_TYPE(a) ,a,
#define M_VALUE_TYPE_VALUE_TYPE(a) ,a,
#define M_KEY_OPLIST_KEY_OPLIST(a) ,a,
#define M_VALUE_OPLIST_VALUE_OPLIST(a) ,a,
#define M_GET_KEY_GET_KEY(a) ,a,
#define M_SET_KEY_SET_KEY(a) ,a,
#define M_SAFE_GET_KEY_SAFE_GET_KEY(a) ,a,
#define M_ERASE_KEY_ERASE_KEY(a) ,a,
#define M_GET_SIZE_GET_SIZE(a) ,a,
#define M_PUSH_PUSH(a) ,a,
#define M_POP_POP(a) ,a,
#define M_PUSH_MOVE_PUSH_MOVE(a) ,a,
#define M_POP_MOVE_POP_MOVE(a) ,a,
#define M_REVERSE_REVERSE(a) ,a,
#define M_GET_STR_GET_STR(a) ,a,
#define M_PARSE_STR_PARSE_STR(a) ,a,
#define M_OUT_STR_OUT_STR(a) ,a,
#define M_IN_STR_IN_STR(a) ,a,
#define M_OUT_SERIAL_OUT_SERIAL(a) ,a,
#define M_IN_SERIAL_IN_SERIAL(a) ,a,
#define M_SEPARATOR_SEPARATOR(a) ,a,
#define M_EXT_ALGO_EXT_ALGO(a) ,a,
#define M_INC_ALLOC_INC_ALLOC(a) ,a,
#define M_OOR_SET_OOR_SET(a) ,a,
#define M_OOR_EQUAL_OOR_EQUAL(a) ,a,
#define M_LIMITS_LIMITS(a) ,a,
#define M_PROPERTIES_PROPERTIES(a) ,a,
#define M_EMPLACE_TYPE_EMPLACE_TYPE(a) ,a,
#define M_X_INIT_INIT(a) ,a,
#define M_X_INIT_SET_INIT_SET(a) ,a,
#define M_X_INIT_MOVE_INIT_MOVE(a) ,a,
#define M_X_INIT_WITH_INIT_WITH(a) ,a,
#define M_X_SWAP_SWAP(a) ,a,
#define M_X_SET_SET(a) ,a,
#define M_X_MOVE_MOVE(a) ,a,
#define M_X_CLEAR_CLEAR(a) ,a,
#define M_X_HASH_HASH(a) ,a,
#define M_X_EQUAL_EQUAL(a) ,a,
#define M_X_CMP_CMP(a) ,a,
#define M_X_TYPE_TYPE(a) ,a,
#define M_X_SUBTYPE_SUBTYPE(a) ,a,
#define M_X_GENTYPE_GENTYPE(a) ,a,
#define M_X_SUBTYPE_PTR_SUBTYPE_PTR(a) ,a,
#define M_X_NAME_NAME(a) ,a,
#define M_X_FIELD_FIELD(a) ,a,
#define M_X_OPLIST_OPLIST(a) ,a,
#define M_X_SORT_SORT(a) ,a,
#define M_X_SPLICE_BACK_SPLICE_BACK(a) ,a,
#define M_X_SPLICE_AT_SPLICE_AT(a) ,a,
#define M_X_IT_TYPE_IT_TYPE(a) ,a,
#define M_X_IT_FIRST_IT_FIRST(a) ,a,
#define M_X_IT_LAST_IT_LAST(a) ,a,
#define M_X_IT_END_IT_END(a) ,a,
#define M_X_IT_SET_IT_SET(a) ,a,
#define M_X_IT_END_P_IT_END_P(a) ,a,
#define M_X_IT_LAST_P_IT_LAST_P(a) ,a,
#define M_X_IT_EQUAL_P_IT_EQUAL_P(a) ,a,
#define M_X_IT_NEXT_IT_NEXT(a) ,a,
#define M_X_IT_PREVIOUS_IT_PREVIOUS(a) ,a,
#define M_X_IT_REF_IT_REF(a) ,a,
#define M_X_IT_CREF_IT_CREF(a) ,a,
#define M_X_IT_REMOVE_IT_REMOVE(a) ,a,
#define M_X_IT_INSERT_IT_INSERT(a) ,a,
#define M_X_EMPTY_P_EMPTY_P(a) ,a,
#define M_X_FULL_P_FULL_P(a) ,a,
#define M_X_ADD_ADD(a) ,a,
#define M_X_SUB_SUB(a) ,a,
#define M_X_MUL_MUL(a) ,a,
#define M_X_DIV_DIV(a) ,a,
#define M_X_RESET_RESET(a) ,a,
#define M_X_KEY_TYPE_KEY_TYPE(a) ,a,
#define M_X_VALUE_TYPE_VALUE_TYPE(a) ,a,
#define M_X_KEY_OPLIST_KEY_OPLIST(a) ,a,
#define M_X_VALUE_OPLIST_VALUE_OPLIST(a) ,a,
#define M_X_GET_KEY_GET_KEY(a) ,a,
#define M_X_SET_KEY_SET_KEY(a) ,a,
#define M_X_SAFE_GET_KEY_SAFE_GET_KEY(a) ,a,
#define M_X_ERASE_KEY_ERASE_KEY(a) ,a,
#define M_X_GET_SIZE_GET_SIZE(a) ,a,
#define M_X_PUSH_PUSH(a) ,a,
#define M_X_POP_POP(a) ,a,
#define M_X_PUSH_MOVE_PUSH_MOVE(a) ,a,
#define M_X_POP_MOVE_POP_MOVE(a) ,a,
#define M_X_REVERSE_REVERSE(a) ,a,
#define M_X_GET_STR_GET_STR(a) ,a,
#define M_X_PARSE_STR_PARSE_STR(a) ,a,
#define M_X_OUT_STR_OUT_STR(a) ,a,
#define M_X_IN_STR_IN_STR(a) ,a,
#define M_X_OUT_SERIAL_OUT_SERIAL(a) ,a,
#define M_X_IN_SERIAL_IN_SERIAL(a) ,a,
#define M_X_SEPARATOR_SEPARATOR(a) ,a,
#define M_X_EXT_ALGO_EXT_ALGO(a) ,a,
#define M_X_INC_ALLOC_INC_ALLOC(a) ,a,
#define M_X_OOR_SET_OOR_SET(a) ,a,
#define M_X_OOR_EQUAL_OOR_EQUAL(a) ,a,
#define M_X_LIMITS_LIMITS(a) ,a,
#define M_X_PROPERTIES_PROPERTIES(a) ,a,
#define M_X_EMPLACE_TYPE_EMPLACE_TYPE(a) ,a,
// As attribute customization
#define M_NEW_NEW(a) ,a,
#define M_DEL_DEL(a) ,a,
#define M_REALLOC_REALLOC(a) ,a,
#define M_FREE_FREE(a) ,a,
#define M_MEMPOOL_MEMPOOL(a) ,a,
#define M_MEMPOOL_LINKAGE_MEMPOOL_LINKAGE(a) ,a,
#define M_SIZE_SIZE(a) ,a,
#define M_CONTEXT_CONTEXT(a) ,a,
#define M_POLICY_POLICY(a) ,a,
#define M_X_NEW_NEW(a) ,a,
#define M_X_DEL_DEL(a) ,a,
#define M_X_REALLOC_REALLOC(a) ,a,
#define M_X_FREE_FREE(a) ,a,
#define M_X_SIZE_SIZE(a) ,a,
#define M_X_CONTEXT_CONTEXT(a) ,a,
#define M_X_POLICY_POLICY(a) ,a,
// As properties only
#define M_LET_AS_INIT_WITH_LET_AS_INIT_WITH(a) ,a,
#define M_NOCLEAR_NOCLEAR(a) ,a,
#define M_X_LET_AS_INIT_WITH_LET_AS_INIT_WITH(a) ,a,
#define M_X_NOCLEAR_NOCLEAR(a) ,a,
#define M_X_THREADSAFE_THREADSAFE(a) ,a,
/* From an oplist - an unorded list of methods : like "INIT(mpz_init),CLEAR(mpz_clear),SET(mpz_set)" -
Return the given method in the oplist or the default method.
@@ -3458,12 +3545,12 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
M_GET_METHOD(INIT, my_default, INIT(mpz_init),CLEAR(mpz_clear),SET(mpz_set)) --> mpz_init
M_GET_METHOD(INIT, my_default, CLEAR(mpz_clear),SET(mpz_set)) --> my_default */
#define M_GET_METHOD(method, method_default, ...) \
M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,)
M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_X_, method, _), __VA_ARGS__), method_default,)
/* Get the given method */
#define M_GET_INIT(...) M_GET_METHOD(INIT, M_INIT_DEFAULT, __VA_ARGS__)
#define M_GET_INIT_SET(...) M_GET_METHOD(INIT_SET, M_SET_DEFAULT, __VA_ARGS__)
#define M_GET_INIT_MOVE(...) M_GET_METHOD(INIT_MOVE, M_NO_DEF_INIT_MOVE, __VA_ARGS__)
#define M_GET_INIT_MOVE(...) M_GET_METHOD(INIT_MOVE, M_SET_DEFAULT, __VA_ARGS__)
#define M_GET_INIT_WITH(...) M_GET_METHOD(INIT_WITH, M_NO_DEF_INIT_WITH, __VA_ARGS__)
#define M_GET_SET(...) M_GET_METHOD(SET, M_SET_DEFAULT, __VA_ARGS__)
#define M_GET_MOVE(...) M_GET_METHOD(MOVE, M_NO_DEF_MOVE, __VA_ARGS__)
@@ -3477,6 +3564,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_GET_SUBTYPE_PTR(...) M_GET_METHOD(SUBTYPE_PTR, M_NO_DEF_SUBTYPE_PTR, __VA_ARGS__)
#define M_GET_GENTYPE(...) M_GET_METHOD(GENTYPE, M_NO_DEF_GENTYPE, __VA_ARGS__)
#define M_GET_NAME(...) M_GET_METHOD(NAME, M_NO_DEF_NAME, __VA_ARGS__)
#define M_GET_FIELD(...) M_GET_METHOD(FIELD, M_NO_DEF_FIELD, __VA_ARGS__)
#define M_GET_OPLIST(...) M_GET_METHOD(OPLIST, (), __VA_ARGS__)
#define M_GET_SORT(...) M_GET_METHOD(SORT, M_NO_DEF_SORT, __VA_ARGS__)
#define M_GET_SPLICE_BACK(...) M_GET_METHOD(SPLICE_BACK, M_NO_DEF_SPLICE_BACK, __VA_ARGS__)
@@ -3496,6 +3584,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_GET_IT_REMOVE(...) M_GET_METHOD(IT_REMOVE, M_NO_DEF_IT_REMOVE, __VA_ARGS__)
#define M_GET_IT_INSERT(...) M_GET_METHOD(IT_INSERT, M_NO_DEF_IT_INSERT, __VA_ARGS__)
#define M_GET_EMPTY_P(...) M_GET_METHOD(EMPTY_P, M_NO_DEF_EMPTY_P, __VA_ARGS__)
#define M_GET_FULL_P(...) M_GET_METHOD(FULL_P, M_FALSE_DEFAULT, __VA_ARGS__)
#define M_GET_ADD(...) M_GET_METHOD(ADD, M_ADD_DEFAULT, __VA_ARGS__)
#define M_GET_SUB(...) M_GET_METHOD(SUB, M_SUB_DEFAULT, __VA_ARGS__)
#define M_GET_MUL(...) M_GET_METHOD(MUL, M_MUL_DEFAULT, __VA_ARGS__)
@@ -3529,12 +3618,10 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_GET_PROPERTIES(...) M_GET_METHOD(PROPERTIES, (), __VA_ARGS__)
#define M_GET_EMPLACE_TYPE(...) M_GET_METHOD(EMPLACE_TYPE, , __VA_ARGS__)
// As attribute customization
#define M_GET_NEW(...) M_GET_METHOD(NEW, M_NEW_DEFAULT, __VA_ARGS__)
#define M_GET_DEL(...) M_GET_METHOD(DEL, M_DEL_DEFAULT, __VA_ARGS__)
#define M_GET_REALLOC(...) M_GET_METHOD(REALLOC, M_REALLOC_DEFAULT, __VA_ARGS__)
#define M_GET_FREE(...) M_GET_METHOD(FREE, M_FREE_DEFAULT, __VA_ARGS__)
#define M_GET_MEMPOOL(...) M_GET_METHOD(MEMPOOL, M_NO_DEF_MEMPOOL, __VA_ARGS__)
#define M_GET_MEMPOOL_LINKAGE(...) M_GET_METHOD(MEMPOOL_LINKAGE, , __VA_ARGS__)
#define M_GET_NEW(...) M_GET_METHOD(NEW, M_MEMORY_ALLOC, __VA_ARGS__)
#define M_GET_DEL(...) M_GET_METHOD(DEL, M_MEMORY_DEL, __VA_ARGS__)
#define M_GET_REALLOC(...) M_GET_METHOD(REALLOC, M_MEMORY_REALLOC, __VA_ARGS__)
#define M_GET_FREE(...) M_GET_METHOD(FREE, M_MEMORY_FREE, __VA_ARGS__)
#define M_GET_SIZE(...) M_GET_METHOD(SIZE, 0, __VA_ARGS__)
#define M_GET_CONTEXT(...) M_GET_METHOD(CONTEXT, 0, __VA_ARGS__)
#define M_GET_POLICY(...) M_GET_METHOD(POLICY, 0, __VA_ARGS__)
@@ -3549,7 +3636,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_CALL_MOVE(oplist, ...) M_APPLY_API(M_GET_MOVE oplist, oplist, __VA_ARGS__)
#define M_CALL_SWAP(oplist, ...) M_APPLY_API(M_GET_SWAP oplist, oplist, __VA_ARGS__)
#define M_CALL_CLEAR(oplist, ...) M_APPLY_API(M_GET_CLEAR oplist, oplist, __VA_ARGS__)
#define M_CALL_NEW(oplist, ...) M_APPLY_API(M_GET_NEW oplist, oplist, __VA_ARGS__)
#define M_CALL_HASH(oplist, ...) M_APPLY_API(M_GET_HASH oplist, oplist, __VA_ARGS__)
#define M_CALL_EQUAL(oplist, ...) M_APPLY_API(M_GET_EQUAL oplist, oplist, __VA_ARGS__)
#define M_CALL_CMP(oplist, ...) M_APPLY_API(M_GET_CMP oplist, oplist, __VA_ARGS__)
@@ -3558,6 +3644,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
//#define M_CALL_SUBTYPE_PTR(oplist, ...) M_APPLY_API(M_GET_SUBTYPE_PTR oplist, oplist, __VA_ARGS__)
//#define M_CALL_GENTYPE(oplist, ...) M_APPLY_API(M_GET_GENTYPE oplist, oplist, __VA_ARGS__)
//#define M_CALL_NAME(oplist, ...) M_APPLY_API(M_GET_NAME oplist, oplist, __VA_ARGS__)
//#define M_CALL_FIELD(oplist, ...) M_APPLY_API(M_GET_FIELD oplist, oplist, __VA_ARGS__)
//#define M_CALL_OPLIST(oplist, ...) M_APPLY_API(M_GET_OPLIST oplist, oplist, __VA_ARGS__)
#define M_CALL_SORT(oplist, ...) M_APPLY_API(M_GET_SORT oplist, oplist, __VA_ARGS__)
#define M_CALL_SPLICE_BACK(oplist, ...) M_APPLY_API(M_GET_SPLICE_BACK oplist, oplist, __VA_ARGS__)
@@ -3577,6 +3664,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_CALL_IT_REMOVE(oplist, ...) M_APPLY_API(M_GET_IT_REMOVE oplist, oplist, __VA_ARGS__)
#define M_CALL_IT_INSERT(oplist, ...) M_APPLY_API(M_GET_IT_INSERT oplist, oplist, __VA_ARGS__)
#define M_CALL_EMPTY_P(oplist, ...) M_APPLY_API(M_GET_EMPTY_P oplist, oplist, __VA_ARGS__)
#define M_CALL_FULL_P(oplist, ...) M_APPLY_API(M_GET_FULL_P oplist, oplist, __VA_ARGS__)
#define M_CALL_ADD(oplist, ...) M_APPLY_API(M_GET_ADD oplist, oplist, __VA_ARGS__)
#define M_CALL_SUB(oplist, ...) M_APPLY_API(M_GET_SUB oplist, oplist, __VA_ARGS__)
#define M_CALL_MUL(oplist, ...) M_APPLY_API(M_GET_MUL oplist, oplist, __VA_ARGS__)
@@ -3605,12 +3693,11 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_CALL_OOR_EQUAL(oplist, ...) M_APPLY_API(M_GET_OOR_EQUAL oplist, oplist, __VA_ARGS__)
//#define M_CALL_PROPERTIES(oplist, ...) M_APPLY_API(M_GET_PROPERTIES oplist, oplist, __VA_ARGS__)
//#define M_CALL_EMPLACE_TYPE(oplist, ...) M_APPLY_API(M_GET_EMPLACE_TYPE oplist, oplist, __VA_ARGS__)
// As attribute customization
#define M_CALL_DEL(oplist, ...) M_APPLY_API(M_GET_DEL oplist, oplist, __VA_ARGS__)
#define M_CALL_REALLOC(oplist, ...) M_APPLY_API(M_GET_REALLOC oplist, oplist, __VA_ARGS__)
#define M_CALL_FREE(oplist, ...) M_APPLY_API(M_GET_FREE oplist, oplist, __VA_ARGS__)
#define M_CALL_MEMPOOL(oplist, ...) M_APPLY_API(M_GET_MEMPOOL oplist, oplist, __VA_ARGS__)
#define M_CALL_MEMPOOL_LINKAGE(oplist, ...) M_APPLY_API(M_GET_MEMPOOL_LINKAGE oplist, oplist, __VA_ARGS__)
// m_context is always added here!
#define M_CALL_NEW(oplist, ...) M_APPLY_API(M_GET_NEW oplist, oplist, m_context, __VA_ARGS__)
#define M_CALL_DEL(oplist, ...) M_APPLY_API(M_GET_DEL oplist, oplist, m_context, __VA_ARGS__)
#define M_CALL_REALLOC(oplist, ...) M_APPLY_API(M_GET_REALLOC oplist, oplist, m_context, __VA_ARGS__)
#define M_CALL_FREE(oplist, ...) M_APPLY_API(M_GET_FREE oplist, oplist, m_context, __VA_ARGS__)
//#define M_CALL_SIZE(oplist, ...) M_APPLY_API(M_GET_SIZE oplist, oplist, __VA_ARGS__)
//#define M_CALL_CONTEXT(oplist, ...) M_APPLY_API(M_GET_CONTEXT oplist, oplist, __VA_ARGS__)
//#define M_CALL_POLICY(oplist, ...) M_APPLY_API(M_GET_POLICY oplist, oplist, __VA_ARGS__)
@@ -3644,6 +3731,14 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_OPLAPI_5(method, oplist, x, ...) , x = M_DELAY3(method)(oplist, __VA_ARGS__)
#define M_OPLAPI_6(method, oplist, x, ...) , M_DELAY3(method)(&x, &__VA_ARGS__)
#define M_OPLAPI_7(method, oplist, x, ...) , M_DELAY3(method)(oplist, &x, &__VA_ARGS__)
#define M_OPLAPI_0P(method, oplist, ...) , M_DELAY3(method)(m_context, __VA_ARGS__)
#define M_OPLAPI_1P(method, oplist, ...) , M_DELAY3(method)(oplist, m_context, __VA_ARGS__)
#define M_OPLAPI_2P(method, oplist, ...) , M_DELAY3(method)(m_context, & __VA_ARGS__)
#define M_OPLAPI_3P(method, oplist, ...) , M_DELAY3(method)(oplist, m_context, &__VA_ARGS__)
#define M_OPLAPI_4P(method, oplist, x, ...) , x = M_DELAY3(method)(m_context, __VA_ARGS__)
#define M_OPLAPI_5P(method, oplist, x, ...) , x = M_DELAY3(method)(oplist, m_context, __VA_ARGS__)
#define M_OPLAPI_6P(method, oplist, x, ...) , M_DELAY3(method)(m_context, &x, &__VA_ARGS__)
#define M_OPLAPI_7P(method, oplist, x, ...) , M_DELAY3(method)(oplist, m_context, &x, &__VA_ARGS__)
/* API transformation support for indirection */
#define M_OPLAPI_INDIRECT_0 M_OPLAPI_ERROR
#define M_OPLAPI_INDIRECT_API_0(...) M_OPLAPI_0
@@ -3662,6 +3757,22 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_OPLAPI_EXTRACT_API_6(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_7(...) M_OPLAPI_7
#define M_OPLAPI_EXTRACT_API_7(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_0P(...) M_OPLAPI_0P
#define M_OPLAPI_EXTRACT_API_0P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_1P(...) M_OPLAPI_1P
#define M_OPLAPI_EXTRACT_API_1P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_2P(...) M_OPLAPI_2P
#define M_OPLAPI_EXTRACT_API_2P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_3P(...) M_OPLAPI_3P
#define M_OPLAPI_EXTRACT_API_3P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_4P(...) M_OPLAPI_4P
#define M_OPLAPI_EXTRACT_API_4P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_5P(...) M_OPLAPI_5P
#define M_OPLAPI_EXTRACT_API_5P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_6P(...) M_OPLAPI_6P
#define M_OPLAPI_EXTRACT_API_6P(...) __VA_ARGS__
#define M_OPLAPI_INDIRECT_API_7P(...) M_OPLAPI_7P
#define M_OPLAPI_EXTRACT_API_7P(...) __VA_ARGS__
/* Generic API transformation.
@@ -3759,7 +3870,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
"The " #operator " operator has no method associated in the given OPLIST."),\
(return_type) 0)
#define M_NO_DEF_INIT_MOVE(...) M_NO_DEFAULT(INIT_MOVE, void)
#define M_NO_DEF_INIT_WITH(...) M_NO_DEFAULT(INIT_WITH, void)
#define M_NO_DEF_MOVE(...) M_NO_DEFAULT(MOVE, void)
#define M_NO_DEF_SWAP(...) M_NO_DEFAULT(SWAP, void)
@@ -3800,7 +3910,6 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_NO_DEF_IN_SERIAL(...) M_NO_DEFAULT(IN_SERIAL, m_serial_return_code_t)
#define M_NO_DEF_OOR_SET(...) M_NO_DEFAULT(OOR_SET, void)
#define M_NO_DEF_OOR_EQUAL(...) M_NO_DEFAULT(OOR_EQUAL, bool)
#define M_NO_DEF_MEMPOOL(...) M_NO_DEFAULT(MEMPOOL, void)
/* Create a type with an invalid static assertion.
Creating a type allowed the macro into something not too bad
@@ -3922,10 +4031,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
#define M_NOTHING_DEFAULT(...) ((void)(__VA_ARGS__))
#define M_EMPTY_DEFAULT(...) ((void)1)
#define M_TRUE_DEFAULT(...) true
#define M_NEW_DEFAULT(a) M_MEMORY_ALLOC(a)
#define M_DEL_DEFAULT(a) M_MEMORY_DEL(a)
#define M_REALLOC_DEFAULT(t,p,s) M_MEMORY_REALLOC(t,p,s)
#define M_FREE_DEFAULT(a) M_MEMORY_FREE(a)
#define M_FALSE_DEFAULT(...) false
#define M_EQUAL_DEFAULT(a,b) ((a) == (b))
#define M_CMP_DEFAULT(a,b) ((a) < (b) ? -1 : (a) > (b))
#define M_ADD_DEFAULT(a,b,c) ((a) = (b) + (c))
@@ -3947,9 +4053,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
/* NOTE: Theses operators are NOT compatible with the '[1]' tricks
if the variable is defined as a parameter of a function
(sizeof (a) is not portable). */
#define M_MOVE_DEFAULT(a,b) (M_CHECK_SAME(a, b), M_MEMCPY_DEFAULT(a, b), memset(&(b), 0, sizeof (a)))
#define M_MEMCPY_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcpy(&(a), &(b), sizeof (a)))
#define M_MEMSET_DEFAULT(a) (memset(&(a), 0, sizeof (a)))
#define M_RESET_DEFAULT(a) (memset(&(a), 0, sizeof (a)))
#define M_MEMCMP1_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcmp(&(a), &(b), sizeof (a)) == 0)
#define M_MEMCMP2_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcmp(&(a), &(b), sizeof (a)))
#define M_SWAP_DEFAULT(el1, el2) do { \
@@ -3960,28 +4064,35 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
memcpy(&(el2), &_tmp, sizeof (el1)); \
} while (0)
/* Default oplist for plain structure */
#define M_POD_OPLIST \
(INIT(M_RESET_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
EQUAL(M_MEMCMP1_DEFAULT), CMP(M_MEMCMP2_DEFAULT), HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT), \
INIT_MOVE(M_SET_DEFAULT) )
/* NOTE: Theses operators are to be used with the '[1]' tricks
if the variable is defined as a parameter of a function
(sizeof (a) is not portable). */
#define M_MOVE_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), M_MEMCPY_A1_DEFAULT(a, b), M_MEMSET_A1_DEFAULT(b))
#define M_MEMCPY_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcpy(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_MEMSET_A1_DEFAULT(a) (memset(&(a[0]), 0, sizeof (a[0])))
#define M_COPY_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcpy(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_RESET_A1_DEFAULT(a) (memset(&(a[0]), 0, sizeof (a[0])))
#define M_MEMCMP1_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0],b[0]), memcmp(&(a[0]), &(b[0]), sizeof (a[0])) == 0)
#define M_MEMCMP2_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcmp(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_HASH_A1_DEFAULT(a) (m_core_hash((const void*) &(a[0]), sizeof (a[0])) )
#define M_SWAP_A1_DEFAULT(a, b) do { \
char _tmp[sizeof (a[0])]; \
M_CHECK_SAME(a[0], b[0]); \
memcpy(&_tmp, &(a[0]), sizeof (a[0])); \
memcpy(&(a[0]), &(b[0]), sizeof (a[0])); \
memcpy(&(b[0]), &_tmp, sizeof (a[0])); \
} while (0)
/* Default oplist for plain structure */
#define M_POD_OPLIST \
(INIT(M_MEMSET_DEFAULT), INIT_SET(M_MEMCPY_DEFAULT), SET(M_MEMCPY_DEFAULT), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_MEMCMP1_DEFAULT), CMP(M_MEMCMP2_DEFAULT), \
HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT))
/* Default oplist for a structure defined with an array of size 1 */
/* Default oplist for a structure defined with an array of size 1.
MOVE operator is not defined since the object may need to release some data */
#define M_A1_OPLIST \
(INIT(M_MEMSET_A1_DEFAULT), INIT_SET(M_MEMCPY_A1_DEFAULT), SET(M_MEMCPY_A1_DEFAULT), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_MEMCMP1_A1_DEFAULT), CMP(M_MEMCMP2_A1_DEFAULT), \
HASH(M_HASH_A1_DEFAULT))
(INIT(M_RESET_A1_DEFAULT), INIT_SET(M_COPY_A1_DEFAULT), SET(M_COPY_A1_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
EQUAL(M_MEMCMP1_A1_DEFAULT), CMP(M_MEMCMP2_A1_DEFAULT), HASH(M_HASH_A1_DEFAULT), SWAP(M_SWAP_A1_DEFAULT), \
INIT_MOVE(M_COPY_A1_DEFAULT) )
/* Oplist for a type that does nothing and shall not be instanciated */
@@ -4006,7 +4117,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
# define M_BASIC_OPLIST \
(INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_BASIC), CMP(M_CMP_BASIC), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT) , \
RESET(M_INIT_BASIC), PROPERTIES( (NOCLEAR(1)) ), \
ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), \
MUL(M_MUL_DEFAULT), DIV(M_DIV_DEFAULT), \
@@ -4019,7 +4130,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
# define M_BASIC_OPLIST \
(INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_BASIC), CMP(M_CMP_BASIC), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT) , \
RESET(M_INIT_BASIC), PROPERTIES( (NOCLEAR(1)) ), \
ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), \
MUL(M_MUL_DEFAULT), DIV(M_DIV_DEFAULT), \
@@ -4032,7 +4143,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
# define M_BASIC_OPLIST \
(INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_BASIC), CMP(M_CMP_BASIC), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT) , \
RESET(M_INIT_BASIC), PROPERTIES( (NOCLEAR(1)) ), \
ADD(M_ADD_DEFAULT), SUB(M_SUB_DEFAULT), \
MUL(M_MUL_DEFAULT), DIV(M_DIV_DEFAULT), \
@@ -4063,7 +4174,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
(INIT(API_1(M_ENUM_INIT)), INIT_SET(M_SET_DEFAULT), \
SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT) , \
HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT), \
TYPE(type), OPLIST(init), \
IN_STR(API_1(M_ENUM_FSCAN)), OUT_STR(M_ENUM_FPRINT), \
@@ -4076,7 +4187,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[])
(INIT(API_1(M_ENUM_INIT)), INIT_SET(M_SET_DEFAULT), \
SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT) , \
HASH(M_HASH_POD_DEFAULT), SWAP(M_SWAP_DEFAULT), \
TYPE(type), OPLIST(init), \
IN_SERIAL(API_1(M_ENUM_IN_SERIAL)), OUT_SERIAL(M_ENUM_OUT_SERIAL), \
@@ -4131,7 +4242,7 @@ m_core_parse2_enum (const char str[], const char **endptr)
#define M_ENUM_IN_SERIAL(oplist, var, serial) \
( var = (M_GET_TYPE oplist)(true ? m_core_in_serial_enum(serial) : 0), (serial)->tmp.r)
#define M_ENUM_GET_STR(str, var, append) \
((append ? m_string_cat_printf : m_string_printf) (str, "%lld", (long long) (var) ))
((append ? m_string_cat_printf : m_string_printf) M_R(str, "%lld", (long long) (var) ))
#define M_ENUM_PARSE(oplist, var, str, endptr) \
( var = (M_GET_TYPE oplist) (true ? m_core_parse1_enum(str) : 0), m_core_parse2_enum(str, endptr))
@@ -4139,32 +4250,29 @@ m_core_parse2_enum (const char str[], const char **endptr)
/* Default oplist for standard types of pointers.
*/
#define M_PTR_OPLIST \
(INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), \
CLEAR(M_NOTHING_DEFAULT), EQUAL(M_EQUAL_DEFAULT), \
INIT_MOVE(M_MOVE_DEFAULT), MOVE(M_MOVE_DEFAULT) , \
SWAP(M_SWAP_DEFAULT) )
(INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
EQUAL(M_EQUAL_DEFAULT), SWAP(M_SWAP_DEFAULT) \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT) )
/* Default oplist for complex objects with "classic" names for methods.
/* Default oplist for complex objects (using [1] trick) with "classic" names for methods.
*/
#define M_CLASSIC_OPLIST(name) ( \
INIT(M_F(name, _init)), \
INIT_SET(M_F(name, _init_set)), \
SET(M_F(name, _set)), \
CLEAR(M_F(name, _clear)), \
TYPE(M_F(name, _t)) )
INIT(M_F(name, _init)), INIT_SET(M_F(name, _init_set)), SET(M_F(name, _set)), CLEAR(M_F(name, _clear)), \
INIT_MOVE(M_COPY_A1_DEFAULT), TYPE(M_F(name, _t)) )
/* OPLIST for 'const char *' string (with NO memory allocation).
/* Default oplist for 'const char *' string (with NO memory allocation).
*/
#define M_CSTR_EQUAL(a,b) (strcmp((a),(b)) == 0)
#define M_CSTR_OUT_STR(file, str) fprintf(file, "%s", str)
#define M_CSTR_OPLIST (INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), \
SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
HASH(m_core_cstr_hash), EQUAL(M_CSTR_EQUAL), \
CMP(strcmp), TYPE(const char *), \
OUT_STR(M_CSTR_OUT_STR) )
#define M_CSTR_OPLIST \
(INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), CLEAR(M_NOTHING_DEFAULT), \
HASH(m_core_cstr_hash), EQUAL(M_CSTR_EQUAL), CMP(strcmp), SWAP(M_SWAP_DEFAULT), \
INIT_MOVE(M_SET_DEFAULT), MOVE(M_SET_DEFAULT), TYPE(const char *), \
OUT_STR(M_CSTR_OUT_STR) )
/****************************************************************************/
/* From an oplist (...) return ... (Like M_ID but for OPLIST) */
#define M_OPFLAT(...) __VA_ARGS__
@@ -4249,13 +4357,6 @@ m_core_parse2_enum (const char str[], const char **endptr)
so that the first argument becomes a pointer to the destination (OBSOLETE MACRO) */
#define M_IPTR(...) ( & __VA_ARGS__ )
/* Perform an INIT_MOVE if present, or emulate it using INIT_SET/CLEAR */
#define M_DO_INIT_MOVE(oplist, dest, src) do { \
M_IF_METHOD(INIT_MOVE, oplist)(M_CALL_INIT_MOVE(oplist, (dest), (src)), \
M_CALL_INIT_SET(oplist, (dest), (src)) ; \
M_CALL_CLEAR(oplist, (src) )); \
} while (0)
/* Perform a MOVE if present, or emulate it using CLEAR/INIT_MOVE
if possible, or with SET/CLEAR otherwise */
#define M_DO_MOVE(oplist, dest, src) do { \
@@ -4404,7 +4505,7 @@ m_core_parse2_enum (const char str[], const char **endptr)
M_IF_OPLIST(oplist)(M_LETI3, M_LETI2_FAILURE)(cont, oplist, __VA_ARGS__)
// Stop with a failure (invalid oplist)
#define M_LETI2_FAILURE(cont, oplist, ...) \
M_STATIC_ASSERT(false, M_LIB_NOT_AN_OPLIST, "(M_LET): the given argument is not a valid oplist: " M_AS_STR(oplist))
M_STATIC_ASSERT(false, M_LIB_NOT_AN_OPLIST, "the last argument of M_LET is not a valid oplist or a registered type: " M_AS_STR(oplist))
// 4. Map all variables to their own LET
#define M_LETI3(cont, oplist, ...) \
for(bool cont = true; cont ; /* unused */) \
@@ -4508,7 +4609,7 @@ m_core_parse2_enum (const char str[], const char **endptr)
for( init; cont ; cont = false) \
M_DEFER_TRY_INJECT_POST(cont, clear) \
for( (void) 0; cont; cont = false)
/* If exceptions are activated, M_CHAIN_OBJ enables support for chaining
initialization at the begining of a constructor for the fields of the constructed
@@ -4638,12 +4739,12 @@ m_core_parse2_enum (const char str[], const char **endptr)
so it reverses the final order.
*/
#define M_INIT_VAI(oplist, dest, ...) \
(void)(M_GET_INIT oplist (dest) , \
M_MAP2_C(M_INIT_VAI_FUNC, (dest, M_GET_PUSH oplist) , __VA_ARGS__) \
M_IF_METHOD(REVERSE, oplist)(M_DEFERRED_COMMA M_GET_REVERSE oplist (dest), ) \
(void)(M_CALL_INIT(oplist, dest) , \
M_MAP2_C(M_INIT_VAI_FUNC, (dest, oplist) , __VA_ARGS__) \
M_IF_METHOD(REVERSE, oplist)(M_DEFERRED_COMMA M_CALL_REVERSE(oplist, dest), ) \
)
#define M_INIT_VAI_FUNC(d, a) \
M_PAIR_2 d (M_PAIR_1 d, a)
M_CALL_PUSH(M_PAIR_2 d, M_PAIR_1 d, a)
/* Initialize the container 'dest' as per 'oplist' INIT operator
@@ -4809,6 +4910,16 @@ m_core_parse2_enum (const char str[], const char **endptr)
#define M_EMPLACE_LIST_TYPE_VAR_SINGLE(prefix, emplace_type) \
, emplace_type const prefix
/* Expand to the list of emplace type with their variable name
to use in the function declaration (doesn't start with a , but slower) */
#define M_EMPLACE_LIST_TYPE_VAR_ALTER(prefix, emplace_type) \
M_IF(M_PARENTHESIS_P( emplace_type ))(M_EMPLACE_LIST_TYPE_VAR_ALTER_MULTI, M_EMPLACE_LIST_TYPE_VAR_ALTER_SINGLE)(prefix, emplace_type)
#define M_EMPLACE_LIST_TYPE_VAR_ALTER_MULTI(prefix, emplace_type) \
M_MAP3(M_EMPLACE_LIST_TYPE_VAR_ALTER_MULTI_F, prefix, M_ID emplace_type)
#define M_EMPLACE_LIST_TYPE_VAR_ALTER_MULTI_F(prefix, num, type) \
M_IF(M_EQUAL(1, num))( /*void*/, M_DEFERRED_COMMA) type const M_C(prefix, num)
#define M_EMPLACE_LIST_TYPE_VAR_ALTER_SINGLE(prefix, emplace_type) \
emplace_type const prefix
/* Expand to the list of variable name based on the the declaration with
emplace type to use in the INIT_WITH or specific method call (start with a ,) */
@@ -4982,11 +5093,11 @@ m_core_parse2_enum (const char str[], const char **endptr)
#define M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(isSet, name, name_t, key_oplist, val_oplist) \
M_ID( M_C(M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF, isSet)(name, name_t, key_oplist, val_oplist) )
#define M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF0(name, name_t, key_oplist, val_oplist) \
M_EMPLACE_ASS_ARRAY_DEF(name, name_t, M_F(name, _emplace), key_oplist, val_oplist, M_EMPLACE_ASS_ARRA1_BOTH_GENE, M_EMPLACE_ASS_ARRA1_KEY_GENE, M_EMPLACE_ASS_ARRA1_VAL_GENE) \
M_EMPLACE_QUEUE_DEF(name, name_t, M_F(name, _get_emplace), key_oplist, M_EMPLACE_GET_GENE)
M_EMPLACE_ASS_ARRAY_DEF(name, name_t, _emplace, key_oplist, val_oplist, M_EMPLACE_ASS_ARRA1_BOTH_GENE, M_EMPLACE_ASS_ARRA1_KEY_GENE, M_EMPLACE_ASS_ARRA1_VAL_GENE) \
M_EMPLACE_QUEUE_DEF(name, name_t, _get_emplace, key_oplist, M_EMPLACE_GET_GENE)
#define M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF1(name, name_t, key_oplist, val_oplist) \
M_EMPLACE_QUEUE_DEF(name, name_t, M_F(name, _emplace), key_oplist, M_EMPLACE_QUEUE_GENE) \
M_EMPLACE_QUEUE_DEF(name, name_t, M_F(name, _get_emplace), key_oplist, M_EMPLACE_GET_GENE)
M_EMPLACE_QUEUE_DEF(name, name_t, _emplace, key_oplist, M_EMPLACE_QUEUE_GENE) \
M_EMPLACE_QUEUE_DEF(name, name_t, _get_emplace, key_oplist, M_EMPLACE_GET_GENE)
/* Definition of the emplace_back function for associative arrays.
@@ -4997,39 +5108,36 @@ m_core_parse2_enum (const char str[], const char **endptr)
one for value only.
*/
#define M_EMPLACE_ASS_ARRA1_BOTH_GENE(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_P(void, name, function_name, name_t v \
M_EMPLACE_LIST_TYPE_VAR(akey, key_emplace_type) \
M_EMPLACE_LIST_TYPE_VAR(aval, val_emplace_type) \
){ \
M_F(name, _key_ct) key; \
M_EMPLACE_CALL_FUNC(akey, key_init_func, key_oplist, key, key_emplace_type); \
M_F(name, _value_ct) *val; \
val = M_F(name, _safe_get)(v, key); \
val = M_F(name, _safe_get)M_R(v, key); \
M_CALL_CLEAR(val_oplist, *val); \
M_EMPLACE_CALL_FUNC(aval, val_init_func, val_oplist, *val, val_emplace_type); \
M_CALL_CLEAR(key_oplist, key); \
} \
#define M_EMPLACE_ASS_ARRA1_KEY_GENE(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_P(void, name, function_name, name_t v \
M_EMPLACE_LIST_TYPE_VAR(akey, key_emplace_type) \
, M_F(name, _value_ct) const val \
){ \
M_F(name, _key_ct) key; \
M_EMPLACE_CALL_FUNC(akey, key_init_func, key_oplist, key, key_emplace_type); \
M_F(name, _set_at)(v, key, val); \
M_F(name, _set_at)M_R(v, key, val); \
M_CALL_CLEAR(key_oplist, key); \
} \
#define M_EMPLACE_ASS_ARRA1_VAL_GENE(name, name_t, function_name, key_oplist, val_oplist, key_init_func, val_init_func, key_emplace_type, val_emplace_type) \
M_INLINE void \
function_name(name_t v, M_F(name, _key_ct) const key \
M_P(void, name, function_name, name_t v, M_F(name, _key_ct) const key \
M_EMPLACE_LIST_TYPE_VAR(aval, val_emplace_type) \
){ \
M_F(name, _value_ct) *val; \
val = M_F(name, _safe_get)(v, key); \
val = M_F(name, _safe_get)M_R(v, key); \
M_CALL_CLEAR(val_oplist, *val); \
M_EMPLACE_CALL_FUNC(aval, val_init_func, val_oplist, *val, val_emplace_type); \
} \
@@ -5040,13 +5148,12 @@ m_core_parse2_enum (const char str[], const char **endptr)
This definition is far from being efficient but works for the current interface.
*/
#define M_EMPLACE_QUEUE_GENE(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_P(void, name, function_name, name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_GET_TYPE oplist data; \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, data, exp_emplace_type); \
M_F(name, _push)(v, data); \
M_F(name, _push) M_R(v, data); \
M_CALL_CLEAR(oplist, data); \
}
@@ -5056,9 +5163,7 @@ m_core_parse2_enum (const char str[], const char **endptr)
This definition is far from being efficient but works for the current interface.
*/
#define M_EMPLACE_GET_GENE(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE M_F(name, _value_ct) * \
function_name(name_t const v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(M_F(name, _value_ct) *, name, function_name, name_t const v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_GET_TYPE oplist data; \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, data, exp_emplace_type); \
@@ -5083,6 +5188,7 @@ m_core_parse2_enum (const char str[], const char **endptr)
* each time it is called with a pseudo random wait.
* It is used to avoid too many threads trying to grab some atomic
* variables at the same times (it makes the threads waits randomly).
* Initialization is cheap.
*/
typedef struct m_core_backoff_s {
unsigned int count; // Number of times it has been run
@@ -5156,7 +5262,7 @@ struct m_string_s;
* - OK & done (object is fully parsed),
* - OK & continue parsing (object is partially parsed)
* - Fail parsing
* - Fail parsing with given arguments (give explicit size)
* - Fail parsing with given arguments (give explicit size) but a retry will succeed.
*/
typedef enum m_serial_return_code_e {
M_SERIAL_OK_DONE = 0, M_SERIAL_OK_CONTINUE = 1, M_SERIAL_FAIL = 2, M_SERIAL_FAIL_RETRY = 4
@@ -5236,7 +5342,7 @@ typedef struct m_serial_read_interface_s {
m_serial_return_code_t (*read_boolean)(m_serial_read_t,bool *);
m_serial_return_code_t (*read_integer)(m_serial_read_t, long long *, const size_t size_of_type);
m_serial_return_code_t (*read_float)(m_serial_read_t, long double *, const size_t size_of_type);
m_serial_return_code_t (*read_string)(m_serial_read_t, struct m_string_s *);
m_serial_return_code_t (*read_string)(M_P_EXPAND m_serial_read_t, struct m_string_s *);
m_serial_return_code_t (*read_array_start)(m_serial_local_t, m_serial_read_t, size_t *);
m_serial_return_code_t (*read_array_next)(m_serial_local_t, m_serial_read_t);
m_serial_return_code_t (*read_map_start)(m_serial_local_t, m_serial_read_t, size_t *);
@@ -5266,22 +5372,22 @@ typedef struct m_serial_write_s {
* All function pointers shall be not null.
*/
typedef struct m_serial_write_interface_s {
m_serial_return_code_t (*write_boolean)(m_serial_write_t,const bool data);
m_serial_return_code_t (*write_integer)(m_serial_write_t,const long long data, const size_t size_of_type);
m_serial_return_code_t (*write_float)(m_serial_write_t, const long double data, const size_t size_of_type);
m_serial_return_code_t (*write_string)(m_serial_write_t,const char data[], size_t len);
m_serial_return_code_t (*write_array_start)(m_serial_local_t, m_serial_write_t, const size_t number_of_elements);
m_serial_return_code_t (*write_array_next)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_array_end)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_map_start)(m_serial_local_t, m_serial_write_t, const size_t number_of_elements);
m_serial_return_code_t (*write_map_value)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_map_next)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_map_end)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_tuple_start)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_tuple_id)(m_serial_local_t, m_serial_write_t, const char * const field_name[], const int max, const int index);
m_serial_return_code_t (*write_tuple_end)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_variant_start)(m_serial_local_t, m_serial_write_t, const char * const field_name[], const int max, const int index);
m_serial_return_code_t (*write_variant_end)(m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_boolean)(M_P_EXPAND m_serial_write_t,const bool data);
m_serial_return_code_t (*write_integer)(M_P_EXPAND m_serial_write_t,const long long data, const size_t size_of_type);
m_serial_return_code_t (*write_float)(M_P_EXPAND m_serial_write_t, const long double data, const size_t size_of_type);
m_serial_return_code_t (*write_string)(M_P_EXPAND m_serial_write_t,const char data[], size_t len);
m_serial_return_code_t (*write_array_start)(M_P_EXPAND m_serial_local_t, m_serial_write_t, const size_t number_of_elements);
m_serial_return_code_t (*write_array_next)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_array_end)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_map_start)(M_P_EXPAND m_serial_local_t, m_serial_write_t, const size_t number_of_elements);
m_serial_return_code_t (*write_map_value)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_map_next)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_map_end)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_tuple_start)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_tuple_id)(M_P_EXPAND m_serial_local_t, m_serial_write_t, const char * const field_name[], const int max, const int index);
m_serial_return_code_t (*write_tuple_end)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
m_serial_return_code_t (*write_variant_start)(M_P_EXPAND m_serial_local_t, m_serial_write_t, const char * const field_name[], const int max, const int index);
m_serial_return_code_t (*write_variant_end)(M_P_EXPAND m_serial_local_t, m_serial_write_t);
} m_serial_write_interface_t;
@@ -5291,23 +5397,23 @@ typedef struct m_serial_write_interface_s {
*/
#define M_OUT_SERIAL_DEFAULT_ARG(serial, x) \
_Generic(((void)0,(x)), \
bool: (serial)->m_interface->write_boolean(serial, M_AS_TYPE(bool, (x))), \
char: (serial)->m_interface->write_integer(serial, M_AS_TYPE(char,(x)), sizeof (x)), \
signed char: (serial)->m_interface->write_integer(serial, M_AS_TYPE(signed char,(x)), sizeof (x)), \
unsigned char: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned char,(x)), sizeof (x)), \
signed short: (serial)->m_interface->write_integer(serial, M_AS_TYPE(signed short,(x)), sizeof (x)), \
unsigned short: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned short,(x)), sizeof (x)), \
signed int: (serial)->m_interface->write_integer(serial, M_AS_TYPE(signed int,(x)), sizeof (x)), \
unsigned int: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned int,(x)), sizeof (x)), \
long int: (serial)->m_interface->write_integer(serial, M_AS_TYPE(long,(x)), sizeof (x)), \
unsigned long int: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned long,(x)), sizeof (x)), \
long long int: (serial)->m_interface->write_integer(serial, M_AS_TYPE(long long,(x)), sizeof (x)), \
unsigned long long int: (serial)->m_interface->write_integer(serial, (long long) M_AS_TYPE(unsigned long long,(x)), sizeof (x)), \
float: (serial)->m_interface->write_float(serial, M_AS_TYPE(float,(x)), sizeof (x)), \
double: (serial)->m_interface->write_float(serial, M_AS_TYPE(double,(x)), sizeof (x)), \
long double: (serial)->m_interface->write_float(serial, M_AS_TYPE(long double,(x)), sizeof (x)), \
const char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(const char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \
char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \
bool: (serial)->m_interface->write_boolean M_R(serial, M_AS_TYPE(bool, (x))), \
char: (serial)->m_interface->write_integer M_R(serial, M_AS_TYPE(char,(x)), sizeof (x)), \
signed char: (serial)->m_interface->write_integer M_R(serial, M_AS_TYPE(signed char,(x)), sizeof (x)), \
unsigned char: (serial)->m_interface->write_integer M_R(serial, (long long) M_AS_TYPE(unsigned char,(x)), sizeof (x)), \
signed short: (serial)->m_interface->write_integer M_R(serial, M_AS_TYPE(signed short,(x)), sizeof (x)), \
unsigned short: (serial)->m_interface->write_integer M_R(serial, (long long) M_AS_TYPE(unsigned short,(x)), sizeof (x)), \
signed int: (serial)->m_interface->write_integer M_R(serial, M_AS_TYPE(signed int,(x)), sizeof (x)), \
unsigned int: (serial)->m_interface->write_integer M_R(serial, (long long) M_AS_TYPE(unsigned int,(x)), sizeof (x)), \
long int: (serial)->m_interface->write_integer M_R(serial, M_AS_TYPE(long,(x)), sizeof (x)), \
unsigned long int: (serial)->m_interface->write_integer M_R(serial, (long long) M_AS_TYPE(unsigned long,(x)), sizeof (x)), \
long long int: (serial)->m_interface->write_integer M_R(serial, M_AS_TYPE(long long,(x)), sizeof (x)), \
unsigned long long int: (serial)->m_interface->write_integer M_R(serial, (long long) M_AS_TYPE(unsigned long long,(x)), sizeof (x)), \
float: (serial)->m_interface->write_float M_R(serial, M_AS_TYPE(float,(x)), sizeof (x)), \
double: (serial)->m_interface->write_float M_R(serial, M_AS_TYPE(double,(x)), sizeof (x)), \
long double: (serial)->m_interface->write_float M_R(serial, M_AS_TYPE(long double,(x)), sizeof (x)), \
const char *: (serial)->m_interface->write_string M_R(serial, M_AS_TYPE(const char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \
char *: (serial)->m_interface->write_string M_R(serial, M_AS_TYPE(char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \
const void *: M_SERIAL_FAIL /* unsupported */, \
void *: M_SERIAL_FAIL /* unsupported */)
+144 -121
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - DEQUE module
*
* Copyright (c) 2017-2024, 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:
@@ -99,8 +99,15 @@
M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \
M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_back), oplist, M_D3QUE_EMPLACE_BACK_DEF) \
M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_front), oplist, M_D3QUE_EMPLACE_FRONT_DEF)
M_EMPLACE_QUEUE_DEF(name, deque_t, _emplace_back, oplist, M_D3QUE_EMPLACE_BACK_DEF) \
M_EMPLACE_QUEUE_DEF(name, deque_t, _emplace_front, oplist, M_D3QUE_EMPLACE_FRONT_DEF)
// OPLIST for the node deletion
#ifdef M_USE_CONTEXT
#define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(M_F(name, _node_list_i_del)))
#else
#define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(API(M_F(name, _node_list_i_del), NONE, ARG2)))
#endif
/* Define the types.
It is a linked list of buckets of the types,
@@ -115,8 +122,8 @@
#define M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \
\
typedef struct M_F(name, _node_s) { \
ILIST_INTERFACE(M_F(name, _node_list), struct M_F(name, _node_s)); \
size_t size; \
M_ILIST_INTERFACE(M_F(name, _node_list), struct M_F(name, _node_s)); \
size_t size, capacity; \
type data[M_MIN_FLEX_ARRAY_SIZE]; \
} node_t; \
\
@@ -124,10 +131,13 @@
M_GET_REALLOC for the allocation). But we want to delete the nodes \
automatically with the intrusive list used for storing the nodes: \
so we register as a DEL operator the FREE operator of the oplist. \
The interfaces are compatible. \
*/ \
/* FIXME: How can I separate public types and private implementation? */ \
ILIST_DEF(M_F(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \
M_P(void, name, _node_list_i_del, node_t *ptr) \
{ \
M_CALL_FREE(oplist, char, ptr, sizeof(node_t) + ptr->capacity * sizeof(type)); \
} \
M_ILIST_DEF(M_F(name, _node_list), node_t, M_D3QU3_NODE_DEL_OPLIST(name) ) \
\
/* Define an internal iterator */ \
typedef struct M_F(name, _it2_s) { \
@@ -170,27 +180,22 @@
#define M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \
\
/* Allocate a new node for a deque */ \
M_INLINE node_t* \
M_C3(m_d3qu3_,name,_new_node)(deque_t d) \
M_P(node_t*, name, _i_new_node, deque_t d) \
{ \
size_t def = d->default_size; \
/* Test for overflow of the size computation */ \
if (M_UNLIKELY_NOMEM (def > SIZE_MAX / sizeof (type) - sizeof(node_t))) { \
M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \
abort(); \
return NULL; \
M_MEMORY_FULL(char, sizeof(node_t)+def * sizeof(type)); \
} \
/* Alloc a new node with dynamic size */ \
node_t*n = (node_t*) (void*) \
M_CALL_REALLOC(oplist, char, NULL, \
sizeof(node_t) + def * sizeof(type) ); \
M_CALL_REALLOC(oplist, char, NULL, 0, sizeof(node_t) + def * sizeof(type) ); \
if (M_UNLIKELY_NOMEM (n==NULL)) { \
M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \
abort(); \
return NULL; \
M_MEMORY_FULL(char, sizeof(node_t)+def * sizeof(type)); \
} \
/* Initialize the node */ \
n->size = def; \
n->capacity = def; \
M_F(name, _node_list_init_field)(n); \
/* Increase the next bucket allocation */ \
/* Do not increase it too much if there are few items */ \
@@ -200,13 +205,12 @@
return n; \
} \
\
M_INLINE void \
M_F(name, _init)(deque_t d) \
M_P(void, name, _init, deque_t d) \
{ \
M_F(name, _node_list_init)(d->list); \
d->default_size = M_USE_DEQUE_DEFAULT_SIZE; \
d->count = 0; \
node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \
node_t *n = M_F(name,_i_new_node)M_R(d); \
M_F(name, _node_list_push_back)(d->list, n); \
d->front->node = n; \
d->front->index = M_USE_DEQUE_DEFAULT_SIZE/2; \
@@ -215,10 +219,10 @@
M_D3QU3_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _reset)(deque_t d) \
M_P(void, name, _reset, deque_t d) \
{ \
M_D3QU3_CONTRACT(d); \
M_UNUSED_CONTEXT(); \
node_t *min_node = NULL; \
for(node_t *n = d->front->node; \
n != NULL ; \
@@ -240,31 +244,29 @@
M_D3QU3_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _clear)(deque_t d) \
M_P(void, name, _clear, deque_t d) \
{ \
M_D3QU3_CONTRACT(d); \
M_F(name, _reset)(d); \
/* We have registered the delete operator to clear all objects */ \
M_F(name, _node_list_clear)(d->list); \
/* It is safer to clean some variables */ \
M_F(name, _reset)M_R(d); \
/* We have registered the delete operator to clear all node objects */ \
M_F(name, _node_list_clear)M_R(d->list); \
/* It is safer to clean some variables to mark d as invalid */ \
d->front->node = NULL; \
d->back->node = NULL; \
d->count = 0; \
} \
\
M_INLINE type * \
M_F(name, _push_back_raw)(deque_t d) \
M_P(type *, name, _push_back_raw, deque_t d) \
{ \
M_D3QU3_CONTRACT(d); \
node_t *n = d->back->node; \
size_t index = d->back->index; \
if (M_UNLIKELY (n->size <= index)) { \
/* try to get an already allocated node */ \
/* Cannot fit in the current node, try to get another allocated node */ \
n = M_F(name, _node_list_next_obj)(d->list, n); \
if (n == NULL) { \
/* No node exists, allocate a new one */ \
n = M_C3(m_d3qu3_,name,_new_node)(d); \
/* No another node exists: allocate a new one */ \
n = M_F(name,_i_new_node)M_R(d); \
M_F(name, _node_list_push_back)(d->list, n); \
} \
d->back->node = n; \
@@ -278,27 +280,24 @@
return ret; \
} \
\
/* Internal, for INIT_WITH */ \
M_INLINE type * \
M_F(name, _push_raw)(deque_t d) \
/* Internal, for generic INIT_WITH */ \
M_P(type *, name, _push_raw, deque_t d) \
{ \
return M_F(name, _push_back_raw)(d); \
return M_F(name, _push_back_raw)M_R(d); \
} \
\
M_INLINE void \
M_F(name, _push_back)(deque_t d, type const x) \
M_P(void, name, _push_back, deque_t d, type const x) \
{ \
type *p = M_F(name, _push_back_raw)(d); \
type *p = M_F(name, _push_back_raw)M_R(d); \
M_ON_EXCEPTION(d->back->index --, d->count--) { \
M_CALL_INIT_SET(oplist, *p, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _push_back_new)(deque_t d) \
M_P(type *, name, _push_back_new, deque_t d) \
{ \
type *p = M_F(name, _push_back_raw)(d); \
type *p = M_F(name, _push_back_raw)M_R(d); \
M_ON_EXCEPTION(d->back->index --, d->count--) { \
M_CALL_INIT(oplist, *p); \
} \
@@ -306,16 +305,14 @@
} \
, ) \
\
M_INLINE void \
M_F(name, _push_back_move)(deque_t d, type *x) \
M_P(void, name, _push_back_move, deque_t d, type *x) \
{ \
M_ASSERT (x != NULL); \
type *p = M_F(name, _push_back_raw)(d); \
M_DO_INIT_MOVE (oplist, *p, *x); \
type *p = M_F(name, _push_back_raw)M_R(d); \
M_CALL_INIT_MOVE (oplist, *p, *x); \
} \
\
M_INLINE type* \
M_F(name, _push_front_raw)(deque_t d) \
M_P(type *, name, _push_front_raw, deque_t d) \
{ \
M_D3QU3_CONTRACT(d); \
node_t *n = d->front->node; \
@@ -325,7 +322,7 @@
if (M_UNLIKELY (n->size <= index)) { \
n = M_F(name, _node_list_previous_obj)(d->list, n); \
if (n == NULL) { \
n = M_C3(m_d3qu3_,name,_new_node)(d); \
n = M_F(name,_i_new_node)M_R(d); \
M_F(name, _node_list_push_front)(d->list, n); \
} \
d->front->node = n; \
@@ -338,20 +335,18 @@
return ret; \
} \
\
M_INLINE void \
M_F(name, _push_front)(deque_t d, type const x) \
M_P(void, name, _push_front, deque_t d, type const x) \
{ \
type *p = M_F(name, _push_front_raw)(d); \
type *p = M_F(name, _push_front_raw)M_R(d); \
M_ON_EXCEPTION(d->front->index ++, d->count--) { \
M_CALL_INIT_SET(oplist, *p, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _push_front_new)(deque_t d) \
M_P(type *, name, _push_front_new, deque_t d) \
{ \
type *p = M_F(name, _push_front_raw)(d); \
type *p = M_F(name, _push_front_raw)M_R(d); \
M_ON_EXCEPTION(d->front->index ++, d->count--) { \
M_CALL_INIT(oplist, *p); \
} \
@@ -359,19 +354,18 @@
} \
,) \
\
M_INLINE void \
M_F(name, _push_front_move)(deque_t d, type *x) \
M_P(void, name, _push_front_move, deque_t d, type *x) \
{ \
M_ASSERT (x != NULL); \
type *p = M_F(name, _push_front_raw)(d); \
M_DO_INIT_MOVE (oplist, *p, *x); \
type *p = M_F(name, _push_front_raw)M_R(d); \
M_CALL_INIT_MOVE (oplist, *p, *x); \
} \
\
M_INLINE void \
M_F(name, _pop_back)(type *ptr, deque_t d) \
M_P(void, name, _pop_back, type *ptr, deque_t d) \
{ \
M_D3QU3_CONTRACT(d); \
M_ASSERT(d->count > 0); \
M_UNUSED_CONTEXT(); \
node_t *n = d->back->node; \
size_t index = d->back->index; \
index --; \
@@ -391,34 +385,35 @@
index = n->size-1; \
} \
if (ptr != NULL) \
M_DO_MOVE(oplist, *ptr, n->data[index]); else \
M_CALL_CLEAR(oplist, n->data[index]); \
M_DO_MOVE(oplist, *ptr, n->data[index]); \
else \
M_CALL_CLEAR(oplist, n->data[index]); \
d->count --; \
d->back->index = index; \
M_D3QU3_CONTRACT(d); \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE void \
M_F(name, _pop_back_move)(type *ptr, deque_t d) \
M_P(void, name, _pop_back_move, type *ptr, deque_t d) \
{ \
M_ASSERT(ptr != NULL); \
/* Note: Lazy implementation. Can be improved if needed */ \
M_CALL_INIT(oplist, *ptr); \
M_F(name, _pop_back)(ptr, d); \
M_F(name, _pop_back)M_R(ptr, d); \
} \
, ) \
\
M_INLINE void \
M_F(name, _pop_front)(type *ptr, deque_t d) \
M_P(void, name, _pop_front, type *ptr, deque_t d) \
{ \
M_D3QU3_CONTRACT(d); \
M_ASSERT(d->count > 0); \
M_UNUSED_CONTEXT(); \
node_t *n = d->front->node; \
size_t index = d->front->index; \
if (ptr != NULL) \
M_DO_MOVE(oplist, *ptr, n->data[index]); else \
M_CALL_CLEAR(oplist, n->data[index]); \
M_DO_MOVE(oplist, *ptr, n->data[index]); \
else \
M_CALL_CLEAR(oplist, n->data[index]); \
index++; \
if (M_UNLIKELY (n->size <= index)) { \
/* If there is a previous node, \
@@ -451,13 +446,12 @@
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE void \
M_F(name, _pop_front_move)(type *ptr, deque_t d) \
M_P(void, name, _pop_front_move, type *ptr, deque_t d) \
{ \
M_ASSERT(ptr != NULL); \
/* Note: Lazy implementation */ \
M_CALL_INIT(oplist, *ptr); \
M_F(name, _pop_front)(ptr, d); \
M_F(name, _pop_front)M_R(ptr, d); \
} \
,) \
\
@@ -663,8 +657,7 @@
return M_CONST_CAST(type, &it->node->data[it->index]); \
} \
\
M_INLINE void \
M_F(name, _remove)(deque_t d, it_t it) \
M_P(void, name, _remove, deque_t d, it_t it) \
{ \
M_D3QU3_CONTRACT(d); \
M_ASSERT (it != NULL && it->node != NULL); \
@@ -711,7 +704,7 @@
/* Node deletion */ \
M_ASSERT(d->count > 1); \
M_F(name, _node_list_unlink)(n); \
M_CALL_FREE(oplist, n); \
M_CALL_FREE(oplist, char, n, sizeof(node_t) + n->capacity * sizeof(type) ); \
} else { \
memmove(&n->data[it->index], &n->data[it->index+1], \
sizeof(type) * (it->node->size - it->index - 1)); \
@@ -726,14 +719,13 @@
M_D3QU3_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _init_set)(deque_t d, const deque_t src) \
M_P(void, name, _init_set, deque_t d, const deque_t src) \
{ \
M_D3QU3_CONTRACT(src); \
M_ASSERT (d != NULL); \
M_F(name, _node_list_init)(d->list); \
d->default_size = M_USE_DEQUE_DEFAULT_SIZE + src->count; \
node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \
node_t *n = M_F(name,_i_new_node)M_R(d); \
d->count = src->count; \
d->default_size /= 2; \
M_F(name, _node_list_push_back)(d->list, n); \
@@ -753,15 +745,14 @@
M_D3QU3_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _set)(deque_t d, deque_t const src) \
M_P(void, name, _set, deque_t d, deque_t const src) \
{ \
if (M_UNLIKELY (src == d)) \
return; \
/* TODO: Reuse memory of d! */ \
M_ON_EXCEPTION( M_F(name, _init)(d) ) { \
M_F(name, _clear)(d); \
M_F(name, _init_set)(d, src); \
M_F(name, _clear)M_R(d); \
M_F(name, _init_set)M_R(d, src); \
} \
} \
\
@@ -781,12 +772,11 @@
M_D3QU3_CONTRACT(d); \
} \
\
M_INLINE void \
M_F(name, _move)(deque_t d, deque_t src) \
M_P(void, name, _move, deque_t d, deque_t src) \
{ \
M_D3QU3_CONTRACT(d); \
M_D3QU3_CONTRACT(src); \
M_F(name, _clear)(d); \
M_F(name, _clear)M_R(d); \
M_F(name, _init_move)(d, src); \
M_D3QU3_CONTRACT(d); \
} \
@@ -832,10 +822,10 @@
return M_CONST_CAST(type, M_F(name, _get)(d, key)); \
} \
\
M_INLINE void \
M_F(name, _set_at)(deque_t d, size_t key, type const x) \
M_P(void, name, _set_at, deque_t d, size_t key, type const x) \
{ \
M_D3QU3_CONTRACT(d); \
M_UNUSED_CONTEXT(); \
M_ASSERT_INDEX (key, d->count); \
type *p = M_F(name, _get)(d, key); \
M_CALL_SET(oplist, *p, x); \
@@ -895,11 +885,10 @@
, /* NO SWAP */) \
\
M_IF_METHOD(GET_STR, oplist)( \
M_INLINE void \
M_F(name, _get_str)(m_string_t str, deque_t const deque, bool append) \
M_P(void, name, _get_str, m_string_t str, deque_t const deque, bool append) \
{ \
M_D3QU3_CONTRACT(deque); \
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
it_t it; \
for (M_F(name, _it)(it, deque) ; \
!M_F(name, _end_p)(it); \
@@ -907,9 +896,9 @@
type const *item = M_F(name, _cref)(it); \
M_CALL_GET_STR(oplist, str, *item, true); \
if (!M_F(name, _last_p)(it)) \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
} \
m_string_push_back (str, ']'); \
m_string_push_back M_R(str, ']'); \
} \
, /* no GET_STR */ ) \
\
@@ -934,12 +923,11 @@
, /* no OUT_STR */ ) \
\
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
M_INLINE bool \
M_F(name, _parse_str)(deque_t deque, const char str[], const char **endp) \
M_P(bool, name, _parse_str, deque_t deque, const char str[], const char **endp) \
{ \
M_D3QU3_CONTRACT(deque); \
M_ASSERT (str != NULL); \
M_F(name,_reset)(deque); \
M_F(name,_reset)M_R(deque); \
int c = *str++; \
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
c = *str++; \
@@ -950,7 +938,7 @@
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
c = m_core_str_nospace(&str); \
if (b == false || c == 0) { c = 0; break; } \
M_F(name, _push_back)(deque, item); \
M_F(name, _push_back)M_R(deque, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
exit: \
@@ -961,12 +949,11 @@
, /* no PARSE_STR */ ) \
\
M_IF_METHOD2(IN_STR, INIT, oplist)( \
M_INLINE bool \
M_F(name, _in_str)(deque_t deque, FILE *file) \
M_P(bool, name, _in_str, deque_t deque, FILE *file) \
{ \
M_D3QU3_CONTRACT(deque); \
M_ASSERT (file != NULL); \
M_F(name,_reset)(deque); \
M_F(name,_reset)M_R(deque); \
int c = fgetc(file); \
if (M_UNLIKELY (c != '[')) return false; \
c = fgetc(file); \
@@ -978,7 +965,7 @@
bool b = M_CALL_IN_STR(oplist, item, file); \
c = m_core_fgetc_nospace(file); \
if (b == false || c == EOF) { c = 0; break; } \
M_F(name, _push_back)(deque, item); \
M_F(name, _push_back)M_R(deque, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
M_D3QU3_CONTRACT(deque); \
@@ -987,47 +974,45 @@
, /* no IN_STR */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, const deque_t deque) \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const deque_t deque) \
{ \
M_D3QU3_CONTRACT(deque); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
bool first_done = false; \
ret = f->m_interface->write_array_start(local, f, deque->count); \
ret = f->m_interface->write_array_start M_R(local, f, deque->count); \
M_F(name, _it_ct) it; \
for (M_F(name, _it)(it, deque) ; \
!M_F(name, _end_p)(it); \
M_F(name, _next)(it)){ \
type const *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(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_METHOD2(IN_SERIAL, INIT, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(deque_t deque, m_serial_read_t f) \
M_P(m_serial_return_code_t, name, _in_serial, deque_t deque, m_serial_read_t f) \
{ \
M_D3QU3_CONTRACT(deque); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
M_F(name,_reset)(deque); \
M_F(name,_reset)M_R(deque); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_QLET(1, item, type, oplist) { \
do { \
ret = M_CALL_IN_SERIAL(oplist, item, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_F(name, _push_back)(deque, item); \
M_F(name, _push_back)M_R(deque, item); \
ret = f->m_interface->read_array_next(local, f); \
} while (ret == M_SERIAL_OK_CONTINUE); \
} \
@@ -1037,11 +1022,9 @@
/* Definition of the emplace_back function for deque */
#define M_D3QUE_EMPLACE_BACK_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)(v); \
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)M_R(v); \
M_ON_EXCEPTION(v->back->index --, v->count--) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
@@ -1050,11 +1033,9 @@
/* Definition of the emplace_front function for deque */
#define M_D3QUE_EMPLACE_FRONT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)(v); \
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)M_R(v); \
M_ON_EXCEPTION(v->front->index ++, v->count--) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
@@ -1076,6 +1057,7 @@
((M_LIB_ERROR(ARGUMENT_OF_DEQUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* OPLIST definition of a deque */
#ifndef M_USE_CONTEXT
#define M_D3QU3_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)) \
,INIT_SET(M_F(name, _init_set)) \
@@ -1105,7 +1087,7 @@
,RESET(M_F(name,_reset)) \
,GET_SIZE(M_F(name, _size)) \
,PUSH(M_F(name,_push_back)) \
,POP(M_F(name,_pop_back)) \
,POP(M_F(name,_pop_front)) \
,OPLIST(oplist) \
,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)),) \
@@ -1116,7 +1098,48 @@
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
)
#else
#define M_D3QU3_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_WITH_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_LAST(M_F(name,_it_last)) \
,IT_END(M_F(name,_it_end)) \
,IT_SET(M_F(name,_it_set)) \
,IT_END_P(M_F(name,_end_p)) \
,IT_LAST_P(M_F(name,_last_p)) \
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
,IT_NEXT(M_F(name,_next)) \
,IT_PREVIOUS(M_F(name,_previous)) \
,IT_REF(M_F(name,_ref)) \
,IT_CREF(M_F(name,_cref)) \
,IT_REMOVE(API_0P(M_F(name,_remove))) \
,RESET(API_0P(M_F(name,_reset))) \
,GET_SIZE(M_F(name, _size)) \
,PUSH(API_0P(M_F(name,_push_back))) \
,POP(API_0P(M_F(name,_pop_front))) \
,OPLIST(oplist) \
,M_IF_METHOD(GET_STR, oplist)(API_0P(GET_STR(M_F(name, _get_str))),) \
,M_IF_METHOD(PARSE_STR, oplist)(API_0P(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(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
/********************************* INTERNAL **********************************/
+241 -201
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Function Object module
*
* Copyright (c) 2017-2024, 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:
+1 -12
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Generic module
*
* Copyright (c) 2017-2024, 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:
@@ -101,17 +101,6 @@
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/* Definition of the typeof keyword
In C23, we can use the standard keyword.
Otherwise we use some extensions of the compilers. */
#ifdef _MSC_VER
#define m_typeof(x) decltype(x)
#elif defined(__GNUC__)
#define m_typeof(x) __typeof__(x)
#else
#define m_typeof(x) typeof(x)
#endif
// Instead of using expensive M_SEQ(1,50), precompute it:
#define M_G3N_SEQ_INT \
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, \
+6 -9
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Integer Generator (GENINT) module
*
* Copyright (c) 2017-2024, 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:
@@ -103,16 +103,14 @@ typedef struct m_genint_s {
* meaning it can be used to index global unique resources shared
* for all threads.
*/
M_INLINE void
m_genint_init(m_genint_t s, unsigned int n)
M_P(void, m_genint, _init, m_genint_t s, unsigned int n)
{
M_ASSERT (s != NULL && n > 0 && n <= M_GENINT_MAX_ALLOC);
const size_t alloc = (n + M_GEN1NT_LIMBSIZE - 1) / M_GEN1NT_LIMBSIZE;
const unsigned int index = n % M_GEN1NT_LIMBSIZE;
atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, alloc);
atomic_ullong *ptr = M_MEMORY_REALLOC (m_context, atomic_ullong, NULL, 0, alloc);
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(alloc);
return;
M_MEMORY_FULL(atomic_ullong, alloc);
}
s->n = n;
s->data = ptr;
@@ -126,11 +124,10 @@ m_genint_init(m_genint_t s, unsigned int n)
}
/* Clear an integer generator (Destructor) */
M_INLINE void
m_genint_clear(m_genint_t s)
M_P(void, m_genint, _clear, m_genint_t s)
{
M_GEN1NT_CONTRACT(s);
M_MEMORY_FREE(s->data);
M_MEMORY_FREE(m_context, atomic_ullong, s->data, s->max+1);
s->data = NULL;
}
+14 -23
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Intrusive List module
*
* Copyright (c) 2017-2024, 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:
@@ -71,6 +71,10 @@
((__VA_ARGS__, M_BASIC_OPLIST), \
(__VA_ARGS__ )))
#define M_ILIST_INIT_FIELD(name, obj) do { \
(obj).name.next = NULL; \
(obj).name.prev = NULL; \
} while(0)
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
@@ -117,7 +121,6 @@ typedef struct m_il1st_head_s {
IT_REF(M_F(name,_ref)), \
IT_CREF(M_F(name,_cref)), \
IT_REMOVE(M_F(name,_remove)), \
M_IF_METHOD(NEW, oplist)(IT_INSERT(M_F(name,_insert)),), \
OPLIST(oplist), \
SPLICE_BACK(M_F(name,_splice_back)) \
)
@@ -207,8 +210,7 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
M_INLINE void \
M_F(name, _reset)(list_t list) \
M_P(void, name, _reset, list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
for(struct m_il1st_head_s *it = list->name.next, *next ; \
@@ -229,11 +231,10 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
M_INLINE void \
M_F(name, _clear)(list_t list) \
M_P(void, name, _clear, list_t list) \
{ \
/* Nothing to do more than clean the list itself */ \
M_F(name, _reset)(list); \
M_F(name, _reset)M_R(list); \
/* For safety purpose (create invalid representation of object) */ \
list->name.next = NULL; \
list->name.prev = NULL; \
@@ -264,10 +265,9 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
M_INLINE void \
M_F(name, _move)(list_t list, list_t ref) \
M_P(void, name, _move, list_t list, list_t ref) \
{ \
M_F(name, _clear)(list); \
M_F(name, _clear)M_R(list); \
M_F(name, _init_move)(list, ref); \
} \
\
@@ -508,8 +508,7 @@ typedef struct m_il1st_head_s {
return M_CONST_CAST(type, ptr); \
} \
\
M_INLINE void \
M_F(name, _remove)(list_t list, it_t it) \
M_P(void, name, _remove, list_t list, it_t it) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_IL1ST_NODE_CONTRACT(it->current); \
@@ -517,30 +516,22 @@ typedef struct m_il1st_head_s {
type *obj = M_TYPE_FROM_FIELD(type, it->current, \
struct m_il1st_head_s, name); \
M_F(name, _unlink)(obj); \
M_CALL_CLEAR(oplist, obj); \
M_CALL_CLEAR(oplist, *obj); \
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
M_F(name, _next)(it); \
} \
\
M_IF_METHOD2(NEW, INIT_SET, oplist)( \
M_INLINE void \
M_F(name, _insert)(list_t list, it_t it, type x) \
M_F(name, _insert)(list_t list, it_t it, type *p) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_IL1ST_NODE_CONTRACT(it->current); \
type *p = M_CALL_NEW(oplist, type); \
if (M_UNLIKELY_NOMEM (p == NULL)) { \
M_MEMORY_FULL (sizeof (type)); \
return ; \
} \
M_CALL_INIT_SET(oplist, *p, x); \
type *obj = M_F(name, _ref)(it); \
M_F(name, _push_after)(obj, p); \
it->current = p; \
it->current = &p->name; \
(void) list; \
M_IL1ST_CONTRACT(name, list); \
} \
, /* NEW & INIT_SET not defined */) \
\
M_INLINE type * \
M_F(name, _pop_back)(list_t list) \
-255
View File
@@ -1,255 +0,0 @@
/*
* M*LIB - INTRUSIVE SHARED PTR Module
*
* Copyright (c) 2017-2024, 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_I_SHARED_PTR_H
#define MSTARLIB_I_SHARED_PTR_H
#include "m-core.h"
#include "m-atomic.h"
M_BEGIN_PROTECTED_CODE
/* Define the oplist of a intrusive shared pointer.
USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */
#define M_ISHARED_PTR_OPLIST(...) \
M_ISHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_BASIC_OPLIST), \
(__VA_ARGS__ )))
/* Interface to add to a structure to allow intrusive support.
name: name of the intrusive shared pointer.
type: name of the type of the structure (aka. struct test_s) - not used currently.
NOTE: There can be only one interface of this kind in a type! */
#define M_ISHARED_PTR_INTERFACE(name, type) \
atomic_int M_F(name, _cpt)
/* Value of the interface field for static initialization (Uses C99 designated element). */
#define M_ISHARED_PTR_STATIC_DESIGNATED_INIT(name, type) \
.M_F(name, _cpt) = M_ATOMIC_VAR_INIT(0)
/* Value of the interface field for static initialization (Uses C89 designated element). */
#define M_ISHARED_PTR_STATIC_INIT(name, type) \
M_ATOMIC_VAR_INIT(0)
/* Define the intrusive shared pointer type and its M_INLINE functions.
USAGE: ISHARED_PTR_DEF(name, type, [, oplist]) */
#define M_ISHARED_PTR_DEF(name, ...) \
M_ISHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define the intrusive shared pointer type and its M_INLINE functions
as the name name_t
USAGE: ISHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */
#define M_ISHARED_PTR_DEF_AS(name, name_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_ISHAR3D_PTR_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
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
/*****************************************************************************/
// Deferred evaluation
#define M_ISHAR3D_PTR_OPLIST_P1(arg) M_ISHAR3D_PTR_OPLIST_P2 arg
/* Validation of the given oplist */
#define M_ISHAR3D_PTR_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_OPLIST_P3, M_ISHAR3D_PTR_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define M_ISHAR3D_PTR_OPLIST_FAILURE(name, oplist) \
((M_LIB_ERROR(ARGUMENT_OF_ISHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
// Define the oplist
#define M_ISHAR3D_PTR_OPLIST_P3(name, oplist) ( \
INIT(M_INIT_DEFAULT), \
INIT_SET(API_4(M_F(name, _init_set))), \
SET(M_F(name, _set) M_IPTR), \
CLEAR(M_F(name, _clear)), \
RESET(M_F(name, _reset) M_IPTR), \
NAME(name), \
TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \
OPLIST(oplist), \
SUBTYPE(M_F(name, _subtype_ct)) \
)
/******************************** INTERNAL ***********************************/
// Deferred evaluation
#define M_ISHAR3D_PTR_DEF_P1(arg) M_ID( M_ISHAR3D_PTR_DEF_P2 arg )
/* Validate the oplist before going further */
#define M_ISHAR3D_PTR_DEF_P2(name, type, oplist, shared_t) \
M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_DEF_P3, M_ISHAR3D_PTR_DEF_FAILURE)(name, type, oplist, shared_t)
/* Stop processing with a compilation failure */
#define M_ISHAR3D_PTR_DEF_FAILURE(name, type, oplist, shared_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ISHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist)
#define M_ISHAR3D_PTR_DEF_P3(name, type, oplist, shared_t) \
M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \
/* Define the types */
#define M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \
\
/* The shared pointer is only a pointer to the type */ \
typedef type *shared_t; \
\
/* Define internal types for oplist */ \
typedef shared_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
/* Define the core functions */
#define M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \
\
M_INLINE shared_t \
M_F(name, _init)(type *ptr) \
{ \
/* Initialize the type referenced by the pointer */ \
if (M_LIKELY (ptr != NULL)) { \
atomic_init(&ptr->M_F(name, _cpt), 2); \
} \
return ptr; \
} \
\
M_INLINE shared_t \
M_F(name, _init_set)(shared_t shared) \
{ \
if (M_LIKELY (shared != NULL)) { \
int n = atomic_fetch_add(&(shared->M_F(name, _cpt)), 2); \
(void) n; \
} \
return shared; \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_IF_DISABLED_METHOD(NEW, oplist) \
( \
/* This function is only for static object */ \
M_INLINE shared_t \
M_F(name, _init_once)(type *shared) \
{ \
if (M_LIKELY (shared != NULL)) { \
/* Pretty much like atomic_add, except the first one increment by 1, others by 2 */ \
int o = atomic_load(&(shared->M_F(name, _cpt))); \
int n; \
do { \
n = o + 1 + (o != 0); \
} while (!atomic_compare_exchange_strong(&(shared->M_F(name, _cpt)), &o, n)); \
if (o == 0) { \
/* Partial initialization: _cpt is odd */ \
/* Call the INIT function once */ \
M_CALL_INIT(oplist, *shared); \
/* Finish initialization: _cpt is even */ \
atomic_fetch_add(&(shared->M_F(name, _cpt)), 1); \
} else if ( (o&1) != 0) { \
/* Not fully initialized yet: wait for initialization */ \
m_core_backoff_ct bkoff; \
m_core_backoff_init(bkoff); \
/* Wait for _cpt to be _even */ \
while ((atomic_load(&(shared->M_F(name, _cpt)))&1) != 0 ) { \
m_core_backoff_wait(bkoff); \
} \
} \
M_ASSERT( (atomic_load(&(shared->M_F(name, _cpt)))&1) == 0); \
} \
return shared; \
} \
, \
/* This function is only for dynamic object */ \
M_INLINE shared_t \
M_F(name, _init_new)(void) \
{ \
type *ptr = M_CALL_NEW(oplist, type); \
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
M_MEMORY_FULL(sizeof(type)); \
return NULL; \
} \
M_CALL_INIT(oplist, *ptr); \
atomic_init (&ptr->M_F(name, _cpt), 2); \
return ptr; \
} \
/* End of NEW */) \
, /* End of INIT */) \
\
M_INLINE void \
M_F(name, _clear)(shared_t shared) \
{ \
if (shared != NULL) { \
if (atomic_fetch_sub(&(shared->M_F(name, _cpt)), 2) == 2) { \
M_CALL_CLEAR(oplist, *shared); \
M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \
} \
} \
} \
\
M_INLINE void \
M_F(name, _clear_ptr)(shared_t *shared) \
{ \
M_ASSERT(shared != NULL); \
M_F(name, _clear)(*shared); \
*shared = NULL; \
} \
\
M_INLINE void \
M_F(name, _reset)(shared_t *shared) \
{ \
M_F(name, _clear)(*shared); \
*shared = NULL; \
} \
\
M_INLINE void \
M_F(name, _set)(shared_t *ptr, shared_t shared) \
{ \
M_ASSERT (ptr != NULL); \
if (M_LIKELY (*ptr != shared)) { \
M_F(name, _clear)(*ptr); \
*ptr = M_F(name, _init_set)(shared); \
} \
} \
\
M_END_PROTECTED_CODE
/******************************** INTERNAL ***********************************/
#if M_USE_SMALL_NAME
#define ISHARED_PTR_OPLIST M_ISHARED_PTR_OPLIST
#define ISHARED_PTR_INTERFACE M_ISHARED_PTR_INTERFACE
#define ISHARED_PTR_STATIC_DESIGNATED_INIT M_ISHARED_PTR_STATIC_DESIGNATED_INIT
#define ISHARED_PTR_STATIC_INIT M_ISHARED_PTR_STATIC_INIT
#define ISHARED_PTR_DEF M_ISHARED_PTR_DEF
#define ISHARED_PTR_DEF_AS M_ISHARED_PTR_DEF_AS
#endif
#endif
+172 -203
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - LIST module
*
* Copyright (c) 2017-2024, 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:
@@ -86,6 +86,8 @@
/********************************** INTERNAL ************************************/
//TODO: Provide an update scheme for mempool using new memory allocator context.
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define M_L1ST_OPLIST_P1(arg) M_L1ST_OPLIST_P2 arg
@@ -99,6 +101,7 @@
((M_LIB_ERROR(ARGUMENT_OF_LIST_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* OPLIST definition of a list and list_dual_push */
#ifndef M_USE_CONTEXT
#define M_L1ST_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)), \
INIT_SET(M_F(name, _init_set)), \
@@ -142,6 +145,51 @@
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
)
#else
#define M_L1ST_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)), \
INIT_SET(API_0P(M_F(name, _init_set))), \
INIT_WITH(API_1(M_INIT_WITH_VAI)), \
SET(API_0P(M_F(name, _set))), \
CLEAR(API_0P(M_F(name, _clear))), \
MOVE(API_0P(M_F(name, _move))), \
INIT_MOVE(M_F(name, _init_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_END(M_F(name,_it_end)), \
IT_SET(M_F(name,_it_set)), \
IT_END_P(M_F(name,_end_p)), \
IT_EQUAL_P(M_F(name,_it_equal_p)), \
IT_LAST_P(M_F(name,_last_p)), \
IT_NEXT(M_F(name,_next)), \
IT_REF(M_F(name,_ref)), \
IT_CREF(M_F(name,_cref)), \
IT_INSERT(API_0P(M_F(name, _insert))), \
IT_REMOVE(API_0P(M_F(name,_remove))), \
RESET(API_0P(M_F(name,_reset))), \
PUSH(API_0P(M_F(name,_push_back))), \
POP(API_0P(M_F(name,_pop_back))), \
PUSH_MOVE(API_0P(M_F(name,_push_move))), \
POP_MOVE(API_0P(M_F(name,_pop_move))) \
,SPLICE_BACK(M_F(name,_splice_back)) \
,SPLICE_AT(M_F(name,_splice_at)) \
,REVERSE(M_F(name,_reverse)) \
,OPLIST(oplist) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(API_0P(M_F(name, _get_str))),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(API_0P(M_F(name, _parse_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 for the list definition,
so that all arguments are evaluated before further expansion */
@@ -155,29 +203,6 @@
#define M_L1ST_DEF_FAILURE(name, type, oplist, list_t, it_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(LIST_DEF): the given argument is not a valid oplist: " #oplist)
/* Define allocation functions. If MEMPOOL, we need to define it */
#define M_L1ST_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
M_IF_METHOD(MEMPOOL, oplist)( \
\
MEMPOOL_DEF(M_F(name, _mempool), struct M_F(name, _s)) \
M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \
M_INLINE struct M_F(name, _s) *M_C3(m_l1st_,name,_new)(void) { \
return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \
} \
M_INLINE void M_C3(m_l1st_,name,_del)(struct M_F(name, _s) *ptr) { \
M_F(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \
} \
\
, /* No mempool allocation */ \
\
M_INLINE struct M_F(name, _s) *M_C3(m_l1st_,name,_new)(void) { \
return M_CALL_NEW(oplist, struct M_F(name, _s)); \
} \
M_INLINE void M_C3(m_l1st_,name,_del)(struct M_F(name, _s) *ptr) { \
M_CALL_DEL(oplist, ptr); \
} \
) \
/* Internal list definition
- name: prefix to be used
@@ -190,9 +215,8 @@
#define M_L1ST_DEF_P3(name, type, oplist, list_t, it_t) \
M_L1ST_DEF_TYPE(name, type, oplist, list_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_L1ST_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \
M_L1ST_DEF_P4(name, type, oplist, list_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, list_t, M_F(name, _emplace_back), oplist, M_L1ST_EMPLACE_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_back, oplist, M_L1ST_EMPLACE_DEF) \
M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t)
@@ -242,8 +266,7 @@
*v = NULL; \
} \
\
M_INLINE void \
M_F(name, _reset)(list_t v) \
M_P(void,name, _reset, list_t v) \
{ \
M_L1ST_CONTRACT(v); \
struct M_F(name, _s) *it = *v; \
@@ -251,16 +274,15 @@
while (it != NULL) { \
struct M_F(name, _s) *next = it->next; \
M_CALL_CLEAR(oplist, it->data); \
M_C3(m_l1st_,name,_del)(it); \
M_CALL_DEL(oplist, it); \
it = next; \
} \
M_L1ST_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _clear)(list_t v) \
M_P(void, name, _clear, list_t v) \
{ \
M_F(name, _reset)(v); \
M_F(name, _reset)M_R(v); \
} \
\
M_INLINE type * \
@@ -271,15 +293,12 @@
return &((*v)->data); \
} \
\
M_INLINE type * \
M_F(name, _push_raw)(list_t v) \
M_P(type *, name, _push_raw, list_t v) \
{ \
M_L1ST_CONTRACT(v); \
struct M_F(name, _s) *next; \
next = M_C3(m_l1st_,name,_new)(); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
return NULL; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
type *ret = &next->data; \
next->next = *v; \
@@ -288,35 +307,32 @@
return ret; \
} \
\
M_INLINE void \
M_F(name, _push_back)(list_t v, type const x) \
M_P(void, name, _push_back, list_t v, type const x) \
{ \
type *data = M_F(name, _push_raw)(v); \
type *data = M_F(name, _push_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
M_ON_EXCEPTION( *v = next->next, M_C3(m_l1st_,name,_del)(next)) { \
M_ON_EXCEPTION( *v = next->next, M_CALL_DEL(oplist, next)) { \
M_CALL_INIT_SET(oplist, *data, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _push_new)(list_t v) \
M_P(type *, name, _push_new, list_t v) \
{ \
type *data = M_F(name, _push_raw)(v); \
type *data = M_F(name, _push_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
M_ON_EXCEPTION( *v = next->next, M_C3(m_l1st_,name,_del)(next)) { \
M_ON_EXCEPTION( *v = next->next, M_CALL_DEL(oplist, next)) { \
M_CALL_INIT(oplist, *data); \
} \
return data; \
} \
, /* No INIT */) \
\
M_INLINE void \
M_F(name, _pop_back)(type *data, list_t v) \
M_P(void, name, _pop_back, type *data, list_t v) \
{ \
M_L1ST_CONTRACT(v); \
M_ASSERT(*v != NULL); \
@@ -327,29 +343,27 @@
} \
struct M_F(name, _s) *tofree = *v; \
*v = (*v)->next; \
M_C3(m_l1st_,name,_del)(tofree); \
M_CALL_DEL(oplist, tofree); \
M_L1ST_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _push_move)(list_t v, type *x) \
M_P(void, name, _push_move, list_t v, type *x) \
{ \
M_ASSERT (x != NULL); \
type *data = M_F(name, _push_raw)(v); \
type *data = M_F(name, _push_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_DO_INIT_MOVE (oplist, *data, *x); \
M_CALL_INIT_MOVE (oplist, *data, *x); \
} \
\
M_INLINE void \
M_F(name, _pop_move)(type *data, list_t v) \
M_P(void, name, _pop_move, type *data, list_t v) \
{ \
M_L1ST_CONTRACT(v); \
M_ASSERT(*v != NULL && data != NULL); \
M_DO_INIT_MOVE (oplist, *data, (*v)->data); \
M_CALL_INIT_MOVE (oplist, *data, (*v)->data); \
struct M_F(name, _s) *tofree = *v; \
*v = (*v)->next; \
M_C3(m_l1st_,name,_del)(tofree); \
M_CALL_DEL(oplist, tofree); \
M_L1ST_CONTRACT(v); \
} \
\
@@ -490,19 +504,16 @@
return M_CONST_CAST(type, M_F(name, _get)(l,i)); \
} \
\
M_INLINE void \
M_F(name, _insert)(list_t list, it_t insertion_point, \
type const x) \
M_P(void, name, _insert, list_t list, it_t insertion_point, type const x) \
{ \
M_L1ST_CONTRACT(list); \
M_ASSERT (insertion_point != NULL); \
M_ASSERT(M_F(name, _sublist_p)(list, insertion_point)); \
struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \
M_ASSERT (M_F(name, _sublist_p)(list, insertion_point)); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
return; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
M_ON_EXCEPTION( M_C3(m_l1st_,name,_del)(next)) \
M_ON_EXCEPTION( M_CALL_DEL(oplist, next)) \
M_CALL_INIT_SET(oplist, next->data, x); \
struct M_F(name, _s) *current = insertion_point->current; \
if (M_UNLIKELY (current == NULL)) { \
@@ -518,13 +529,12 @@
M_L1ST_CONTRACT(list); \
} \
\
M_INLINE void \
M_F(name, _remove)(list_t list, it_t removing_point) \
M_P(void, name, _remove, list_t list, it_t removing_point) \
{ \
M_L1ST_CONTRACT(list); \
M_ASSERT (removing_point != NULL); \
M_ASSERT (removing_point->current != NULL); \
M_ASSERT(M_F(name, _sublist_p)(list, removing_point)); \
M_ASSERT (M_F(name, _sublist_p)(list, removing_point)); \
struct M_F(name, _s) *next = removing_point->current->next; \
if (M_UNLIKELY (removing_point->previous == NULL)) { \
*list = next; \
@@ -532,13 +542,12 @@
removing_point->previous->next = next; \
} \
M_CALL_CLEAR(oplist, removing_point->current->data); \
M_C3(m_l1st_,name,_del) (removing_point->current); \
M_CALL_DEL(oplist, removing_point->current); \
removing_point->current = next; \
M_L1ST_CONTRACT(list); \
} \
\
M_INLINE void \
M_F(name, _init_set)(list_t list, const list_t org) \
M_P(void, name, _init_set, list_t list, const list_t org) \
{ \
M_L1ST_CONTRACT(org); \
struct M_F(name, _s) *m_volatile next = NULL; \
@@ -546,15 +555,14 @@
struct M_F(name, _s) **update_list; \
update_list = list; \
it_org = *org; \
/* On exceptions, free node and clear list*/ \
/* If exceptions, always keep list as a valid list */ \
M_IF_EXCEPTION(*update_list = NULL); \
M_ON_EXCEPTION(M_C3(m_l1st_,name,_del)(next), M_F(name, _clear)(list) ) \
/* On exceptions, free node and clear list*/ \
M_ON_EXCEPTION(M_CALL_DEL(oplist, next), M_F(name, _clear)(list) ) \
while (it_org != NULL) { \
next = M_C3(m_l1st_,name,_new)(); \
next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
*update_list = NULL; \
return; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
M_CALL_INIT_SET(oplist, next->data, it_org->data); \
*update_list = next; \
@@ -566,13 +574,12 @@
M_L1ST_CONTRACT(list); \
} \
\
M_INLINE void \
M_F(name, _set)(list_t list, const list_t org) \
M_P(void, name, _set, list_t list, const list_t org) \
{ \
if (M_UNLIKELY (list == org)) return; \
M_ON_EXCEPTION(M_F(name, _init)(list) ) { \
M_F(name, _clear)(list); \
M_F(name, _init_set)(list, org); \
M_F(name, _clear)M_R(list); \
M_F(name, _init_set)M_R(list, org); \
} \
} \
\
@@ -585,11 +592,10 @@
*org = NULL; /* safer */ \
} \
\
M_INLINE void \
M_F(name, _move)(list_t list, list_t org) \
M_P(void, name, _move, list_t list, list_t org) \
{ \
M_ASSERT (list != org); \
M_F(name, _clear)(list); \
M_F(name, _clear)M_R(list); \
M_F(name, _init_move)(list, org); \
} \
\
@@ -699,12 +705,10 @@
#define M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t) \
\
M_IF_METHOD(GET_STR, oplist)( \
M_INLINE void \
M_F(name, _get_str)(m_string_t str, const list_t list, \
bool append) \
M_P(void, name, _get_str, m_string_t str, const list_t list, bool append) \
{ \
M_ASSERT (str != NULL && list != NULL); \
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
it_t it; \
for (M_F(name, _it)(it, list) ; \
!M_F(name, _end_p)(it); \
@@ -712,9 +716,9 @@
type const *item = M_F(name, _cref)(it); \
M_CALL_GET_STR(oplist, str, *item, true); \
if (!M_F(name, _last_p)(it)) \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
} \
m_string_push_back (str, ']'); \
m_string_push_back M_R(str, ']'); \
} \
, /* no str */ ) \
\
@@ -738,11 +742,10 @@
, /* no out_str */ ) \
\
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
M_INLINE bool \
M_F(name, _parse_str)(list_t list, const char str[], const char **endp) \
M_P(bool, name, _parse_str, list_t list, const char str[], const char **endp) \
{ \
M_ASSERT (str != NULL && list != NULL); \
M_F(name,_reset)(list); \
M_F(name,_reset)M_R(list); \
bool success = false; \
int c = *str++; \
if (M_UNLIKELY (c != '[')) goto exit; \
@@ -755,7 +758,7 @@
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
c = m_core_str_nospace(&str); \
if (b == false || c == 0) { c = 0; break; } \
M_F(name, _push_back)(list, item); \
M_F(name, _push_back)M_R(list, item); \
} while (c == M_GET_SEPARATOR oplist); \
success = (c == ']'); \
} \
@@ -767,11 +770,10 @@
, /* no PARSE_STR & INIT */ ) \
\
M_IF_METHOD2(IN_STR, INIT, oplist)( \
M_INLINE bool \
M_F(name, _in_str)(list_t list, FILE *file) \
M_P(bool, name, _in_str, list_t list, FILE *file) \
{ \
M_ASSERT (file != NULL && list != NULL); \
M_F(name,_reset)(list); \
M_F(name,_reset)M_R(list); \
int c = fgetc(file); \
if (M_UNLIKELY (c != '[')) return false; \
c = fgetc(file); \
@@ -783,7 +785,7 @@
bool b = M_CALL_IN_STR(oplist, item, file); \
c = m_core_fgetc_nospace(file); \
if (b == false || c == EOF) { c = 0; break; } \
M_F(name, _push_back)(list, item); \
M_F(name, _push_back)M_R(list, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
M_F(name, _reverse)(list); \
@@ -792,18 +794,17 @@
, /* no IN_STR & INIT */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, const list_t list) \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const list_t list) \
{ \
M_ASSERT (list != NULL); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
bool first_done = false; \
ret = f->m_interface->write_array_start(local, f, (size_t)-1); \
ret = f->m_interface->write_array_start M_R(local, f, (size_t)-1); \
if (ret == M_SERIAL_FAIL_RETRY) { \
size_t n = M_F(name, _size)(list); \
ret = f->m_interface->write_array_start(local, f, n); \
ret = f->m_interface->write_array_start M_R(local, f, n); \
} \
it_t it; \
for (M_F(name, _it)(it, list) ; \
@@ -811,32 +812,31 @@
M_F(name, _next)(it)){ \
type const *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(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_METHOD2(IN_SERIAL, INIT, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(list_t list, m_serial_read_t f) \
M_P(m_serial_return_code_t, name, _in_serial, list_t list, m_serial_read_t f) \
{ \
M_ASSERT (list != NULL); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_return_code_t ret; \
m_serial_local_t local; \
size_t estimated_size = 0; \
M_F(name,_reset)(list); \
M_F(name,_reset)M_R(list); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_QLET(1, item, type, oplist) { \
do { \
ret = M_CALL_IN_SERIAL(oplist, item, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_F(name, _push_back)(list, item); \
M_F(name, _push_back)M_R(list, item); \
ret = f->m_interface->read_array_next(local, f); \
} while (ret == M_SERIAL_OK_CONTINUE); \
} \
@@ -890,15 +890,13 @@
/* Definition of the emplace_back function for single list */
#define M_L1ST_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_F(name, _subtype_ct) *data = M_F(name, _push_raw)(v); \
M_F(name, _subtype_ct) *data = M_F(name, _push_raw)M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
M_ON_EXCEPTION( *v = next->next, M_C3(m_l1st_,name,_del)(next)) { \
M_ON_EXCEPTION( *v = next->next, M_CALL_DEL(oplist, next)) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
}
@@ -906,15 +904,13 @@
/* Definition of the emplace_back function for dual push list */
#define M_L1ST_EMPLACE_BACK_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)(v); \
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->back); \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_C3(m_l1st_,name,_del)(next)) { \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_CALL_DEL(oplist, next)) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
}
@@ -922,17 +918,15 @@
/* Definition of the emplace_front function for dual push list */
#define M_L1ST_EMPLACE_FRONT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_IF_EXCEPTION(struct M_F(name, _s) *front = v->front, *back = v->back); \
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)(v); \
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->front); \
M_IF_EXCEPTION(M_ASSERT(tofree != NULL)); \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_C3(m_l1st_,name,_del)(tofree)) { \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
}
@@ -969,10 +963,9 @@
#define M_L1ST_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \
M_L1ST_DUAL_PUSH_DEF_TYPE(name, type, oplist, list_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_L1ST_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \
M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, list_t, M_F(name, _emplace_back), oplist, M_L1ST_EMPLACE_BACK_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, M_F(name, _emplace_front), oplist, M_L1ST_EMPLACE_FRONT_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_back, oplist, M_L1ST_EMPLACE_BACK_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_front, oplist, M_L1ST_EMPLACE_FRONT_DEF) \
M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t)
@@ -1026,15 +1019,14 @@
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _reset)(list_t v) \
M_P(void, name, _reset, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
struct M_F(name, _s) *it = v->back; \
while (it != NULL) { \
struct M_F(name, _s) *next = it->next; \
M_CALL_CLEAR(oplist, it->data); \
M_C3(m_l1st_,name,_del)(it); \
M_CALL_DEL(oplist, it); \
it = next; \
} \
v->front = NULL; \
@@ -1042,10 +1034,9 @@
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _clear)(list_t v) \
M_P(void, name, _clear, list_t v) \
{ \
M_F(name, _reset)(v); \
M_F(name, _reset)M_R(v); \
} \
\
M_INLINE type * \
@@ -1056,20 +1047,18 @@
return &(v->back->data); \
} \
\
M_INLINE type * \
M_F(name, _push_back_raw)(list_t v) \
M_P(type *, name, _push_back_raw, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
return NULL; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
type *ret = &next->data; \
next->next = v->back; \
v->back = next; \
/* Update front too if the list was empty */ \
/* This C code shall generate branchless code */ \
/* This C code should generate branchless code */ \
struct M_F(name, _s) *front = v->front; \
front = (front == NULL) ? next : front; \
v->front = front; \
@@ -1078,57 +1067,51 @@
} \
\
/* Internal, for INIT_WITH */ \
M_INLINE type * \
M_F(name, _push_raw)(list_t d) \
M_P(type *, name, _push_raw, list_t d) \
{ \
return M_F(name, _push_back_raw)(d); \
return M_F(name, _push_back_raw)M_R(d); \
} \
\
M_INLINE void \
M_F(name, _push_back)(list_t v, type const x) \
M_P(void, name, _push_back, list_t v, type const x) \
{ \
type *data = M_F(name, _push_back_raw)(v); \
type *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->back); \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_C3(m_l1st_,name,_del)(next)) { \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_CALL_DEL(oplist, next)) { \
M_CALL_INIT_SET(oplist, *data, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _push_back_new)(list_t v) \
M_P(type *, name, _push_back_new, list_t v) \
{ \
type *data = M_F(name, _push_back_raw)(v); \
type *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->back); \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_C3(m_l1st_,name,_del)(next)) { \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_CALL_DEL(oplist, next)) { \
M_CALL_INIT(oplist, *data); \
} \
return data; \
} \
, /* No INIT */ ) \
\
M_INLINE void \
M_F(name, _push_back_move)(list_t v, type *x) \
M_P(void, name, _push_back_move, list_t v, type *x) \
{ \
M_ASSERT (x != NULL); \
type *data = M_F(name, _push_back_raw)(v); \
type *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_DO_INIT_MOVE (oplist, *data, *x); \
M_CALL_INIT_MOVE (oplist, *data, *x); \
} \
\
M_INLINE void \
M_F(name, _push_move)(list_t v, type *x) \
M_P(void, name, _push_move, list_t v, type *x) \
{ \
M_F(name, _push_back_move)(v, x); \
M_F(name, _push_back_move)M_R(v, x); \
} \
\
M_INLINE void \
M_F(name, _pop_back)(type *data, list_t v) \
M_P(void, name, _pop_back, type *data, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (v->back != NULL); \
@@ -1139,7 +1122,7 @@
M_CALL_CLEAR(oplist, tofree->data); \
} \
v->back = tofree->next; \
M_C3(m_l1st_,name,_del)(tofree); \
M_CALL_DEL(oplist, tofree); \
/* Update front too if the list became empty */ \
/* This C code shall generate branchless code */ \
struct M_F(name, _s) *front = v->front; \
@@ -1148,16 +1131,15 @@
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
M_INLINE void \
M_F(name, _pop_move)(type *data, list_t v) \
M_P(void, name, _pop_move, type *data, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (v->back != NULL); \
M_ASSERT (data != NULL); \
struct M_F(name, _s) *tofree = v->back; \
M_DO_INIT_MOVE (oplist, *data, tofree->data); \
M_CALL_INIT_MOVE (oplist, *data, tofree->data); \
v->back = tofree->next; \
M_C3(m_l1st_,name,_del)(tofree); \
M_CALL_DEL(oplist, tofree); \
/* Update front too if the list became empty */ \
/* This C code shall generate branchless code */ \
struct M_F(name, _s) *front = v->front; \
@@ -1174,14 +1156,12 @@
return &(v->front->data); \
} \
\
M_INLINE type * \
M_F(name, _push_front_raw)(list_t v) \
M_P(type *, name, _push_front_raw, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
struct M_F(name, _s) *next = M_C3(m_l1st_,name,_new)(); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
return NULL; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
type *ret = &next->data; \
next->next = NULL; \
@@ -1196,41 +1176,38 @@
return ret; \
} \
\
M_INLINE void \
M_F(name, _push_front)(list_t v, type const x) \
M_P(void, name, _push_front, list_t v, type const x) \
{ \
M_IF_EXCEPTION(struct M_F(name, _s) *front = v->front, *back = v->back); \
type *data = M_F(name, _push_front_raw)(v); \
type *data = M_F(name, _push_front_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->front); \
M_IF_EXCEPTION(M_ASSERT(tofree != NULL)); \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_C3(m_l1st_,name,_del)(tofree)) { \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_CALL_INIT_SET(oplist, *data, x); \
} \
} \
\
M_INLINE void \
M_F(name, _push_front_move)(list_t v, type *x) \
M_P(void, name, _push_front_move, list_t v, type *x) \
{ \
M_ASSERT (x != NULL); \
type *data = M_F(name, _push_front_raw)(v); \
type *data = M_F(name, _push_front_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_DO_INIT_MOVE (oplist, *data, *x); \
M_CALL_INIT_MOVE (oplist, *data, *x); \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE type * \
M_F(name, _push_front_new)(list_t v) \
M_P(type *, name, _push_front_new, list_t v) \
{ \
M_IF_EXCEPTION(struct M_F(name, _s) *front = v->front, *back = v->back); \
type *data = M_F(name, _push_front_raw)(v); \
type *data = M_F(name, _push_front_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->front); \
M_IF_EXCEPTION(M_ASSERT(tofree != NULL)); \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_C3(m_l1st_,name,_del)(tofree)) { \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_CALL_INIT(oplist, *data); \
} \
return data; \
@@ -1336,18 +1313,15 @@
return size; \
} \
\
M_INLINE void \
M_F(name, _insert)(list_t list, it_t insertion_point, \
type const x) \
M_P(void, name, _insert, list_t list, it_t insertion_point, type const x) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(list); \
M_ASSERT (insertion_point != NULL); \
struct M_F(name, _s) *m_volatile next = M_C3(m_l1st_,name,_new)(); \
struct M_F(name, _s) *m_volatile next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
return; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
M_ON_EXCEPTION( M_C3(m_l1st_,name,_del)(next)) \
M_ON_EXCEPTION( M_CALL_DEL(oplist, next)) \
M_CALL_INIT_SET(oplist, next->data, x); \
if (M_UNLIKELY (insertion_point->current == NULL)) { \
next->next = list->back; \
@@ -1366,8 +1340,7 @@
} \
} \
\
M_INLINE void \
M_F(name, _remove)(list_t list, it_t removing_point) \
M_P(void, name, _remove, list_t list, it_t removing_point) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(list); \
M_ASSERT (removing_point != NULL); \
@@ -1385,12 +1358,11 @@
list->front = front; \
/* Remove node */ \
M_CALL_CLEAR(oplist, removing_point->current->data); \
M_C3(m_l1st_,name,_del) (removing_point->current); \
M_CALL_DEL(oplist, removing_point->current); \
removing_point->current = next; \
} \
\
M_INLINE void \
M_F(name, _set)(list_t list, const list_t org) \
M_P(void, name, _set, list_t list, const list_t org) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(list); \
M_L1ST_DUAL_PUSH_CONTRACT(org); \
@@ -1398,16 +1370,15 @@
struct M_F(name, _s) *it_org; \
struct M_F(name, _s) **update_list; \
if (M_UNLIKELY (list == org)) return; \
M_F(name, _reset)(list); \
M_F(name, _reset)M_R(list); \
update_list = &list->back; \
it_org = org->back; \
M_ASSERT(*update_list == NULL); \
M_ON_EXCEPTION(M_C3(m_l1st_,name,_del)(next) ) \
M_ON_EXCEPTION(M_CALL_DEL(oplist, next) ) \
while (it_org != NULL) { \
next = M_C3(m_l1st_,name,_new)(); \
next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (next == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _s))); \
return; \
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
M_CALL_INIT_SET(oplist, next->data, it_org->data); \
/* Link new node to the previous one*/ \
@@ -1421,13 +1392,12 @@
} \
} \
\
M_INLINE void \
M_F(name, _init_set)(list_t list, const list_t org) \
M_P(void, name, _init_set, list_t list, const list_t org) \
{ \
M_ASSERT (list != org); \
M_ON_EXCEPTION( M_F(name, _clear)(list)) { \
M_F(name, _init)(list); \
M_F(name, _set)(list, org); \
M_F(name, _set) M_R(list, org); \
} \
} \
\
@@ -1441,10 +1411,9 @@
org->front = NULL; \
} \
\
M_INLINE void \
M_F(name, _move)(list_t list, list_t org) \
M_P(void, name, _move, list_t list, list_t org) \
{ \
M_F(name, _clear)(list); \
M_F(name, _clear)M_R(list); \
M_F(name, _init_move)(list, org); \
} \
\
-206
View File
@@ -1,206 +0,0 @@
/*
* M*LIB - MEMPOOL module
*
* Copyright (c) 2017-2024, 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_MEMPOOL_H
#define MSTARLIB_MEMPOOL_H
#include "m-core.h"
/* Fast, fixed size, thread unsafe allocator based on memory regions.
No oplist is needed.
USAGE:
MEMPOOL_DEF(name, type)
Example:
MEMPOOL_DEF(mempool_uint, unsigned int)
...
mempool_uint_t m;
mempool_uint_init(m);
unsigned int *ptr = mempool_uint_alloc(m);
*ptr = 17;
mempool_uint_free(m, ptr);
mempool_uint_clear(m); // Give back memory to system
*/
#define M_MEMPOOL_DEF(name, type) \
M_MEMPOOL_DEF_AS(name, M_F(name,_t), type)
/* Fast, fixed Size, thread unsafe allocator based on memory region.
USAGE:
MEMPOOL_DEF_AS(name, name_t, type)
*/
#define M_MEMPOOL_DEF_AS(name, name_t, type) \
M_BEGIN_PROTECTED_CODE \
M_M3MPOOL_DEF_P2(name, type, name_t ) \
M_END_PROTECTED_CODE
/* User shall be able to customize the size of the region segment and/or
the minimum number of elements.
The default is the number of elements that fits in 16KB, or 256
is the size of the type is too big.
*/
#ifndef M_USE_MEMPOOL_MAX_PER_SEGMENT
#define M_USE_MEMPOOL_MAX_PER_SEGMENT(type) \
M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U)
#endif
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/*
Technically, it uses a list of memory regions, where multiple
allocations are performed in each region. However, it
can not use m-list since it may be expanded from LIST_DEF
(recursive dependency problem). */
#define M_M3MPOOL_DEF_P2(name, type, name_t) \
M_M3MPOOL_DEF_TYPE(name, type, name_t) \
M_M3MPOOL_DEF_CORE(name, type, name_t)
/* Define the types of the mempool */
#define M_M3MPOOL_DEF_TYPE(name, type, name_t) \
\
/* Define the type of element in a segment of the mempool. \
Either it is the basic type or a pointer to another one. */ \
typedef union M_F(name,_union_s) { \
type t; \
union M_F(name,_union_s) *next; \
} M_F(name,_union_ct); \
\
/* Define a segment of a mempool. \
It is an array of basic type, each segment is in a linked list */ \
typedef struct M_F(name,_segment_s) { \
unsigned int count; \
struct M_F(name,_segment_s) *next; \
M_F(name,_union_ct) tab[M_USE_MEMPOOL_MAX_PER_SEGMENT(type)]; \
} M_F(name,_segment_ct); \
\
/* Define a mempool. \
It is a pointer to the first free object within the segments \
and the segments themselves */ \
typedef struct M_F(name, _s) { \
M_F(name,_union_ct) *free_list; \
M_F(name,_segment_ct) *current_segment; \
} name_t[1]; \
/* Define the core functions of the mempool */
#define M_M3MPOOL_DEF_CORE(name, type, name_t) \
\
M_INLINE void \
M_F(name,_init)(name_t mem) \
{ \
mem->free_list = NULL; \
mem->current_segment = M_MEMORY_ALLOC(M_F(name,_segment_ct)); \
if (M_UNLIKELY_NOMEM(mem->current_segment == NULL)) { \
M_MEMORY_FULL(sizeof (M_F(name,_segment_ct))); \
return; \
} \
mem->current_segment->next = NULL; \
mem->current_segment->count = 0; \
M_M3MPOOL_CONTRACT(mem, type); \
} \
\
M_INLINE void \
M_F(name,_clear)(name_t mem) \
{ \
M_M3MPOOL_CONTRACT(mem, type); \
M_F(name,_segment_ct) *segment = mem->current_segment; \
while (segment != NULL) { \
M_F(name,_segment_ct) *next = segment->next; \
M_MEMORY_DEL (segment); \
segment = next; \
} \
/* Clean pointers to be safer */ \
mem->free_list = NULL; \
mem->current_segment = NULL; \
} \
\
M_INLINE type * \
M_F(name,_alloc)(name_t mem) \
{ \
M_M3MPOOL_CONTRACT(mem, type); \
/* Test if one object is in the free list */ \
M_F(name,_union_ct) *ret = mem->free_list; \
if (ret != NULL) { \
/* Yes, so return it, and pop it from the free list */ \
mem->free_list = ret->next; \
return &ret->t; \
} \
/* No cheap free object exist. Test within a segment */ \
M_F(name,_segment_ct) *segment = mem->current_segment; \
M_ASSERT(segment != NULL); \
unsigned int count = segment->count; \
/* If segment is full, allocate a new one from the system */ \
if (M_UNLIKELY (count >= M_USE_MEMPOOL_MAX_PER_SEGMENT(type))) { \
M_F(name,_segment_ct) *new_segment = M_MEMORY_ALLOC (M_F(name,_segment_ct)); \
if (M_UNLIKELY_NOMEM (new_segment == NULL)) { \
M_MEMORY_FULL(sizeof (M_F(name,_segment_ct))); \
return NULL; \
} \
new_segment->next = segment; \
new_segment->count = 0; \
mem->current_segment = new_segment; \
segment = new_segment; \
count = 0; \
} \
/* Return the object as the last element of the current segment */ \
ret = &segment->tab[count]; \
segment->count = count + 1; \
M_M3MPOOL_CONTRACT(mem, type); \
return &ret->t; \
} \
\
M_INLINE void \
M_F(name,_free)(name_t mem, type *ptr) \
{ \
M_M3MPOOL_CONTRACT(mem, type); \
/* NOTE: Unsafe cast: suppose that the given pointer \
was allocated by the previous alloc function. */ \
M_F(name,_union_ct) *ret = (M_F(name,_union_ct) *)(uintptr_t)ptr; \
/* Add the object back in the free list */ \
ret->next = mem->free_list; \
mem->free_list = ret; \
/* NOTE: the objects are NOT given back to the system until the mempool \
is fully cleared */ \
M_M3MPOOL_CONTRACT(mem, type); \
} \
/* MEMPOOL contract. We only control the current segment. */
#define M_M3MPOOL_CONTRACT(mempool, type) do { \
M_ASSERT((mempool) != NULL); \
M_ASSERT((mempool)->current_segment != NULL); \
M_ASSERT((mempool)->current_segment->count <= M_USE_MEMPOOL_MAX_PER_SEGMENT(type)); \
} while (0)
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define MEMPOOL_DEF M_MEMPOOL_DEF
#define MEMPOOL_DEF_AS M_MEMPOOL_DEF_AS
#endif
#endif
-28
View File
@@ -1,28 +0,0 @@
/*
* M*LIB - Thin Mutex & Thread wrapper (compatibility layer)
*
* Copyright (c) 2017-2024, 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.
*/
#if defined(__GNUC__) && __GNUC__ >= 4
#warning "m-mutex.h is an obsolete header. Use m-thread.h instead."
#endif
#include "m-thread.h"
+68 -47
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic priority queue module
*
* Copyright (c) 2017-2024, 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,6 @@
#ifndef MSTARLIB_PRIOQUEUE_H
#define MSTARLIB_PRIOQUEUE_H
#include "m-core.h"
#include "m-array.h" /* Priority queue are built upon array */
/* Priority queue based on binary heap implementation */
@@ -72,6 +71,7 @@
((M_LIB_ERROR(ARGUMENT_OF_PRIOQUEUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* Define oplist of a priority queue */
#ifndef M_USE_CONTEXT
#define M_PR1OQUEUE_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)) \
,INIT_SET(M_F(name, _init_set)) \
@@ -106,7 +106,42 @@
,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)),) \
)
#else
#define M_PR1OQUEUE_OPLIST_P3(name, oplist) \
(INIT(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)) \
,RESET(API_0P(M_F(name,_reset))) \
,PUSH(API_0P(M_F(name,_push))) \
,POP(API_0P(M_F(name,_pop))) \
,OPLIST(oplist) \
,EMPTY_P(M_F(name, _empty_p)) \
,GET_SIZE(M_F(name, _size)) \
,IT_TYPE(M_F(name, _it_ct)) \
,IT_FIRST(M_F(name,_it)) \
,IT_END(M_F(name,_it_end)) \
,IT_SET(M_F(name,_it_set)) \
,IT_END_P(M_F(name,_end_p)) \
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
,IT_LAST_P(M_F(name,_last_p)) \
,IT_NEXT(M_F(name,_next)) \
,IT_CREF(M_F(name,_cref)) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(API_0P(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))),) \
)
#endif
/********************************** INTERNAL *********************************/
@@ -131,13 +166,13 @@
*/
#define M_PR1OQUEUE_DEF_P3(name, type, oplist, prioqueue_t, it_t) \
/* Definition of the internal array used to construct the priority queue */ \
ARRAY_DEF(M_F(name, _array), type, oplist) \
M_ARRAY_DEF(M_F(name, _array), type, oplist) \
M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_PR1OQUEUE_DEF_CORE(name, type, oplist, prioqueue_t, it_t) \
M_PR1OQUEUE_DEF_IT(name, type, oplist, prioqueue_t, it_t) \
M_PR1OQUEUE_DEF_IO(name, type, oplist, prioqueue_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, prioqueue_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
M_EMPLACE_QUEUE_DEF(name, prioqueue_t, _emplace, oplist, M_EMPLACE_QUEUE_GENE)
/* Define the types */
#define M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \
@@ -167,22 +202,19 @@
M_F(name, _array_init)(p->array); \
} \
\
M_INLINE void \
M_F(name, _init_set)(prioqueue_t p, prioqueue_t const o) \
M_P(void, name, _init_set, prioqueue_t p, prioqueue_t const o) \
{ \
M_F(name, _array_init_set)(p->array, o->array); \
M_F(name, _array_init_set) M_R(p->array, o->array); \
} \
\
M_INLINE void \
M_F(name, _set)(prioqueue_t p, prioqueue_t const o) \
M_P(void, name, _set, prioqueue_t p, prioqueue_t const o) \
{ \
M_F(name, _array_set)(p->array, o->array); \
M_F(name, _array_set) M_R(p->array, o->array); \
} \
\
M_INLINE void \
M_F(name, _clear)(prioqueue_t p) \
M_P(void, name, _clear, prioqueue_t p) \
{ \
M_F(name, _array_clear)(p->array); \
M_F(name, _array_clear) M_R(p->array); \
} \
\
M_INLINE void \
@@ -191,10 +223,9 @@
M_F(name, _array_init_move)(p->array, o->array); \
} \
\
M_INLINE void \
M_F(name, _move)(prioqueue_t p, prioqueue_t o) \
M_P(void, name, _move, prioqueue_t p, prioqueue_t o) \
{ \
M_F(name, _array_move)(p->array, o->array); \
M_F(name, _array_move) M_R(p->array, o->array); \
} \
\
M_INLINE void \
@@ -203,10 +234,9 @@
M_F(name, _array_swap)(p->array, o->array); \
} \
\
M_INLINE void \
M_F(name, _reset)(prioqueue_t p) \
M_P(void, name, _reset, prioqueue_t p) \
{ \
M_F(name, _array_reset)(p->array); \
M_F(name, _array_reset)M_R(p->array); \
} \
\
M_INLINE size_t \
@@ -249,11 +279,10 @@
return M_F(name, _array_size)(p->array); \
} \
\
M_INLINE void \
M_F(name, _push)(prioqueue_t p, type const x) \
M_P(void, name, _push, prioqueue_t p, type const x) \
{ \
/* Push back the new element at the end of the array */ \
M_F(name, _array_push_back)(p->array, x); \
M_F(name, _array_push_back) M_R(p->array, x); \
\
/* Reorder the array by swapping with its parent \
* until it reaches the right position */ \
@@ -273,14 +302,13 @@
return M_F(name, _array_cget)(p->array, 0); \
} \
\
M_INLINE void \
M_F(name, _pop)(type *x, prioqueue_t p) \
M_P(void, name, _pop, type *x, prioqueue_t p) \
{ \
/* Swap the front element with the last element */ \
size_t size = M_F(name, _array_size)(p->array)-1; \
M_F(name, _array_swap_at) (p->array, 0, size); \
/* Swap the new last element */ \
M_F(name, _array_pop_back)(x, p->array); \
M_F(name, _array_pop_back) M_R(x, p->array); \
\
/* Reorder the heap */ \
size_t i = 0; \
@@ -323,8 +351,7 @@
return i; \
} \
\
M_INLINE bool \
M_F(name, _erase)(prioqueue_t p, type const x) \
M_P(bool, name, _erase, prioqueue_t p, type const x) \
{ \
/* First pass: search for an item EQUAL to x */ \
size_t size = M_F(name, _array_size)(p->array); \
@@ -335,7 +362,7 @@
/* Swap the found item and the last element */ \
size--; \
M_F(name, _array_swap_at) (p->array, i, size); \
M_F(name, _array_pop_back)(NULL, p->array); \
M_F(name, _array_pop_back) M_R(NULL, p->array); \
/* Move back the last swapped element to its right position in the heap */ \
while (true) { \
size_t child = M_F(name, _i_lchild)(i); \
@@ -352,8 +379,7 @@
return true; \
} \
\
M_INLINE void \
M_F(name, _update)(prioqueue_t p, type const xold, type const xnew) \
M_P(void, name, _update, prioqueue_t p, type const xold, type const xnew) \
{ \
/* NOTE: xold can be the same pointer than xnew */ \
/* First pass: search for an item EQUAL to x */ \
@@ -364,7 +390,7 @@
/* Test if the position of the old data is further or nearer than the new */ \
int cmp = M_CALL_CMP(oplist, *M_F(name, _array_cget)(p->array, i), xnew); \
/* Set the found item to the new element */ \
M_F(name, _array_set_at) (p->array, i, xnew); \
M_F(name, _array_set_at) M_R(p->array, i, xnew); \
if (cmp < 0) { \
/* Move back the updated element to its new position, further in the heap */ \
while (true) { \
@@ -467,42 +493,37 @@
,/* No OUT_STR */) \
\
M_IF_METHOD(IN_STR, oplist)( \
M_INLINE bool \
M_F(name, _in_str)(prioqueue_t p, FILE *file) \
M_P(bool, name, _in_str, prioqueue_t p, FILE *file) \
{ \
return M_F(name, _array_in_str)(p->array, file); \
return M_F(name, _array_in_str)M_R(p->array, file); \
} \
,/* No IN_STR */) \
\
M_IF_METHOD(GET_STR, oplist)( \
M_INLINE void \
M_F(name, _get_str)(string_t str, const prioqueue_t p, bool append) \
M_P(void, name, _get_str, m_string_t str, const prioqueue_t p, bool append) \
{ \
M_F(name, _array_get_str)(str, p->array, append); \
M_F(name, _array_get_str)M_R(str, p->array, append); \
} \
,/* No GET_STR */) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
M_INLINE bool \
M_F(name, _parse_str)(prioqueue_t p, const char str[], const char **endp) \
M_P(bool, name, _parse_str, prioqueue_t p, const char str[], const char **endp) \
{ \
return M_F(name, _array_parse_str)(p->array, str, endp); \
return M_F(name, _array_parse_str)M_R(p->array, str, endp); \
} \
,/* No PARSE_STR */) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, const prioqueue_t p) \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const prioqueue_t p) \
{ \
return M_F(name, _array_out_serial)(f, p->array); \
return M_F(name, _array_out_serial) M_R(f, p->array); \
} \
,/* No OUT_SERIAL */) \
\
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(prioqueue_t p, m_serial_read_t f) \
M_P(m_serial_return_code_t, name, _in_serial, prioqueue_t p, m_serial_read_t f) \
{ \
return M_F(name, _array_in_serial)(p->array, f); \
return M_F(name, _array_in_serial)M_R(p->array, f); \
} \
,/* No in_SERIAL */) \
+323
View File
@@ -0,0 +1,323 @@
/*
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface (Not Thread safe)
*
* 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_QUEUE_H
#define MSTARLIB_QUEUE_H
#include "m-core.h"
/* Define a fixed size queue.
If size is 0, then the size will only be defined at run-time when initializing the queue,
otherwise the size will be a compile time constant embedded in the structure.
USAGE: QUEUE_DEF(name, type, size_of_QUEUE_or_0[, oplist]) */
#define M_QUEUE_DEF(name, ... ) M_QUEUE_DEF_AS(name, M_F(name, _t), __VA_ARGS__)
/* Define a fixed size queue
as the provided type name_t.
USAGE: QUEUE_DEF_AS(name, name_t, type, size_of_QUEUE_or_0[, oplist of type]) */
#define M_QUEUE_DEF_AS(name, name_t, type, ... ) \
M_BEGIN_PROTECTED_CODE \
M_QUEU3_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t, 0), \
(name, type, __VA_ARGS__, name_t, 0))) \
M_END_PROTECTED_CODE
/* Define a fixed size stack.
If size is 0, then the size will only be defined at run-time when initializing the stack,
otherwise the size will be a compile time constant embedded in the structure.
USAGE: STACK_DEF(name, type, size_of_stack_or_0[, oplist]) */
#define M_STACK_DEF(name, ... ) M_STACK_DEF_AS(name, M_F(name, _t), __VA_ARGS__)
/* Define a fixed size stack
as the provided type name_t.
USAGE: QUEUE_DEF_AS(name, name_t, type, size_of_stack_or_0[, oplist of type]) */
#define M_STACK_DEF_AS(name, name_t, type, ... ) \
M_BEGIN_PROTECTED_CODE \
M_QUEU3_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t, 1), \
(name, type, __VA_ARGS__, name_t, 1))) \
M_END_PROTECTED_CODE
/* Define the oplist of a fixed size queue given its name and its oplist.
USAGE: QUEUE_OPLIST(name[, oplist of the type]) */
#define M_QUEUE_OPLIST(...) \
M_QUEU3_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_BASIC_OPLIST), \
(__VA_ARGS__ )))
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define M_QUEU3_OPLIST_P1(arg) M_QUEU3_OPLIST_P2 arg
/* Validation of the given oplist */
#define M_QUEU3_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_QUEU3_OPLIST_P3, M_QUEU3_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define M_QUEU3_OPLIST_FAILURE(name, oplist) \
((M_LIB_ERROR(ARGUMENT_OF_BUFFER_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* OPLIST definition of a buffer */
#define M_QUEU3_OPLIST_P3(name, oplist) \
(INIT(API_0P(M_F(name, _i_init))) \
,INIT_SET(API_0P(M_F(name, _init_set))) \
,SET(API_0P(M_F(name, _set))) \
,CLEAR(API_0P(M_F(name, _clear))) \
,NAME(name) \
,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \
,SUBTYPE(M_F(name, _subtype_ct)) \
,RESET(API_0P(M_F(name,_reset))) \
,PUSH(API_0P(M_F(name,_push))) \
,POP(API_0P(M_F(name,_pop))) \
,OPLIST(oplist) \
,EMPTY_P(M_F(name, _empty_p)), \
,FULL_P(M_F(name, _full_p)), \
,GET_SIZE(M_F(name, _size)) \
)
/* Test if the size is only run-time or build time */
#define M_QUEU3_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size))
/* Return the size (run time or build time).
NOTE: It assumed that the queue variable name is 'v' */
#define M_QUEU3_SIZE(v, m_size) \
M_QUEU3_IF_CTE_SIZE(m_size) (m_size, v->capacity)
/* Contract of a queue. */
#define M_QUEU3_CONTRACT(queue, size) do { \
M_ASSERT (queue != NULL); \
M_ASSERT (queue->data != NULL); \
}while (0)
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define M_QUEU3_DEF_P1(arg) M_ID( M_QUEU3_DEF_P2 arg )
/* Validate the value oplist before going further */
#define M_QUEU3_DEF_P2(name, type, m_size, oplist, queue_t, stack_mode) \
M_IF_OPLIST(oplist)(M_QUEU3_DEF_P3, M_QUEU3_DEF_FAILURE)(name, type, m_size, oplist, queue_t, stack_mode)
/* Stop processing with a compilation failure */
#define M_QUEU3_DEF_FAILURE(name, type, m_size, oplist, queue_t, stack_mode) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(M_QUEUE_DEF/M_STACK_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
/* Define the queue type using mutex lock and its functions.
- name: main prefix of the container
- type: type of an element of the queue
- m_size: constant to 0 if variable runtime size, or else the fixed size of the queue
- oplist: the oplist of the type of an element of the queue
- queue_t: name of the queue
*/
#define M_QUEU3_DEF_P3(name, type, m_size, oplist, queue_t, stack_mode) \
M_QUEU3_DEF_TYPE(name, type, m_size, oplist, queue_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_QUEU3_DEF_CORE(name, type, m_size, oplist, queue_t, stack_mode) \
M_EMPLACE_QUEUE_DEF(name, queue_t, _emplace, oplist, M_EMPLACE_QUEUE_GENE)
/* Define the type of a queue */
#define M_QUEU3_DEF_TYPE(name, type, m_size, oplist, queue_t) \
\
typedef struct M_F(name, _s) { \
unsigned idx_prod; /* Index of the production threads */ \
M_QUEU3_IF_CTE_SIZE(m_size)( ,unsigned capacity;) /* Capacity of the queue */ \
unsigned idx_cons; /* Index of the consumption threads */ \
unsigned number; /* Number of elements in the queue */ \
/* If fixed size, array of elements, otherwise pointer to element */ \
type M_QUEU3_IF_CTE_SIZE(m_size)(data[m_size], *data); \
} queue_t[1]; \
\
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
/* Internal types used by the oplist */ \
typedef type M_F(name, _subtype_ct); \
typedef queue_t M_F(name, _ct); \
/* Define the core functionalities of a queue */
#define M_QUEU3_DEF_CORE(name, type, m_size, oplist, queue_t, stack_mode) \
\
M_P(void, name, _init, queue_t v, size_t size) \
{ \
M_ASSERT(size <= UINT_MAX); \
M_UNUSED_CONTEXT(); \
M_QUEU3_IF_CTE_SIZE(m_size)(M_ASSERT(size == m_size), v->capacity = (unsigned) size); \
v->idx_prod = v->idx_cons = v->number = 0; \
\
M_QUEU3_IF_CTE_SIZE(m_size)( /* Statically allocated */ , \
v->data = M_CALL_REALLOC(oplist, type, NULL, 0, M_QUEU3_SIZE(v, m_size)); \
if (M_UNLIKELY_NOMEM (v->data == NULL)) { \
M_MEMORY_FULL (type, M_QUEU3_SIZE(v, m_size)); \
} \
) \
for(size_t i = 0; i < size; i++) { \
M_CALL_INIT(oplist, v->data[i]); \
} \
M_QUEU3_CONTRACT(v,m_size); \
} \
\
M_QUEU3_IF_CTE_SIZE(m_size)( \
M_P(void, name,_i_init, queue_t v) \
{ \
M_F(name, _init)M_R(v, m_size); \
} \
, ) \
\
M_P(void, name, _i_clear_obj, queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
M_UNUSED_CONTEXT(); \
for(size_t i = 0; i < M_QUEU3_SIZE(v, m_size); i++) { \
M_CALL_CLEAR(oplist, v->data[i]); \
} \
v->idx_prod = v->idx_cons = v->number = 0; \
M_QUEU3_CONTRACT(v,m_size); \
} \
\
M_P(void, name, _clear, queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
M_UNUSED_CONTEXT(); \
M_F(name,_i_clear_obj)M_R(v); \
M_QUEU3_IF_CTE_SIZE(m_size)( , \
M_CALL_FREE(oplist, type, v->data, M_QUEU3_SIZE(v, m_size)); \
v->data = NULL; \
) \
} \
\
M_N(void, name, _reset, queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
v->idx_prod = v->idx_cons = v->number = 0; \
M_QUEU3_CONTRACT(v,m_size); \
} \
\
M_P(void, name, _init_set, queue_t dest, const queue_t src) \
{ \
M_ASSERT (dest != src); \
M_QUEU3_CONTRACT(src,m_size); \
M_F(name, _init) M_R(dest, M_QUEU3_SIZE(src, m_size)); \
for(size_t i = 0; i < M_QUEU3_SIZE(src, m_size); i++) { \
M_CALL_SET(oplist, dest->data[i], src->data[i]); \
} \
dest->idx_prod = src->idx_prod; \
dest->idx_cons = src->idx_cons; \
dest->number = src->number; \
M_QUEU3_CONTRACT(dest, m_size); \
} \
\
M_P(void, name, _set, queue_t dest, const queue_t src) \
{ \
M_F(name, _clear) M_R(dest); \
M_F(name, _init_set) M_R(dest, src); \
} \
\
M_INLINE bool \
M_F(name, _empty_p)(const queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
return v->number == 0; \
} \
\
M_INLINE bool \
M_F(name, _full_p)(const queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
return v->number == M_QUEU3_SIZE(v, m_size); \
} \
\
M_INLINE size_t \
M_F(name, _size)(const queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
return v->number; \
} \
\
M_P(bool, name, _push, queue_t v, type const data) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
M_UNUSED_CONTEXT(); \
if (M_F(name, _full_p)(v)) \
return false; \
\
unsigned idx = v->idx_prod; \
M_CALL_SET(oplist, v->data[idx], data); \
idx++; \
M_IF(stack_mode)(, idx = (idx == M_QUEU3_SIZE(v, m_size)) ? 0 : idx); \
v->idx_prod = idx; \
\
v->number++; \
\
M_QUEU3_CONTRACT(v,m_size); \
return true; \
} \
\
M_P(bool, name, _pop, type *data, queue_t v) \
{ \
M_QUEU3_CONTRACT(v,m_size); \
M_ASSERT (data != NULL); \
M_UNUSED_CONTEXT(); \
if (M_F(name, _empty_p)(v)) \
return false; \
\
/* POP data from the queue and update INDEX */ \
M_IF(stack_mode)( \
/* STACK queue */ \
v->idx_prod --; \
M_CALL_SET(oplist, *data, v->data[v->idx_prod]); \
, \
/* FIFO queue */ \
M_CALL_SET(oplist, *data, v->data[v->idx_cons]); \
v->idx_cons = (v->idx_cons == M_QUEU3_SIZE(v, m_size)-1) ? 0 : (v->idx_cons + 1); \
) \
v->number--; \
\
M_QUEU3_CONTRACT(v,m_size); \
return true; \
} \
\
M_INLINE size_t \
M_F(name, _capacity)(const queue_t v) \
{ \
(void) v; /* may be unused */ \
return M_QUEU3_SIZE(v, m_size); \
} \
#if M_USE_SMALL_NAME
#define QUEUE_DEF M_QUEUE_DEF
#define QUEUE_DEF_AS M_QUEUE_DEF_AS
#define STACK_DEF M_STACK_DEF
#define STACK_DEF_AS M_STACK_DEF_AS
#define QUEUE_OPLIST M_QUEUE_OPLIST
#endif
#endif
+91 -95
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - RED BLACK TREE module
*
* Copyright (c) 2017-2024, 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:
@@ -71,6 +71,7 @@
/* OPLIST definition of a rbtree
NOTE: IT_REF is not exported so that the container appears as not modifiable
by algorithm.*/
#ifndef M_USE_CONTEXT
#define M_RBTR33_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)), \
INIT_SET(M_F(name, _init_set)), \
@@ -110,7 +111,46 @@
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
)
#else
#define M_RBTR33_OPLIST_P3(name, oplist) \
(INIT(M_F(name, _init)), \
INIT_SET(API_0P(M_F(name, _init_set))), \
INIT_WITH(API_1(M_INIT_EMPLACE_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)), \
GET_SIZE(M_F(name, _size)), \
IT_TYPE(M_F(name, _it_ct)), \
IT_FIRST(M_F(name,_it)), \
IT_SET(M_F(name,_it_set)), \
IT_LAST(M_F(name,_it_last)), \
IT_END(M_F(name,_it_end)), \
IT_END_P(M_F(name,_end_p)), \
IT_LAST_P(M_F(name,_last_p)), \
IT_EQUAL_P(M_F(name,_it_equal_p)), \
IT_NEXT(M_F(name,_next)), \
IT_PREVIOUS(M_F(name,_previous)), \
IT_CREF(M_F(name,_cref)), \
IT_REMOVE(M_F(name,_remove)), \
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)(GET_STR(API_0P(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
/********************************** INTERNAL *********************************/
@@ -176,11 +216,10 @@ typedef enum {
*/
#define M_RBTR33_DEF_P3(name, type, oplist, tree_t, node_t, it_t) \
M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \
M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \
M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, tree_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
M_EMPLACE_QUEUE_DEF(name, tree_t, _emplace, oplist, M_EMPLACE_QUEUE_GENE)
/* Define the types associated to a R/B Tree */
#define M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \
@@ -215,33 +254,6 @@ typedef enum {
typedef tree_t M_F(name, _ct); \
typedef it_t M_F(name, _it_ct); \
/* Define the mempool encapsulation */
#define M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \
/* Link with fast memory allocator if requested */ \
M_IF_METHOD(MEMPOOL, oplist)( \
/* Definition of the memory pool of this kind of node */ \
MEMPOOL_DEF(M_F(name, _mempool), node_t) \
/* Definition of the global variable used to reference this pool */ \
M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \
/* Allocator function */ \
M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \
return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \
} \
/* Deallocator function */ \
M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
M_F(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \
} \
\
, /* No mempool allocation */ \
/* Classic Allocator function (common case) */ \
M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \
return M_CALL_NEW(oplist, node_t); \
} \
/* Classic deallocator function (common case) */ \
M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
M_CALL_DEL(oplist, ptr); \
} ) \
/* Define the core functions */
#define M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \
\
@@ -254,8 +266,7 @@ typedef enum {
M_RBTR33_CONTRACT(tree); \
} \
\
M_INLINE void \
M_F(name, _reset)(tree_t tree) \
M_P(void, name, _reset, tree_t tree) \
{ \
M_RBTR33_CONTRACT(tree); \
node_t *stack[M_RBTR33_MAX_STACK]; \
@@ -289,7 +300,7 @@ typedef enum {
M_ASSERT (n == stack[cpt - 1]); \
/* Clear the bottom left node */ \
M_CALL_CLEAR(oplist, n->data); \
M_C3(m_rbtr33_,name,_del) (n); \
M_CALL_DEL(oplist, n); \
M_ASSERT((stack[cpt-1] = NULL) == NULL); \
/* Go up to the parent */ \
cpt--; \
@@ -299,15 +310,13 @@ typedef enum {
tree->size = 0; \
} \
\
M_INLINE void \
M_F(name, _clear)(tree_t tree) \
M_P(void, name, _clear, tree_t tree) \
{ \
/* Nothing more than clean the tree as everything is cleared */ \
M_F(name, _reset)(tree); \
M_F(name, _reset) M_R(tree); \
} \
\
M_INLINE void \
M_F(name, _push)(tree_t tree, type const data) \
M_P(void, name, _push, tree_t tree, type const data) \
{ \
M_RBTR33_CONTRACT(tree); \
node_t *tab[M_RBTR33_MAX_STACK]; \
@@ -316,10 +325,9 @@ typedef enum {
node_t *n = tree->node; \
/* If there is no root node, create a new node */ \
if (n == NULL) { \
n = M_C3(m_rbtr33_,name,_new)(); \
n = M_CALL_NEW(oplist, node_t); \
if (M_UNLIKELY_NOMEM (n == NULL)) { \
M_MEMORY_FULL(sizeof (node_t)); \
return; \
M_MEMORY_FULL(node_t, 1); \
} \
/* Copy the data in the root node */ \
M_CALL_INIT_SET(oplist, n->data, data); \
@@ -357,10 +365,9 @@ typedef enum {
return; \
} \
/* Create new node to store the data */ \
n = M_C3(m_rbtr33_,name,_new)(); \
n = M_CALL_NEW(oplist, node_t); \
if (M_UNLIKELY_NOMEM (n == NULL) ) { \
M_MEMORY_FULL (sizeof (node_t)); \
return; \
M_MEMORY_FULL (node_t, 1); \
} \
/* Copy the data and mark the node as red */ \
M_CALL_INIT_SET(oplist, n->data, data); \
@@ -684,41 +691,37 @@ typedef enum {
} \
\
/* Create a copy of the given node (recursively) */ \
M_INLINE node_t * \
M_C3(m_rbtr33_,name,_copy_node)(const node_t *o) \
M_P(node_t *, name, _i_copy_node, const node_t *o) \
{ \
if (o == NULL) return NULL; \
node_t *n = M_C3(m_rbtr33_,name,_new)(); \
if (M_UNLIKELY(o == NULL)) return NULL; \
node_t *n = M_CALL_NEW(oplist, node_t); \
if (M_UNLIKELY_NOMEM (n == NULL) ) { \
M_MEMORY_FULL (sizeof (node_t)); \
return NULL; \
M_MEMORY_FULL (node_t, 1); \
} \
M_CALL_INIT_SET(oplist, n->data, o->data); \
n->child[0] = M_C3(m_rbtr33_,name,_copy_node)(o->child[0]); \
n->child[1] = M_C3(m_rbtr33_,name,_copy_node)(o->child[1]); \
n->child[0] = M_F(name,_i_copy_node)M_R(o->child[0]); \
n->child[1] = M_F(name,_i_copy_node)M_R(o->child[1]); \
M_RBTR33_COPY_COLOR (n, o); \
return n; \
} \
\
M_INLINE void \
M_F(name, _init_set)(tree_t tree, const tree_t ref) \
M_P(void, name, _init_set, tree_t tree, const tree_t ref) \
{ \
M_RBTR33_CONTRACT (ref); \
M_ASSERT (tree != NULL && tree != ref); \
tree->size = ref->size; \
/* Copy the root node recursively */ \
tree->node = M_C3(m_rbtr33_,name,_copy_node)(ref->node); \
tree->node = M_F(name,_i_copy_node)M_R(ref->node); \
M_RBTR33_CONTRACT (tree); \
} \
\
M_INLINE void \
M_F(name, _set)(tree_t tree, const tree_t ref) \
M_P(void, name, _set, tree_t tree, const tree_t ref) \
{ \
M_RBTR33_CONTRACT (tree); \
M_RBTR33_CONTRACT (ref); \
if (tree == ref) return; \
M_F(name,_clear)(tree); \
M_F(name,_init_set)(tree, ref); \
M_F(name,_clear)M_R(tree); \
M_F(name,_init_set)M_R(tree, ref); \
} \
\
M_INLINE void \
@@ -728,18 +731,18 @@ typedef enum {
M_ASSERT (tree != NULL && tree != ref); \
tree->size = ref->size; \
tree->node = ref->node; \
/* Mark ref as an invalid representation */ \
ref->node = NULL; \
ref->size = 0; \
ref->size = 1; \
M_RBTR33_CONTRACT (tree); \
} \
\
M_INLINE void \
M_F(name, _move)(tree_t tree, tree_t ref) \
M_P(void, name, _move, tree_t tree, tree_t ref) \
{ \
M_RBTR33_CONTRACT (tree); \
M_RBTR33_CONTRACT (ref); \
M_ASSERT (tree != ref); \
M_F(name,_clear)(tree); \
M_F(name,_clear)M_R(tree); \
M_F(name,_init_move)(tree, ref); \
M_RBTR33_CONTRACT (tree); \
} \
@@ -801,8 +804,7 @@ typedef enum {
} \
) \
\
M_INLINE bool \
M_F(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \
M_P(bool, name, _pop_at, type *data_ptr, tree_t tree, type const key) \
{ \
M_RBTR33_CONTRACT (tree); \
node_t *tab[M_RBTR33_MAX_STACK]; \
@@ -955,24 +957,22 @@ typedef enum {
M_DO_MOVE(oplist, *data_ptr, n->data); \
else \
M_CALL_CLEAR(oplist, n->data); \
M_C3(m_rbtr33_,name,_del) (n); \
M_CALL_DEL(oplist, n); \
tree->size --; \
M_RBTR33_CONTRACT (tree); \
return true; \
} \
\
M_INLINE void M_F(name,_remove)(tree_t t, it_t it) \
M_P(void, name,_remove, tree_t t, it_t it) \
{ \
/* Not optimum: another search in the tree is performed */ \
type data; \
M_CALL_INIT_SET(oplist, data, *M_F(name,_cref)(it)); \
type const *data = M_F(name,_cref)(it); \
M_F(name,_next)(it); \
M_F(name, _pop_at)(NULL, t, data); \
/* We have changed the tree: the iterator is partialy invalid */ \
M_F(name, _pop_at)M_R(NULL, t, *data); \
/* We have changed the tree: the iterator is partially invalid */ \
if (!M_F(name, _end_p)(it)) { \
M_F(name, _it_from)(it, t, *M_F(name,_cref)(it)); \
} \
M_CALL_CLEAR(oplist, data); \
} \
\
M_IF_METHOD(EQUAL, oplist)( \
@@ -1022,24 +1022,24 @@ typedef enum {
/* Define the I/O functions */
#define M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \
M_IF_METHOD(GET_STR, oplist)( \
M_INLINE void M_F(name, _get_str)(m_string_t str, \
tree_t const t1, bool append) { \
M_P(void, name, _get_str, m_string_t str, tree_t const t1, bool append) \
{ \
M_RBTR33_CONTRACT(t1); \
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, "["); \
/* NOTE: The print is really naive, and not really efficient */ \
bool commaToPrint = false; \
it_t it1; \
M_F(name, _it)(it1, t1); \
while (!M_F(name, _end_p)(it1)) { \
if (commaToPrint) \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
commaToPrint = true; \
type const *ref1 = M_F(name, _cref)(it1); \
M_CALL_GET_STR(oplist, str, *ref1, true); \
M_F(name, _next)(it1); \
} \
m_string_push_back (str, ']'); \
m_string_push_back M_R(str, ']'); \
} \
, /* NO GET_STR */ ) \
\
@@ -1066,12 +1066,11 @@ typedef enum {
, /* no out_str */ ) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
M_INLINE bool \
M_F(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \
M_P(bool, name, _parse_str, tree_t rbtree, const char str[], const char **endp) \
{ \
M_RBTR33_CONTRACT(rbtree); \
M_ASSERT (str != NULL); \
M_F(name,_reset)(rbtree); \
M_F(name,_reset)M_R(rbtree); \
int c = m_core_str_nospace(&str); \
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
c = m_core_str_nospace(&str); \
@@ -1082,7 +1081,7 @@ typedef enum {
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
c = m_core_str_nospace(&str); \
if (b == false || c == 0) { c= 0 ; break; }; \
M_F(name, _push)(rbtree, item); \
M_F(name, _push)M_R(rbtree, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
exit: \
@@ -1092,12 +1091,11 @@ typedef enum {
, /* no parse_str */ ) \
\
M_IF_METHOD(IN_STR, oplist)( \
M_INLINE bool \
M_F(name, _in_str)(tree_t rbtree, FILE *file) \
M_P(bool, name, _in_str, tree_t rbtree, FILE *file) \
{ \
M_RBTR33_CONTRACT(rbtree); \
M_ASSERT (file != NULL); \
M_F(name,_reset)(rbtree); \
M_F(name,_reset)M_R(rbtree); \
int c = fgetc(file); \
if (M_UNLIKELY (c != '[')) return false; \
c = fgetc(file); \
@@ -1109,7 +1107,7 @@ typedef enum {
bool b = M_CALL_IN_STR(oplist, item, file); \
c = m_core_fgetc_nospace(file); \
if (b == false || c == EOF) { c = 0; break; } \
M_F(name, _push)(rbtree, item); \
M_F(name, _push)M_R(rbtree, item); \
} while (c == M_GET_SEPARATOR oplist); \
} \
return c == ']'; \
@@ -1117,8 +1115,7 @@ typedef enum {
, /* no in_str */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
M_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_RBTR33_CONTRACT(t1); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
@@ -1127,38 +1124,37 @@ typedef enum {
M_F(name, _subtype_ct) const *item; \
bool first_done = false; \
it_t it; \
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(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(IN_SERIAL, oplist)( \
M_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_RBTR33_CONTRACT(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; \
M_F(name,_reset)(t1); \
M_F(name,_reset)M_R(t1); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_QLET(1, key, type, oplist) { \
do { \
ret = M_CALL_IN_SERIAL(oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_F(name, _push)(t1, key); \
M_F(name, _push)M_R(t1, key); \
ret = f->m_interface->read_array_next(local, f); \
} while (ret == M_SERIAL_OK_CONTINUE); \
} \
+30 -30
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Serial BIN
*
* Copyright (c) 2017-2024, 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:
@@ -111,9 +111,9 @@ m_ser1al_bin_read_size(FILE *f, size_t *size)
/* Write the boolean 'data' into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_boolean(m_serial_write_t serial, const bool data)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_boolean, m_serial_write_t serial, const bool data)
{
M_UNUSED_CONTEXT();
FILE *f = (FILE *)serial->data[0].p;
size_t n = fwrite (M_ASSIGN_CAST(const void*, &data), sizeof (bool), 1, f);
return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail();
@@ -121,9 +121,9 @@ m_ser1al_bin_write_boolean(m_serial_write_t serial, const bool data)
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_integer, m_serial_write_t serial,const long long data, const size_t size_of_type)
{
M_UNUSED_CONTEXT();
size_t n;
FILE *f = (FILE *)serial->data[0].p;
@@ -146,9 +146,9 @@ m_ser1al_bin_write_integer(m_serial_write_t serial,const long long data, const s
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_float, m_serial_write_t serial, const long double data, const size_t size_of_type)
{
M_UNUSED_CONTEXT();
size_t n;
FILE *f = (FILE *)serial->data[0].p;
@@ -168,9 +168,9 @@ m_ser1al_bin_write_float(m_serial_write_t serial, const long double data, const
/* Write the null-terminated string 'data'into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_string(m_serial_write_t serial, const char data[], size_t length)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_string, m_serial_write_t serial, const char data[], size_t length)
{
M_UNUSED_CONTEXT();
M_ASSERT_SLOW(length == strlen(data) );
FILE *f = (FILE *)serial->data[0].p;
M_ASSERT(f != NULL && data != NULL);
@@ -188,9 +188,9 @@ m_ser1al_bin_write_string(m_serial_write_t serial, const char data[], size_t len
Initialize 'local' so that it can be used to serialize the array
(local is an unique serialization object of the array).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_array_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
M_UNUSED_CONTEXT();
(void) local; //Unused
if (number_of_elements == (size_t)-1) return M_SERIAL_FAIL_RETRY;
FILE *f = (FILE *)serial->data[0].p;
@@ -200,9 +200,9 @@ m_ser1al_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial,
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_array_next, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // Unused
(void) serial; // Unused
return M_SERIAL_OK_CONTINUE;
@@ -210,9 +210,9 @@ m_ser1al_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial)
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_array_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // Unused
(void) serial; // Unused
return M_SERIAL_OK_CONTINUE;
@@ -220,9 +220,9 @@ m_ser1al_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial)
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_map_value, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_CONTINUE;
@@ -232,9 +232,9 @@ m_ser1al_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
Initialize 'local' so that it can serial the tuple
(local is an unique serialization object of the tuple).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_tuple_start, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_CONTINUE;
@@ -242,9 +242,9 @@ m_ser1al_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_tuple_id, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) serial;
(void) field_name;
@@ -255,9 +255,9 @@ m_ser1al_bin_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, con
/* End the write of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_tuple_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_DONE;
@@ -268,9 +268,9 @@ m_ser1al_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise
Otherwise, the field 'field_name[index]' will be filled.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_variant_start, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
M_UNUSED_CONTEXT();
(void) field_name;
(void) max;
(void) local;
@@ -281,9 +281,9 @@ m_ser1al_bin_write_variant_start(m_serial_local_t local, m_serial_write_t serial
/* End Writing a variant into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_bin, _write_variant_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) serial;
return M_SERIAL_OK_DONE;
@@ -399,15 +399,15 @@ m_ser1al_bin_read_float(m_serial_read_t serial, long double *r, const size_t siz
/* Read from the stream 'serial' a string.
Set 's' with the string if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_string(m_serial_read_t serial, struct string_s *s){
M_P(m_serial_return_code_t, m_ser1al_bin, _read_string, m_serial_read_t serial, struct m_string_s *s)
{
FILE *f = (FILE*) serial->data[0].p;
M_ASSERT(f != NULL && s != NULL);
// First read the number of non null characters
size_t length;
if (m_ser1al_bin_read_size(f, &length) != true) return m_core_serial_fail();
// Use of internal string interface to dimension the string
char *p = m_str1ng_fit2size(s, length + 1);
char *p = m_str1ng_fit2size M_R(s, length + 1);
m_str1ng_set_size(s, length);
// Read the characters excluding the final null one.
// NOTE: fread supports length == 0.
+83 -94
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Serial JSON
*
* Copyright (c) 2017-2024, 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:
@@ -36,9 +36,9 @@ M_BEGIN_PROTECTED_CODE
/* Write the boolean 'data' into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_boolean(m_serial_write_t serial, const bool data)
M_P(m_serial_return_code_t, m_ser1al_json, _write_boolean, m_serial_write_t serial, const bool data)
{
M_UNUSED_CONTEXT();
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%s", data ? "true" : "false");
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
@@ -46,9 +46,9 @@ m_ser1al_json_write_boolean(m_serial_write_t serial, const bool data)
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
M_P(m_serial_return_code_t, m_ser1al_json, _write_integer, m_serial_write_t serial,const long long data, const size_t size_of_type)
{
M_UNUSED_CONTEXT();
(void) size_of_type; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%lld", data);
@@ -57,9 +57,9 @@ m_ser1al_json_write_integer(m_serial_write_t serial,const long long data, const
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
M_P(m_serial_return_code_t, m_ser1al_json, _write_float, m_serial_write_t serial, const long double data, const size_t size_of_type)
{
M_UNUSED_CONTEXT();
(void) size_of_type; // Ignored
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "%Lf", data);
@@ -68,18 +68,20 @@ m_ser1al_json_write_float(m_serial_write_t serial, const long double data, const
/* Write the null-terminated string 'data'into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t length)
M_P(m_serial_return_code_t, m_ser1al_json, _write_string, m_serial_write_t serial, const char data[], size_t length)
{
M_UNUSED_CONTEXT();
M_ASSERT(length == (m_str1ng_size_t) length);
M_ASSERT_SLOW(length == strlen(data) );
FILE *f = (FILE *)serial->data[0].p;
M_ASSERT(f != NULL && data != NULL);
/* HACK: Build dummy string to reuse m_string_out_str */
m_string_t v2;
uintptr_t ptr = (uintptr_t) data;
v2->u.heap.size = length;
v2->u.heap.alloc = length + 1;
v2->ptr = (char*)ptr;
v2->u.heap.size = (m_str1ng_size_t) length;
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-2] = 1;
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-1] = 31;
v2->u.heap.ptr = (char*)ptr;
m_string_out_str(f, v2);
return M_UNLIKELY(ferror(f)) ? m_core_serial_fail() : M_SERIAL_OK_DONE;
}
@@ -90,9 +92,9 @@ m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t le
Initialize 'local' so that it can be used to serialize the array
(local is an unique serialization object of the array).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
M_P(m_serial_return_code_t, m_ser1al_json, _write_array_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) number_of_elements; // Ignored
FILE *f = (FILE *)serial->data[0].p;
@@ -102,9 +104,9 @@ m_ser1al_json_write_array_start(m_serial_local_t local, m_serial_write_t serial,
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_array_next, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, ",");
@@ -113,9 +115,9 @@ m_ser1al_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_array_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "]");
@@ -128,9 +130,9 @@ m_ser1al_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
Initialize 'local' so that it can be used to serialize the map
(local is an unique serialization object of the map).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) number_of_elements; // Ignored
FILE *f = (FILE *)serial->data[0].p;
@@ -140,9 +142,9 @@ m_ser1al_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, c
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_value, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, ":");
@@ -151,9 +153,9 @@ m_ser1al_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
/* Write a map separator between elements of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_next, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, ",");
@@ -162,9 +164,9 @@ m_ser1al_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
/* End the writing of a map into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "}");
@@ -175,9 +177,9 @@ m_ser1al_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
Initialize 'local' so that it can serial the tuple
(local is an unique serialization object of the tuple).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_tuple_start, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "{");
@@ -186,9 +188,9 @@ m_ser1al_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
M_P(m_serial_return_code_t, m_ser1al_json, _write_tuple_id, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) max; // Ignored
FILE *f = (FILE *)serial->data[0].p;
@@ -198,9 +200,9 @@ m_ser1al_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, co
/* End the write of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_tuple_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "}");
@@ -212,9 +214,9 @@ m_ser1al_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise
Otherwise, the field 'field_name[index]' will be filled.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
M_P(m_serial_return_code_t, m_ser1al_json, _write_variant_start, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
(void) max; // argument not used
FILE *f = (FILE *)serial->data[0].p;
@@ -231,9 +233,9 @@ m_ser1al_json_write_variant_start(m_serial_local_t local, m_serial_write_t seria
/* End Writing a variant into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_json, _write_variant_end, m_serial_local_t local, m_serial_write_t serial)
{
M_UNUSED_CONTEXT();
(void) local; // argument not used
FILE *f = (FILE *)serial->data[0].p;
int n = fprintf(f, "}");
@@ -342,12 +344,12 @@ m_ser1al_json_read_float(m_serial_read_t serial, long double *r, const size_t si
/* Read from the stream 'serial' a string.
Set 's' with the string if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_json_read_string(m_serial_read_t serial, struct m_string_s *s){
M_P(m_serial_return_code_t, m_ser1al_json, _read_string, m_serial_read_t serial, struct m_string_s *s)
{
FILE *f = (FILE*) serial->data[0].p;
int n = m_core_fscanf(f, " "); // Skip any leading spaces.
(void) n; // Unused return value.
return m_string_in_str(s, f) ? M_SERIAL_OK_DONE : m_core_serial_fail();
return m_string_in_str M_R(s, f) ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
/* Start reading from the stream 'serial' an array.
@@ -585,51 +587,50 @@ typedef m_serial_read_t m_serial_json_read_t;
/* Write the boolean 'data' into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_boolean(m_serial_write_t serial, const bool data)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_boolean, m_serial_write_t serial, const bool data)
{
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
int n = m_string_cat_printf(f, "%s", data ? "true" : "false");
//TODO: Simplify code.
int n = m_string_cat_printf M_R(f, "%s", data ? "true" : "false");
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_integer, m_serial_write_t serial, const long long data, const size_t size_of_type)
{
(void) size_of_type; // Ignored
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
int n = m_string_cat_printf(f, "%lld", data);
int n = m_string_cat_printf M_R(f, "%lld", data);
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_float, m_serial_write_t serial, const long double data, const size_t size_of_type)
{
(void) size_of_type; // Ignored
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
int n = m_string_cat_printf(f, "%Lf", data);
int n = m_string_cat_printf M_R(f, "%Lf", data);
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
/* Write the null-terminated string 'data'into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_string(m_serial_write_t serial, const char data[], size_t length)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_string, m_serial_write_t serial, const char data[], size_t length)
{
M_ASSERT(length == (m_str1ng_size_t) length);
M_ASSERT_SLOW(length == strlen(data) );
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
M_ASSERT(f != NULL && data != NULL);
/* HACK: Build dummy string to reuse m_string_get_str */
m_string_t v2;
uintptr_t ptr = (uintptr_t) data;
v2->u.heap.size = length;
v2->u.heap.alloc = length + 1;
v2->ptr = (char*)ptr;
m_string_get_str(f, v2, true);
v2->u.heap.size = (m_str1ng_size_t) length;
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-2] = 1;
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-1] = 31;
v2->u.heap.ptr = (char*)ptr;
m_string_get_str M_R(f, v2, true);
return M_SERIAL_OK_DONE;
}
@@ -639,35 +640,32 @@ m_ser1al_str_json_write_string(m_serial_write_t serial, const char data[], size_
Initialize 'local' so that it can be used to serialize the array
(local is an unique serialization object of the array).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_array_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
(void) local; // argument not used
(void) number_of_elements; // Ignored
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, '[');
m_string_push_back M_R(f, '[');
return M_SERIAL_OK_CONTINUE;
}
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_array_next, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, ',');
m_string_push_back M_R(f, ',');
return M_SERIAL_OK_CONTINUE;
}
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_array_end, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, ']');
m_string_push_back M_R(f, ']');
return M_SERIAL_OK_CONTINUE;
}
@@ -677,46 +675,42 @@ m_ser1al_str_json_write_array_end(m_serial_local_t local, m_serial_write_t seria
Initialize 'local' so that it can be used to serialize the map
(local is an unique serialization object of the map).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
{
(void) local; // argument not used
(void) number_of_elements; // Ignored
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, '{');
m_string_push_back M_R(f, '{');
return M_SERIAL_OK_CONTINUE;
}
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_value, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, ':');
m_string_push_back M_R(f, ':');
return M_SERIAL_OK_CONTINUE;
}
/* Write a map separator between elements of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_next, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, ',');
m_string_push_back M_R(f, ',');
return M_SERIAL_OK_CONTINUE;
}
/* End the writing of a map into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_end, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, '}');
m_string_push_back M_R(f, '}');
return M_SERIAL_OK_CONTINUE;
}
@@ -724,35 +718,32 @@ m_ser1al_str_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
Initialize 'local' so that it can serial the tuple
(local is an unique serialization object of the tuple).
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_tuple_start, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, '{');
m_string_push_back M_R(f, '{');
return M_SERIAL_OK_CONTINUE;
}
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_tuple_id, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
(void) local; // argument not used
(void) max; // Ignored
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
int n = m_string_cat_printf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]);
int n = m_string_cat_printf M_R(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]);
return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
}
/* End the write of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_tuple_end, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, '}');
m_string_push_back M_R(f, '}');
return M_SERIAL_OK_CONTINUE;
}
@@ -761,8 +752,7 @@ m_ser1al_str_json_write_tuple_end(m_serial_local_t local, m_serial_write_t seria
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise
Otherwise, the field 'field_name[index]' will be filled.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_variant_start, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
{
(void) local; // argument not used
(void) max; // argument not used
@@ -770,22 +760,21 @@ m_ser1al_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t s
int n;
if (index >= 0) {
M_ASSERT (index < max);
n = m_string_cat_printf(f, "{\"%s\":", field_name[index]);
n = m_string_cat_printf M_R(f, "{\"%s\":", field_name[index]);
return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
} else {
n = m_string_cat_printf(f, "{}");
n = m_string_cat_printf M_R(f, "{}");
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
}
/* End Writing a variant into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_variant_end, m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
m_string_push_back(f, '}');
m_string_push_back M_R(f, '}');
return M_SERIAL_OK_CONTINUE;
}
@@ -929,12 +918,12 @@ m_ser1al_str_json_read_float(m_serial_read_t serial, long double *r, const size_
/* Read from the stream 'serial' a string.
Set 's' with the string if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_string(m_serial_read_t serial, struct m_string_s *s){
M_P(m_serial_return_code_t, m_ser1al_str_json, _read_string, m_serial_read_t serial, struct m_string_s *s)
{
const char **f = &serial->data[0].cstr;
m_ser1al_str_json_skip(f);
const char *e;
bool b = m_string_parse_str(s, *f, &e);
bool b = m_string_parse_str M_R(s, *f, &e);
serial->data[0].cstr = e;
return b ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
+1413
View File
File diff suppressed because it is too large. Load diff
-553
View File
@@ -1,553 +0,0 @@
/*
* M*LIB - SHARED Pointer Module
*
* Copyright (c) 2017-2024, 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_SHARED_PTR_H
#define MSTARLIB_SHARED_PTR_H
#include "m-core.h"
#include "m-atomic.h"
#include "m-genint.h"
M_BEGIN_PROTECTED_CODE
/* Define shared pointer and its function.
USAGE: SHARED_PTR_DEF(name, type, [, oplist]) */
#define M_SHARED_PTR_DEF(name, ...) \
M_SHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define shared pointer and its function
as the given name name_t
USAGE: SHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */
#define M_SHARED_PTR_DEF_AS(name, name_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_SHAR3D_ATOMIC_OPLIST, name_t ), \
(name, __VA_ARGS__ , M_SHAR3D_ATOMIC_OPLIST, name_t ))) \
M_END_PROTECTED_CODE
/* Define the oplist of a shared pointer.
USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */
#define M_SHARED_PTR_OPLIST(...) \
M_SHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((__VA_ARGS__, M_BASIC_OPLIST ), \
(__VA_ARGS__ )))
/* Define relaxed shared pointer and its function (thread unsafe).
USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
#define M_SHARED_PTR_RELAXED_DEF(name, ...) \
M_SHARED_PTR_RELAXED_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define relaxed shared pointer and its function (thread unsafe)
as the given name name_t
USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
#define M_SHARED_PTR_RELAXED_DEF_AS(name, name_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_SHAR3D_INTEGER_OPLIST, name_t ), \
(name, __VA_ARGS__, M_SHAR3D_INTEGER_OPLIST, name_t ))) \
M_END_PROTECTED_CODE
/* Define shared resource and its function.
This is a bounded pool of resource shared by multiple owners.
USAGE: SHARED_RESOURCE_DEF(name, type, [, oplist]) */
#define M_SHARED_RESOURCE_DEF(name, ...) \
M_SHARED_RESOURCE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
/* Define shared resource and its function
as the given name named_t and the iterator it_t
This is a bounded pool of resource shared by multiple owners.
USAGE: SHARED_RESOURCE_DEF_AS(name, name_t, it_t, type, [, oplist]) */
#define M_SHARED_RESOURCE_DEF_AS(name, name_t, it_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_SHAR3D_RESOURCE_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \
(name, __VA_ARGS__, name_t, it_t ))) \
M_END_PROTECTED_CODE
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
// deferred evaluation
#define M_SHAR3D_PTR_OPLIST_P1(arg) M_SHAR3D_PTR_OPLIST_P2 arg
/* Validation of the given, shared_t oplist */
#define M_SHAR3D_PTR_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_SHAR3D_PTR_OPLIST_P3, M_SHAR3D_PTR_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define M_SHAR3D_PTR_OPLIST_FAILURE(name, oplist) \
((M_LIB_ERROR(ARGUMENT_OF_SHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
#define M_SHAR3D_PTR_OPLIST_P3(name, oplist) ( \
INIT(M_F(name, _init)), \
CLEAR(M_F(name, _clear)), \
INIT_SET(M_F(name, _init_set)), \
SET(M_F(name, _set)) \
INIT_MOVE(M_F(name, _init_move)), \
RESET(M_F(name, _reset)), \
MOVE(M_F(name, _move)), \
SWAP(M_F(name, _swap)) \
,NAME(name) \
,TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*) \
)
// OPLIST to handle a counter of atomic type
#define M_SHAR3D_ATOMIC_OPLIST (TYPE(atomic_int), \
INIT_SET(atomic_init), \
ADD(atomic_fetch_add), \
SUB(atomic_fetch_sub), \
IT_CREF(atomic_load))
// OPLIST to handle a counter of non-atomic type
#define M_SHAR3D_INTEGER_OPLIST (TYPE(int), \
INIT_SET(m_shar3d_integer_init_set), \
ADD(m_shar3d_integer_add), \
SUB(m_shar3d_integer_sub), \
IT_CREF(m_shar3d_integer_cref))
/* Atomic like interface for basic integers */
M_INLINE void m_shar3d_integer_init_set(int *p, int val) { *p = val; }
M_INLINE int m_shar3d_integer_add(int *p, int val) { int r = *p; *p += val; return r; }
M_INLINE int m_shar3d_integer_sub(int *p, int val) { int r = *p; *p -= val; return r; }
M_INLINE int m_shar3d_integer_cref(int *p) { return *p; }
/********************************** INTERNAL *********************************/
/* Contract of a shared pointer */
#define M_SHAR3D_CONTRACT(shared, cpt_oplist) do { \
M_ASSERT(shared != NULL); \
M_ASSERT(*shared == NULL || M_CALL_IT_CREF(cpt_oplist, &(*shared)->cpt) >= 1); \
} while (0)
// deferred evaluation
#define M_SHAR3D_PTR_DEF_P1(arg) M_ID( M_SHAR3D_PTR_DEF_P2 arg )
/* Validate the oplist before going further */
#define M_SHAR3D_PTR_DEF_P2(name, type, oplist, cpt_oplist, shared_t) \
M_IF_OPLIST(oplist)(M_SHAR3D_PTR_DEF_P3, M_SHAR3D_PTR_DEF_FAILURE)(name, type, oplist, cpt_oplist, shared_t)
/* Stop processing with a compilation failure */
#define M_SHAR3D_PTR_DEF_FAILURE(name, type, oplist, cpt_oplist, shared_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist)
/* Code generation */
#define M_SHAR3D_PTR_DEF_P3(name, type, oplist, cpt_oplist, shared_t) \
M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t) \
M_EMPLACE_QUEUE_DEF(name, cpt_oplist, M_F(name, _init_with), oplist, M_SHAR3D_PTR_DEF_EMPLACE)
/* Define the types */
#define M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t) \
\
typedef struct M_F(name, _s){ \
type *data; /* Pointer to the data */ \
M_GET_TYPE cpt_oplist cpt; /* Counter of how many refs the data */ \
bool combineAlloc; /* Does the data and the ptr share the slot? */ \
} *shared_t[1]; \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
/* Internal type for oplist */ \
typedef shared_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
\
typedef struct M_F(name, _combine_s) { \
struct M_F(name, _s) ptr; \
type data; \
} M_F(name, combine_ct)[1]; \
/* Define the core functions */
#define M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t) \
\
M_INLINE void \
M_F(name, _init)(shared_t shared) \
{ \
*shared = NULL; \
} \
\
M_INLINE void \
M_F(name, _init2)(shared_t shared, type *data) \
{ \
M_ASSERT (shared != NULL); \
/* The shared ptr get exclusive access to data */ \
struct M_F(name, _s) *ptr; \
if (M_UNLIKELY (data == NULL)) { \
*shared = NULL; \
return; \
} \
ptr = M_CALL_NEW(oplist, struct M_F(name, _s)); \
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
M_MEMORY_FULL(sizeof(struct M_F(name, _s))); \
return; \
} \
ptr->data = data; \
M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \
ptr->combineAlloc = false; \
*shared = ptr; \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_INLINE void \
M_F(name, _init_new)(shared_t shared) \
{ \
/* NOTE: Alloc 1 struct with both structures. */ \
struct M_F(name, _combine_s) *p = \
M_CALL_NEW(oplist, struct M_F(name, _combine_s)); \
if (M_UNLIKELY_NOMEM (p == NULL)) { \
M_MEMORY_FULL(sizeof(struct M_F(name, _combine_s))); \
return; \
} \
struct M_F(name, _s) *ptr = &p->ptr; \
ptr->combineAlloc = true; \
type *data = &p->data; \
M_CALL_INIT( oplist, *data); \
ptr->data = data; \
M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \
*shared = ptr; \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
} \
, /* No INIT */ ) \
\
M_INLINE bool \
M_F(name, _NULL_p)(const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
return *shared == NULL; \
} \
\
M_INLINE void \
M_F(name, _init_set)(shared_t dest, \
const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT (dest != shared); \
*dest = *shared; \
if (*dest != NULL) { \
int n = M_CALL_ADD(cpt_oplist, &((*dest)->cpt), 1); \
(void) n; /* unused return value */ \
} \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
} \
\
M_INLINE void \
M_F(name, _clear)(shared_t dest) \
{ \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
if (*dest != NULL) { \
if (M_CALL_SUB(cpt_oplist, &((*dest)->cpt), 1) == 1) { \
bool combineAlloc = (*dest)->combineAlloc; \
/* Note: if combineAlloc is true, the address of the slot \
combining both data & ptr is the same as the address of the \
first element, aka data itself. Static analyzer tools don't \
seem to detect this and report error. */ \
M_CALL_CLEAR(oplist, *(*dest)->data); \
if (combineAlloc == false) { \
M_CALL_DEL(oplist, (*dest)->data); \
} \
M_CALL_DEL(oplist, *dest); \
} \
*dest = NULL; \
} \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
} \
\
M_INLINE void \
M_F(name, _reset)(shared_t dest) \
{ \
/* NOTE: Clear will also set dest to NULL */ \
M_F(name, _clear)(dest); \
} \
\
M_INLINE void \
M_F(name, _set)(shared_t dest, \
const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_F(name, _clear)(dest); \
M_F(name, _init_set)(dest, shared); \
} \
\
M_INLINE void \
M_F(name, _init_move)(shared_t dest, \
shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT (dest != NULL && dest != shared); \
*dest = *shared; \
*shared = NULL; \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
} \
\
M_INLINE void \
M_F(name, _move)(shared_t dest, \
shared_t shared) \
{ \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT (dest != shared); \
M_F(name, _clear)(dest); \
M_F(name, _init_move)(dest, shared); \
} \
\
M_INLINE void \
M_F(name, _swap)(shared_t p1, \
shared_t p2) \
{ \
M_SHAR3D_CONTRACT(p1, cpt_oplist); \
M_SHAR3D_CONTRACT(p2, cpt_oplist); \
/* NOTE: SWAP is not atomic */ \
M_SWAP (struct M_F(name, _s)*, *p1, *p2); \
M_SHAR3D_CONTRACT(p1, cpt_oplist); \
M_SHAR3D_CONTRACT(p2, cpt_oplist); \
} \
\
M_INLINE bool \
M_F(name, _equal_p)(const shared_t p1, \
const shared_t p2) \
{ \
M_SHAR3D_CONTRACT(p1, cpt_oplist); \
M_SHAR3D_CONTRACT(p2, cpt_oplist); \
return *p1 == *p2; \
} \
\
M_INLINE type const * \
M_F(name, _cref)(const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT(*shared != NULL); \
type *data = (*shared)->data; \
M_ASSERT (data != NULL); \
return M_CONST_CAST (type, data); \
} \
\
M_INLINE type * \
M_F(name, _ref)(shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT(*shared != NULL); \
type *data = (*shared)->data; \
M_ASSERT (data != NULL); \
return data; \
} \
/* Definition of the emplace_back function for arrays */
#define M_SHAR3D_PTR_DEF_EMPLACE(name, cpt_oplist, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(M_F(name, _ct) shared \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
/* NOTE: Alloc 1 struct with both structures. */ \
struct M_F(name, _combine_s) *p = \
M_CALL_NEW(oplist, struct M_F(name, _combine_s)); \
if (M_UNLIKELY_NOMEM (p == NULL)) { \
M_MEMORY_FULL(sizeof(struct M_F(name, _combine_s))); \
return; \
} \
struct M_F(name, _s) *ptr = &p->ptr; \
ptr->combineAlloc = true; \
M_F(name, _subtype_ct) *data = &p->data; \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
ptr->data = data; \
M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \
*shared = ptr; \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
} \
/********************************** INTERNAL *********************************/
#define M_SHAR3D_RESOURCE_CONTRACT(s) do { \
M_ASSERT (s != NULL); \
M_ASSERT (s->buffer != NULL); \
} while (0)
// deferred
#define M_SHAR3D_RESOURCE_DEF_P1(arg) M_ID( M_SHAR3D_RESOURCE_DEF_P2 arg )
/* Validate the oplist before going further */
#define M_SHAR3D_RESOURCE_DEF_P2(name, type, oplist, shared_t, it_t) \
M_IF_OPLIST(oplist)(M_SHAR3D_RESOURCE_DEF_P3, M_SHAR3D_RESOURCE_DEF_FAILURE)(name, type, oplist, shared_t, it_t)
/* Stop processing with a compilation failure */
#define M_SHAR3D_RESOURCE_DEF_FAILURE(name, type, oplist, shared_t, it_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SHARED_RESOURCE_DEF): the given argument is not a valid oplist: " #oplist)
#define M_SHAR3D_RESOURCE_DEF_P3(name, type, oplist, shared_t, it_t) \
M_SHAR3D_RESOURCE_DEF_TYPE(name, type, oplist, shared_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_SHAR3D_RESOURCE_DEF_CORE(name, type, oplist, shared_t, it_t) \
/* Define the types */
#define M_SHAR3D_RESOURCE_DEF_TYPE(name, type, oplist, shared_t, it_t) \
\
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_F(name, _atype_s) { \
atomic_uint cpt; \
type x; \
M_CACHELINE_ALIGN(align, type, atomic_uint); \
} M_F(name, _atype_ct); \
\
typedef struct M_F(name, _s) { \
m_genint_t core; \
M_F(name, _atype_ct) *buffer; \
} shared_t[1]; \
\
typedef struct M_F(name, _it_s) { \
unsigned int idx; \
struct M_F(name, _s) *ref; \
} it_t[1]; \
\
/* Internal Types for oplist */ \
typedef shared_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
/* Define the core functions */
#define M_SHAR3D_RESOURCE_DEF_CORE(name, type, oplist, shared_t, it_t) \
M_INLINE void \
M_F(name, _init)(shared_t s, size_t n) \
{ \
M_ASSERT(s != NULL); \
M_ASSERT (n > 0 && n < UINT_MAX); \
s->buffer = M_CALL_REALLOC(oplist, M_F(name, _atype_ct), NULL, n); \
if (M_UNLIKELY_NOMEM (s->buffer == NULL)) { \
M_MEMORY_FULL(sizeof(M_F(name, _atype_ct)) * n); \
return; \
} \
for(size_t i = 0; i < n; i++) { \
M_CALL_INIT(oplist, s->buffer[i].x); \
atomic_init (&s->buffer[i].cpt, 0U); \
} \
m_genint_init(s->core, (unsigned int) n); \
M_SHAR3D_RESOURCE_CONTRACT(s); \
} \
\
M_INLINE void \
M_F(name, _clear)(shared_t s) \
{ \
M_SHAR3D_RESOURCE_CONTRACT(s); \
size_t n = m_genint_size(s->core); \
for(size_t i = 0; i < n; i++) { \
M_CALL_CLEAR(oplist, s->buffer[i].x); \
} \
M_CALL_FREE(oplist, s->buffer); \
s->buffer = NULL; \
m_genint_clear(s->core); \
} \
\
M_INLINE void \
M_F(name, _it)(it_t it, shared_t s) \
{ \
M_SHAR3D_RESOURCE_CONTRACT(s); \
M_ASSERT (it != NULL); \
unsigned int idx = m_genint_pop(s->core); \
it->idx = idx; \
it->ref = s; \
if (M_LIKELY (idx != M_GENINT_ERROR)) { \
M_ASSERT(atomic_load(&s->buffer[idx].cpt) == 0); \
atomic_store(&s->buffer[idx].cpt, 1U); \
} \
} \
\
M_INLINE bool \
M_F(name, _end_p)(it_t it) \
{ \
M_ASSERT (it != NULL); \
return it->idx == M_GENINT_ERROR; \
} \
\
M_INLINE type * \
M_F(name, _ref)(it_t it) \
{ \
M_ASSERT (it != NULL && it->ref != NULL && it->idx != M_GENINT_ERROR); \
M_SHAR3D_RESOURCE_CONTRACT(it->ref); \
return &it->ref->buffer[it->idx].x; \
} \
\
M_INLINE type const * \
M_F(name, _cref)(it_t it) \
{ \
M_ASSERT (it != NULL && it->ref != NULL && it->idx != M_GENINT_ERROR); \
M_SHAR3D_RESOURCE_CONTRACT(it->ref); \
return M_CONST_CAST (type, &it->ref->buffer[it->idx].x); \
} \
\
M_INLINE void \
M_F(name, _end)(it_t it, shared_t s) \
{ \
M_SHAR3D_RESOURCE_CONTRACT(s); \
M_ASSERT (it != NULL); \
M_ASSERT (it->ref == s); \
unsigned int idx = it->idx; \
if (M_LIKELY (idx != M_GENINT_ERROR)) { \
unsigned int c = atomic_fetch_sub (&it->ref->buffer[idx].cpt, 1U); \
if (c == 1) { \
m_genint_push(it->ref->core, idx); \
} \
it->idx = M_GENINT_ERROR; \
} \
} \
\
M_INLINE void \
M_F(name, _it_set)(it_t itd, it_t its) \
{ \
M_ASSERT (itd != NULL && its != NULL); \
M_SHAR3D_RESOURCE_CONTRACT(its->ref); \
itd->ref = its->ref; \
unsigned int idx = its->idx; \
itd->idx = idx; \
if (M_LIKELY (idx != M_GENINT_ERROR)) { \
unsigned int c = atomic_fetch_add(&itd->ref->buffer[idx].cpt, 1U); \
M_ASSERT (c >= 1); \
} \
} \
M_END_PROTECTED_CODE
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define SHARED_PTR_OPLIST M_SHARED_PTR_OPLIST
#define SHARED_PTR_DEF M_SHARED_PTR_DEF
#define SHARED_PTR_DEF_AS M_SHARED_PTR_DEF_AS
#define SHARED_PTR_RELAXED_DEF M_SHARED_PTR_RELAXED_DEF
#define SHARED_PTR_RELAXED_DEF_AS M_SHARED_PTR_RELAXED_DEF_AS
#define SHARED_RESOURCE_DEF M_SHARED_RESOURCE_DEF
#define SHARED_RESOURCE_DEF_AS M_SHARED_RESOURCE_DEF_AS
#endif
#endif
+31 -39
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SNAPSHOT Module
*
* Copyright (c) 2017-2024, 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:
@@ -150,6 +150,7 @@ M_BEGIN_PROTECTED_CODE
#define M_SNAPSH0T_SPSC_CONTRACT(snap) do { \
M_ASSERT((snap) != NULL); \
unsigned char f = atomic_load (&(snap)->flags); \
(void) f; \
M_SNAPSH0T_SPSC_FLAGS_CONTRACT(f); \
} while (0)
@@ -197,10 +198,10 @@ M_BEGIN_PROTECTED_CODE
/* Define the core functions */
#define M_SNAPSH0T_SPSC_DEF_CORE(name, type, oplist, snapshot_t) \
\
M_INLINE void \
M_F(name, _init)(snapshot_t snap) \
M_P(void, name, _init, snapshot_t snap) \
{ \
M_ASSERT(snap != NULL); \
M_UNUSED_CONTEXT(); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
M_CALL_INIT(oplist, snap->data[i].x); \
} \
@@ -208,20 +209,20 @@ M_BEGIN_PROTECTED_CODE
M_SNAPSH0T_SPSC_CONTRACT(snap); \
} \
\
M_INLINE void \
M_F(name, _clear)(snapshot_t snap) \
M_P(void, name, _clear, snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
M_UNUSED_CONTEXT(); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
M_CALL_CLEAR(oplist, snap->data[i].x); \
} \
} \
\
/* const is missing for org due to use of atomic_load of org */ \
M_INLINE void \
M_F(name, _init_set)(snapshot_t snap, snapshot_t org) \
M_P(void, name, _init_set, snapshot_t snap, snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(org); \
M_UNUSED_CONTEXT(); \
M_ASSERT(snap != NULL && snap != org); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
M_CALL_INIT_SET(oplist, snap->data[i].x, org->data[i].x); \
@@ -231,11 +232,11 @@ M_BEGIN_PROTECTED_CODE
} \
\
/* const is missing for org due to use of atomic_load of org */ \
M_INLINE void \
M_F(name, _set)(snapshot_t snap, snapshot_t org) \
M_P(void, name, _set, snapshot_t snap, snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
M_SNAPSH0T_SPSC_CONTRACT(org); \
M_UNUSED_CONTEXT(); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
M_CALL_SET(oplist, snap->data[i].x, org->data[i].x); \
} \
@@ -259,12 +260,11 @@ M_BEGIN_PROTECTED_CODE
,) /* IF_METHOD (INIT_MOVE) */ \
\
M_IF_METHOD(MOVE, oplist)( \
M_INLINE void \
M_F(name, _move)(snapshot_t snap, \
snapshot_t org) \
M_P(void, name, _move, snapshot_t snap, snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
M_SNAPSH0T_SPSC_CONTRACT(org); \
M_UNUSED_CONTEXT(); \
M_ASSERT(snap != org); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
M_CALL_MOVE(oplist, snap->data[i].x, org->data[i].x); \
@@ -382,8 +382,7 @@ typedef struct m_snapsh0t_mrsw_s {
} while (0)
/* Initialize m_snapsh0t_mrsw_ct for n readers (constructor) */
M_INLINE void
m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n)
M_P(void, m_snapsh0t, _mrsw_init, m_snapsh0t_mrsw_ct s, size_t n)
{
M_ASSERT (s != NULL);
M_ASSERT (n >= 1 && n <= M_SNAPSH0T_SPMC_MAX_READER);
@@ -391,15 +390,14 @@ m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n)
n += M_SNAPSH0T_SPMC_EXTRA_BUFFER;
// Initialize the counters to zero (no reader use it)
atomic_uint *ptr = M_MEMORY_REALLOC (atomic_uint, NULL, n);
atomic_uint *ptr = M_MEMORY_REALLOC (m_context, atomic_uint, NULL, 0, n);
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(sizeof (atomic_uint) * n);
return;
M_MEMORY_FULL(atomic_uint, n);
}
s->cptTab = ptr;
for(size_t i = 0; i < n; i++)
atomic_init(&s->cptTab[i], 0U);
m_genint_init (s->freeList, (unsigned int) n);
m_genint_init M_R(s->freeList, (unsigned int) n);
// Get a free buffer and set it as available for readers
unsigned int w = m_genint_pop(s->freeList);
@@ -415,12 +413,11 @@ m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n)
}
/* Clear m_snapsh0t_mrsw_ct (destructor) */
M_INLINE void
m_snapsh0t_mrsw_clear(m_snapsh0t_mrsw_ct s)
M_P(void, m_snapsh0t, _mrsw_clear, m_snapsh0t_mrsw_ct s)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
M_MEMORY_FREE (s->cptTab);
m_genint_clear(s->freeList);
M_MEMORY_FREE (m_context, atomic_uint, s->cptTab, s->n_reader+M_SNAPSH0T_SPMC_EXTRA_BUFFER);
m_genint_clear M_R(s->freeList);
s->cptTab = NULL;
s->n_reader = 0;
}
@@ -639,35 +636,32 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
/* Define the core functions */
#define M_SNAPSH0T_SPMC_DEF_CORE(name, type, oplist, snapshot_t) \
\
M_INLINE void \
M_F(name, _init)(snapshot_t snap, size_t nReader) \
M_P(void, name, _init, snapshot_t snap, size_t nReader) \
{ \
M_ASSERT (snap != NULL); \
M_ASSERT (nReader > 0 && nReader <= M_SNAPSH0T_SPMC_MAX_READER); \
snap->data = M_CALL_REALLOC(oplist, M_F(name, _aligned_type_ct), \
NULL, nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER); \
NULL, 0, nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER); \
if (M_UNLIKELY_NOMEM (snap->data == NULL)) { \
M_MEMORY_FULL(sizeof(M_F(name, _aligned_type_ct)) * \
M_MEMORY_FULL(M_F(name, _aligned_type_ct), \
(nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER)); \
return; \
} \
for(size_t i = 0; i < nReader + M_SNAPSH0T_SPMC_EXTRA_BUFFER; i++) { \
M_CALL_INIT(oplist, snap->data[i].x); \
} \
m_snapsh0t_mrsw_init(snap->core, nReader); \
m_snapsh0t_mrsw_init M_R(snap->core, nReader); \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
} \
\
M_INLINE void \
M_F(name, _clear)(snapshot_t snap) \
M_P(void, name, _clear, snapshot_t snap) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
size_t nReader = m_snapsh0t_mrsw_size(snap->core); \
for(size_t i = 0; i < nReader + M_SNAPSH0T_SPMC_EXTRA_BUFFER; i++) { \
M_CALL_CLEAR(oplist, snap->data[i].x); \
} \
M_CALL_FREE(oplist, snap->data); \
m_snapsh0t_mrsw_clear(snap->core); \
M_CALL_FREE(oplist, M_F(name, _aligned_type_ct), snap->data, nReader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); \
m_snapsh0t_mrsw_clear M_R(snap->core); \
} \
\
M_INLINE type * \
@@ -746,19 +740,17 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
/* Define the core functions */
#define M_SNAPSH0T_MPMC_DEF_CORE(name, type, oplist, snapshot_t) \
\
M_INLINE void \
M_F(name, _init)(snapshot_t snap, size_t nReader, size_t nWriter) \
M_P(void, name, _init, snapshot_t snap, size_t nReader, size_t nWriter) \
{ \
M_F(name, _mrsw_init)(snap->core, nReader + nWriter -1 ); \
M_F(name, _mrsw_init)M_R(snap->core, nReader + nWriter -1 ); \
unsigned int idx = snap->core->core->currentWrite; \
snap->core->core->currentWrite = M_GENINT_ERROR; \
m_snapsh0t_mrsw_write_end(snap->core->core, idx); \
} \
\
M_INLINE void \
M_F(name, _clear)(snapshot_t snap) \
M_P(void, name, _clear, snapshot_t snap) \
{ \
M_F(name, _mrsw_clear)(snap->core); \
M_F(name, _mrsw_clear)M_R(snap->core); \
} \
\
M_INLINE type * \
@@ -774,7 +766,7 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap->core); \
const M_F(name, _mrsw_aligned_type_ct) *oldx; \
oldx = M_CTYPE_FROM_FIELD(M_F(name, _mrsw_aligned_type_ct), old, type, x); \
oldx = (const M_F(name, _mrsw_aligned_type_ct) *) old; \
M_ASSERT (oldx >= snap->core->data); \
M_ASSERT (oldx < snap->core->data + snap->core->core->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); \
M_ASSERT(snap->core->core->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER < UINT_MAX); \
+352 -263
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin Mutex & Thread wrapper
*
* Copyright (c) 2017-2024, 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:
+104 -147
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - TREE module
*
* Copyright (c) 2017-2024, 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:
@@ -215,9 +215,9 @@ typedef int32_t m_tr33_index_t;
M_TR33_CONTRACT(tree); \
} \
\
M_INLINE void \
M_F(name, _reset)(tree_t tree) { \
M_P(void, name, _reset, tree_t tree) { \
M_TR33_CONTRACT(tree); \
M_UNUSED_CONTEXT(); \
if (tree->size > 0) { \
/* We don't scan recursively the node tree, but sequentially */ \
m_tr33_index_t free_index = tree->free_index; \
@@ -241,18 +241,16 @@ typedef int32_t m_tr33_index_t;
M_TR33_CONTRACT(tree); \
} \
\
M_INLINE void \
M_F(name, _clear)(tree_t tree) { \
M_F(name, _reset)(tree); \
M_P(void, name, _clear, tree_t tree) { \
M_F(name, _reset)M_R(tree); \
struct M_F(name,_node_s)*ptr = tree->tab == NULL ? NULL : tree->tab-1;\
M_CALL_FREE(oplist, ptr); \
M_CALL_FREE(oplist, struct M_F(name, _node_s), ptr, (size_t)(tree->capacity+1)); \
/* This is so reusing the object implies an assertion failure */ \
tree->size = 1; \
tree->tab = NULL; \
} \
\
M_INLINE void \
M_F(name, _reserve)(tree_t tree, size_t alloc) { \
M_P(void, name, _reserve, tree_t tree, size_t alloc) { \
M_TR33_CONTRACT(tree); \
/* Nothing to do if the request is lower than the current capacity. */ \
if (alloc <= (size_t) tree->capacity) { \
@@ -260,18 +258,16 @@ typedef int32_t m_tr33_index_t;
} \
/* Realloc the array */ \
if (M_UNLIKELY_NOMEM (alloc >= INT32_MAX)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \
return; \
M_MEMORY_FULL(struct M_F(name, _node_s), alloc); \
} \
/* Allocate one more term in the array so that tab[-1] exists. \
This enables performing latter tab[M_TR33_NO_NODE].x = something; \
as M_TR33_NO_NODE is -1. This enables avoiding testing for \
M_TR33_NO_NODE in some cases, performing branchless code. */ \
struct M_F(name,_node_s)*ptr = tree->tab == NULL ? NULL : tree->tab-1;\
ptr = M_CALL_REALLOC(oplist, struct M_F(name, _node_s), ptr, alloc+1);\
ptr = M_CALL_REALLOC(oplist, struct M_F(name, _node_s), ptr, (size_t)(tree->capacity+1), alloc+1); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \
return; \
M_MEMORY_FULL(struct M_F(name, _node_s), alloc); \
} \
/* Skip the first term to keep it as empty & unused */ \
ptr++; \
@@ -305,8 +301,7 @@ typedef int32_t m_tr33_index_t;
M_TR33_CONTRACT(tree); \
} \
\
M_INLINE m_tr33_index_t \
M_C3(m_tr33_, name, _alloc_node)(tree_t tree) { \
M_P(m_tr33_index_t, name, _i_alloc_node, tree_t tree) { \
m_tr33_index_t ret = tree->free_index; \
if (M_UNLIKELY(ret < 0)) { \
/* No more enough space: realloc the array */ \
@@ -314,18 +309,16 @@ typedef int32_t m_tr33_index_t;
/* Take into account if realloc is allowed */ \
alloc += tree->allow_realloc; \
if (M_UNLIKELY_NOMEM (alloc >= INT32_MAX)) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \
return M_TR33_NO_NODE; \
M_MEMORY_FULL(struct M_F(name, _node_s), alloc); \
} \
/* Allocate one more term in the array so that tab[-1] exists. \
This enables performing latter tab[M_TR33_NO_NODE].x = something; \
as M_TR33_NO_NODE is -1. This enables avoiding testing for \
M_TR33_NO_NODE in some cases, performing branchless code. */ \
struct M_F(name,_node_s)*ptr = tree->tab == NULL ? NULL : tree->tab-1; \
ptr = M_CALL_REALLOC(oplist, struct M_F(name, _node_s), ptr, alloc+1); \
ptr = M_CALL_REALLOC(oplist, struct M_F(name, _node_s), ptr, (size_t)(tree->capacity+1), alloc+1); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof (struct M_F(name, _node_s)) * alloc); \
return M_TR33_NO_NODE; \
M_MEMORY_FULL(struct M_F(name, _node_s), alloc); \
} \
/* Skip the first term to keep it as empty & unused */ \
ptr++; \
@@ -360,11 +353,10 @@ typedef int32_t m_tr33_index_t;
tree->free_index = i; \
} \
\
M_INLINE it_t \
M_F(name, _set_root)(tree_t tree, type const data) { \
M_P(it_t, name, _set_root, tree_t tree, type const data) { \
M_TR33_CONTRACT(tree); \
M_F(name, _reset)(tree); \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(tree); \
M_F(name, _reset)M_R(tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(tree); \
tree->tab[i].parent = M_TR33_ROOT_NODE; \
tree->tab[i].left = M_TR33_NO_NODE; \
tree->tab[i].right = M_TR33_NO_NODE; \
@@ -446,10 +438,9 @@ typedef int32_t m_tr33_index_t;
return i < 0 ? NULL : &it.tree->tab[i].data; \
} \
\
M_INLINE it_t \
M_F(name, _insert_up_raw)(it_t it) { \
M_P(it_t, name, _insert_up_raw, it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(it.tree); \
m_tr33_index_t parent = it.tree->tab[it.index].parent; \
m_tr33_index_t left = it.tree->tab[it.index].left; \
m_tr33_index_t right = it.tree->tab[it.index].right; \
@@ -476,26 +467,23 @@ typedef int32_t m_tr33_index_t;
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_up)(it_t pos, type const data) { \
it_t it = M_F(name, _insert_up_raw)(pos); \
M_P(it_t, name, _insert_up, it_t pos, type const data) { \
it_t it = M_F(name, _insert_up_raw)M_R(pos); \
M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _move_up)(it_t pos, type *data) { \
it_t it = M_F(name, _insert_up_raw)(pos); \
M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_P(it_t, name, _move_up, it_t pos, type *data) { \
it_t it = M_F(name, _insert_up_raw)M_R(pos); \
M_CALL_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_down_raw)(it_t it) { \
M_P(it_t, name, _insert_down_raw, it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(it.tree); \
m_tr33_index_t child = it.tree->tab[it.index].child; \
it.tree->tab[i].parent = it.index; \
it.tree->tab[i].left = M_TR33_NO_NODE; \
@@ -513,28 +501,25 @@ typedef int32_t m_tr33_index_t;
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_down)(it_t pos, type const data) { \
it_t it = M_F(name, _insert_down_raw)(pos); \
M_P(it_t, name, _insert_down, it_t pos, type const data) { \
it_t it = M_F(name, _insert_down_raw)M_R(pos); \
M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _move_down)(it_t pos, type *data) { \
it_t it = M_F(name, _insert_down_raw)(pos); \
M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_P(it_t, name, _move_down, it_t pos, type *data) { \
it_t it = M_F(name, _insert_down_raw)M_R(pos); \
M_CALL_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_child_raw)(it_t it) { \
M_P(it_t, name, _insert_child_raw, it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
/* Insert a node as a child of another, making the current children \
of the nodes their siblings */ \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(it.tree); \
m_tr33_index_t child = it.tree->tab[it.index].child; \
it.tree->tab[i].parent = it.index; \
it.tree->tab[i].left = M_TR33_NO_NODE; \
@@ -550,27 +535,24 @@ typedef int32_t m_tr33_index_t;
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_child)(it_t pos, type const data) { \
it_t it = M_F(name, _insert_child_raw)(pos); \
M_P(it_t, name, _insert_child, it_t pos, type const data) { \
it_t it = M_F(name, _insert_child_raw)M_R(pos); \
M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _move_child)(it_t pos, type *data) { \
it_t it = M_F(name, _insert_child_raw)(pos); \
M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_P(it_t, name, _move_child, it_t pos, type *data) { \
it_t it = M_F(name, _insert_child_raw)M_R(pos); \
M_CALL_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_left_raw)(it_t it) { \
M_P(it_t, name, _insert_left_raw, it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
M_ASSERT(it.index != it.tree->root_index); \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(it.tree); \
m_tr33_index_t left = it.tree->tab[it.index].left; \
m_tr33_index_t parent = it.tree->tab[it.index].parent; \
it.tree->tab[i].parent = parent; \
@@ -590,27 +572,24 @@ typedef int32_t m_tr33_index_t;
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_left)(it_t pos, type const data) { \
it_t it = M_F(name, _insert_left_raw)(pos); \
M_P(it_t, name, _insert_left, it_t pos, type const data) { \
it_t it = M_F(name, _insert_left_raw)M_R(pos); \
M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _move_left)(it_t pos, type *data) { \
it_t it = M_F(name, _insert_left_raw)(pos); \
M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_P(it_t, name, _move_left, it_t pos, type *data) { \
it_t it = M_F(name, _insert_left_raw)M_R(pos); \
M_CALL_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_right_raw)(it_t it) { \
M_P(it_t, name, _insert_right_raw, it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
M_ASSERT(it.index != it.tree->root_index); \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(it.tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(it.tree); \
m_tr33_index_t right = it.tree->tab[it.index].right; \
it.tree->tab[i].parent = it.tree->tab[it.index].parent; \
it.tree->tab[i].left = it.index; \
@@ -624,18 +603,16 @@ typedef int32_t m_tr33_index_t;
return it; \
} \
\
M_INLINE it_t \
M_F(name, _insert_right)(it_t pos, type const data) { \
it_t it = M_F(name, _insert_right_raw)(pos); \
M_P(it_t, name, _insert_right, it_t pos, type const data) { \
it_t it = M_F(name, _insert_right_raw)M_R(pos); \
M_CALL_INIT_SET(oplist, it.tree->tab[it.index].data, data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
\
M_INLINE it_t \
M_F(name, _move_right)(it_t pos, type *data) { \
it_t it = M_F(name, _insert_right_raw)(pos); \
M_DO_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_P(it_t, name, _move_right, it_t pos, type *data) { \
it_t it = M_F(name, _insert_right_raw)M_R(pos); \
M_CALL_INIT_MOVE(oplist, it.tree->tab[it.index].data, *data); \
M_TR33_IT_CONTRACT(it, true); \
return it; \
} \
@@ -884,9 +861,9 @@ typedef int32_t m_tr33_index_t;
return &it.tree->tab[it.index].data; \
} \
\
M_INLINE bool \
M_F(name, _remove)(it_t it) { \
M_P(bool, name, _remove, it_t it) { \
M_TR33_IT_CONTRACT(it, false); \
M_UNUSED_CONTEXT(); \
if (M_UNLIKELY(it.index < 0)) { return false; } \
type *ptr = M_F(name, _unlink)(it); \
M_CALL_CLEAR(oplist, *ptr); \
@@ -1018,15 +995,14 @@ typedef int32_t m_tr33_index_t;
assert(b); \
} \
\
M_INLINE void \
M_F(name, _prune)(it_t it) { \
M_P(void, name, _prune, it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
/* remove the node, including its childs */ \
it_t child = M_F(name, _it_subpost)(it); \
while (!M_F(name, _end_p)(child)) { \
it_t next = child; \
M_F(name, _next_subpost)(&next, it); \
bool b = M_F(name, _remove)(child); \
bool b = M_F(name, _remove)M_R(child); \
(void) b; /* parameter not used */ \
M_ASSERT(b); \
child = next; \
@@ -1122,8 +1098,7 @@ typedef int32_t m_tr33_index_t;
/* Define the classic extended missing methods of a tree */
#define M_TR33_DEF_P4_EXT(name, type, oplist, tree_t, it_t) \
M_INLINE void \
M_F(name, _init_set)(tree_t tree, const tree_t ref) { \
M_P(void, name, _init_set, tree_t tree, const tree_t ref) { \
tree->size = ref->size; \
tree->capacity = ref->capacity; \
tree->root_index = ref->root_index; \
@@ -1134,10 +1109,9 @@ typedef int32_t m_tr33_index_t;
tree->tab = NULL; \
} else { \
struct M_F(name, _node_s) *ptr = \
M_CALL_REALLOC(oplist, struct M_F(name, _node_s), NULL, alloc+1); \
M_CALL_REALLOC(oplist, struct M_F(name, _node_s), NULL, 0, alloc+1); \
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
M_MEMORY_FULL(sizeof(struct M_F(name, _node_s)) * alloc); \
return; \
M_MEMORY_FULL(struct M_F(name, _node_s), alloc); \
} \
tree->tab = ++ptr; \
/* We don't scan recursively the node tree, but sequentially */ \
@@ -1155,11 +1129,10 @@ typedef int32_t m_tr33_index_t;
M_TR33_CONTRACT(tree); \
} \
\
M_INLINE void \
M_F(name, _set)(tree_t tree, const tree_t ref) { \
M_P(void, name, _set, tree_t tree, const tree_t ref) { \
/* No optimum, but good enough currently */ \
M_F(name, _clear)(tree); \
M_F(name, _init_set)(tree, ref); \
M_F(name, _clear)M_R(tree); \
M_F(name, _init_set)M_R(tree, ref); \
} \
\
M_INLINE void \
@@ -1175,9 +1148,8 @@ typedef int32_t m_tr33_index_t;
ref->tab = NULL; \
} \
\
M_INLINE void \
M_F(name, _move)(tree_t tree, tree_t ref) { \
M_F(name, _clear)(tree); \
M_P(void, name, _move, tree_t tree, tree_t ref) { \
M_F(name, _clear)M_R(tree); \
M_F(name, _init_move)(tree, ref); \
} \
\
@@ -1299,30 +1271,29 @@ typedef int32_t m_tr33_index_t;
/* Define the IO methods of a tree */
#define M_TR33_DEF_P4_IO(name, type, oplist, tree_t, it_t) \
M_IF_METHOD(GET_STR, oplist)( \
M_INLINE void \
M_F(name, _get_str)(string_t str, /*const*/ tree_t tree, bool append) { \
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
M_P(void, name, _get_str, m_string_t str, /*const*/ tree_t tree, bool append) { \
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
it_t it = M_F(name, _it)(tree); \
while (!M_F(name, _end_p)(it)) { \
m_string_push_back (str, '{'); \
m_string_push_back M_R(str, '{'); \
type const *item = M_F(name, _cref)(it); \
M_CALL_GET_STR(oplist, str, *item, true); \
/* Go down the tree */ \
if (M_F(name, _it_down)(&it)) { \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back (str, '['); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, '['); \
continue; \
} \
m_string_push_back (str, '}'); \
m_string_push_back M_R(str, '}'); \
if (M_F(name, _it_right)(&it)) { \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
continue; \
} \
while (M_F(name, _it_up)(&it)) { \
m_string_push_back (str, ']'); \
m_string_push_back (str, '}'); \
m_string_push_back M_R(str, ']'); \
m_string_push_back M_R(str, '}'); \
if (M_F(name, _it_right)(&it)) { \
m_string_push_back (str, M_GET_SEPARATOR oplist); \
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
goto continue_tree; \
} \
} \
@@ -1330,17 +1301,16 @@ M_F(name, _get_str)(string_t str, /*const*/ tree_t tree, bool append) { \
continue_tree: \
(void) 0; \
} \
m_string_push_back (str, ']'); \
m_string_push_back M_R(str, ']'); \
} \
, /* No GET_STR */ ) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
M_INLINE bool \
M_F(name, _parse_str)(tree_t tree, const char str[], const char **endp) { \
M_P(bool, name, _parse_str, tree_t tree, const char str[], const char **endp) { \
M_TR33_CONTRACT(tree); \
int cmd = 0; \
it_t it; \
M_F(name, _reset)(tree); \
M_F(name, _reset)M_R(tree); \
bool success = false; \
int c = *str; \
if (M_UNLIKELY (c != '[')) goto exit; \
@@ -1357,9 +1327,9 @@ M_F(name, _parse_str)(tree_t tree, const char str[], const char **endp) { \
if (b == false || c == 0) break; \
/* Insert the item as the root or as a right sibling or as a child */ \
switch (cmd) { \
case 0: it = M_F(name, _set_root)(tree, item); break; \
case 1: it = M_F(name, _insert_right)(it, item); break; \
case 2: it = M_F(name, _insert_child)(it, item); break; \
case 0: it = M_F(name, _set_root)M_R(tree, item); break; \
case 1: it = M_F(name, _insert_right)M_R(it, item); break; \
case 2: it = M_F(name, _insert_child)M_R(it, item); break; \
default: M_ASSERT(0); \
} \
if (c == ',') { \
@@ -1432,12 +1402,11 @@ M_F(name, _out_str)(FILE *f, /*const*/ tree_t tree) { \
, /* No OUT_STR */ ) \
\
M_IF_METHOD(IN_STR, oplist)( \
M_INLINE bool \
M_F(name, _in_str)(tree_t tree, FILE *f) { \
M_P(bool, name, _in_str, tree_t tree, FILE *f) { \
M_TR33_CONTRACT(tree); \
int cmd = 0; \
it_t it; \
M_F(name, _reset)(tree); \
M_F(name, _reset)M_R(tree); \
bool success = false; \
int c = fgetc(f); \
if (M_UNLIKELY (c != '[')) goto exit; \
@@ -1455,9 +1424,9 @@ M_F(name, _in_str)(tree_t tree, FILE *f) { \
if (b == false || c == 0) break; \
/* Insert the item as the root or as a right sibling or as a child */ \
switch (cmd) { \
case 0: it = M_F(name, _set_root)(tree, item); break; \
case 1: it = M_F(name, _insert_right)(it, item); break; \
case 2: it = M_F(name, _insert_child)(it, item); break; \
case 0: it = M_F(name, _set_root)M_R(tree, item); break; \
case 1: it = M_F(name, _insert_right)M_R(it, item); break; \
case 2: it = M_F(name, _insert_child)M_R(it, item); break; \
default: M_ASSERT(0); \
} \
if (c == ',') { \
@@ -1494,22 +1463,20 @@ exit: \
/* Define the emplace methods of a tree */
#define M_TR33_DEF_P4_EMPLACE(name, type, oplist, tree_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, tree_t, M_F(name, _emplace_root), oplist, M_TR33_EMPLACE_ROOT_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_up), oplist, M_TR33_EMPLACE_UP_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_down), oplist, M_TR33_EMPLACE_DOWN_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_child), oplist, M_TR33_EMPLACE_CHILD_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_left), oplist, M_TR33_EMPLACE_LEFT_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, M_F(name, _emplace_right), oplist, M_TR33_EMPLACE_RIGHT_DEF)
M_EMPLACE_QUEUE_DEF(name, tree_t, _emplace_root, oplist, M_TR33_EMPLACE_ROOT_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, _emplace_up, oplist, M_TR33_EMPLACE_UP_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, _emplace_down, oplist, M_TR33_EMPLACE_DOWN_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, _emplace_child, oplist, M_TR33_EMPLACE_CHILD_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, _emplace_left, oplist, M_TR33_EMPLACE_LEFT_DEF) \
M_EMPLACE_QUEUE_DEF(name, it_t, _emplace_right, oplist, M_TR33_EMPLACE_RIGHT_DEF)
/* Definition of the emplace_back functions */
#define M_TR33_EMPLACE_ROOT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE M_F(name, _it_ct) \
function_name(name_t tree \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(M_F(name, _it_ct), name, function_name, name_t tree M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_TR33_CONTRACT(tree); \
M_F(name, _reset)(tree); \
m_tr33_index_t i = M_C3(m_tr33_, name, _alloc_node)(tree); \
M_F(name, _reset)M_R(tree); \
m_tr33_index_t i = M_F(name, _i_alloc_node)M_R(tree); \
tree->tab[i].parent = M_TR33_ROOT_NODE; \
tree->tab[i].left = M_TR33_NO_NODE; \
tree->tab[i].right = M_TR33_NO_NODE; \
@@ -1525,55 +1492,45 @@ exit: \
}
#define M_TR33_EMPLACE_UP_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE name_t \
function_name(name_t pos \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(name_t, name, function_name, name_t pos M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
name_t it = M_F(name, _insert_up_raw)(pos); \
name_t it = M_F(name, _insert_up_raw)M_R(pos); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \
exp_emplace_type); \
return it; \
}
#define M_TR33_EMPLACE_DOWN_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE name_t \
function_name(name_t pos \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(name_t, name, function_name, name_t pos M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
name_t it = M_F(name, _insert_down_raw)(pos); \
name_t it = M_F(name, _insert_down_raw)M_R(pos); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \
exp_emplace_type); \
return it; \
}
#define M_TR33_EMPLACE_CHILD_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE name_t \
function_name(name_t pos \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(name_t, name, function_name, name_t pos M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
name_t it = M_F(name, _insert_child_raw)(pos); \
name_t it = M_F(name, _insert_child_raw)M_R(pos); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \
exp_emplace_type); \
return it; \
}
#define M_TR33_EMPLACE_LEFT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE name_t \
function_name(name_t pos \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(name_t, name, function_name, name_t pos M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
name_t it = M_F(name, _insert_left_raw)(pos); \
name_t it = M_F(name, _insert_left_raw)M_R(pos); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \
exp_emplace_type); \
return it; \
}
#define M_TR33_EMPLACE_RIGHT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE name_t \
function_name(name_t pos \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
M_P(name_t, name, function_name, name_t pos M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
name_t it = M_F(name, _insert_right_raw)(pos); \
name_t it = M_F(name, _insert_right_raw)M_R(pos); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, it.tree->tab[it.index].data, \
exp_emplace_type); \
return it; \
+70 -34
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - try / catch mechanism for M*LIB
*
* Copyright (c) 2017-2024, 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:
@@ -33,9 +33,27 @@
* so that for each variable defined using M_LET,
* its destructor is still called when exceptions are thrown.
* It is either the C++ try,
* or it uses a GCC or CLANG extension,
* or the standard C compliant way (much slower).
* or it uses a GCC extension,
* or it uses a CLANG extension,
* or the standard C compliant way through longjmp (slower).
* The user can override the desired mechanism.
*
* Performance Measures of the mechanism costs to create recursively 100000 empty strings (RAII / no exception)
* TCC : LET(longjmp): 6412 us ON_EXCEPTION(longjmp): 6550 us
* CLANG : LET(longjmp): 3922 us ON_EXCEPTION(longjmp): 3972 us
* CLANG+BLOCK: LET(BLOCK): 3549 us ON_EXCEPTION(longjmp): 4196 us
* GCC (O2) : LET(nested): 1828 us ON_EXCEPTION(longjmp): 2033 us
* G++ (O2/O3): LET(c++): 1850 us ON_EXCEPTION(c++): 1874 us
* GCC (O3) : LET(nested): 1377 us ON_EXCEPTION(longjmp): 1402 us
* CLANG++(O2/O3): LET(c++): 428 us ON_EXCEPTION(c++): 433 us
* Therefore the C mechanism of try emulation is 3x times slower than the C++ "zero cost" implementation
* in this scenario where the mechanism cost is the most measurable.
* Most of the time difference comes from the size of the stack frame (memory bandwidth):
* * 240 bytes for CLANG(block/longjmp)
* * 80 bytes for GCC/nested
* * 96 bytes for GCC/longjmp
* * 64 bytes for G++ (!)
* * 16 bytes for CLANG++
*/
#ifndef M_USE_TRY_MECHANISM
# if defined(__has_extension)
@@ -252,7 +270,7 @@ namespace m_lib {
* The main difficulty is the mechanism to register the CLEAR operators
* to call when throwing an exception.
* Contrary to the C++ back-end, it is not cost-free as it adds some
* instructions to the normal behavior of the program.
* instructions to the normal behavior of the program, usually 5 store instructions.
*/
#else
@@ -292,12 +310,16 @@ typedef intptr_t m_try_jmp_buf[5];
// this point in the stack frame. Each nodes are linked together, so that we can
// analyze the stack frame on exception.
typedef struct m_try_s {
enum { M_STATE_TRY, M_STATE_EXCEPTION_IN_PROGRESS, M_STATE_EXCEPTION_CAUGHT,
M_STATE_CLEAR_JMPBUF, M_STATE_CLEAR_CB } kind;
struct m_try_s *next;
enum { M_STATE_TRY, // Try block starts
M_STATE_EXCEPTION_IN_PROGRESS, // A try block or jmpbuf is being in progress (before being caught)
M_STATE_EXCEPTION_CAUGHT, // An exception in progress has been successfully caught (==> no rethrow)
M_STATE_CLEAR_JMPBUF, // Need to jmpbuf here to clear things
M_STATE_CLEAR_CB // Need to call the function to clear things
} kind;
struct m_try_s *next; // Next try block or clear mechanism on the stack frame.
union {
m_try_jmp_buf buf;
struct { void (M_TRY_FUNC_OPERATOR func)(void*); void *data; } clear;
m_try_jmp_buf buffer; // To use for M_STATE_CLEAR_JMPBUF
struct { void (M_TRY_FUNC_OPERATOR func)(void*); void *data; } clear; // To use for M_STATE_CLEAR_CB
} data;
} m_try_t[1];
@@ -306,14 +328,14 @@ typedef struct m_try_s {
#define M_TRY_B(cont, buf, exception) \
for(bool cont = true ; cont ; cont = false) \
for(m_try_t buf ; cont ; m_try_clear(buf), cont = false ) \
for(const struct m_exception_s *exception = NULL; cont; cont = false, exception = exception) \
if (m_try_init(buf))
for(const struct m_exception_s *exception = (m_try_init(buf), NULL); cont; cont = false, exception = exception) \
if (m_try_setjmp((buf->data.buffer)) == 0)
// Throw the error code
#define M_THROW_1(error_code) \
m_throw( &(const struct m_exception_s) { error_code, __LINE__, 0, __FILE__, { 0 } } )
// Throw the error code
// Throw the error code and the associated data.
#define M_THROW_N(error_code, ...) \
m_throw( &(const struct m_exception_s) { error_code, __LINE__, M_NARGS(__VA_ARGS__), __FILE__, \
{ __VA_ARGS__ } } )
@@ -334,6 +356,7 @@ extern M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void m_throw(const struct m_excepti
// Macro to add once in one source file to define theses global:
#define M_TRY_DEF_ONCE_B() \
/* Define the global variables */ \
M_THREAD_ATTR struct m_try_s *m_global_error_list; \
M_THREAD_ATTR struct m_exception_s m_global_exception; \
\
@@ -345,7 +368,7 @@ extern M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void m_throw(const struct m_excepti
/* Analyze the error list to see what has been registered */ \
struct m_try_s *e = m_global_error_list; \
while (e != NULL) { \
/* A CLEAR operator has been registered: call it */ \
/* A CLEAR by CB operator has been registered: call it */ \
if (e->kind == M_STATE_CLEAR_CB) { \
e->data.clear.func(e->data.clear.data); \
} \
@@ -358,12 +381,12 @@ extern M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void m_throw(const struct m_excepti
m_exception_set(&m_global_exception, exception); \
e->kind = M_STATE_EXCEPTION_IN_PROGRESS; \
m_global_error_list = e; \
m_try_longjmp(e->data.buf, 1); \
m_try_longjmp(e->data.buffer, 1); \
} \
/* Next stack frame */ \
e = e->next; \
} \
/* No exception found. \
/* No try block found. \
Display the information and halt program . */ \
M_RAISE_FATAL("Exception '%u' raised by (%s:%d) is not caught. Program aborted.\n", \
exception->error_code, exception->filename, exception->line); \
@@ -385,6 +408,9 @@ m_catch(m_try_t state, unsigned error_code, const struct m_exception_s **excepti
M_ASSERT(m_global_error_list == state);
M_ASSERT(state->kind == M_STATE_EXCEPTION_IN_PROGRESS);
*exception = &m_global_exception;
// If error_code is 0, we want to catch **all** errors.
// Otherwise we want to cache only the given error
// Return false if we don't catcht it.
if (error_code != 0 && m_global_exception.error_code != error_code)
return false;
// The exception has been caught.
@@ -395,6 +421,14 @@ m_catch(m_try_t state, unsigned error_code, const struct m_exception_s **excepti
return true;
}
#if defined(__GNUC__)
#if __GNUC__ >= 12
/* Warnings disabled for GNU C in C mode (Affecting a global variable to the address of a local variable) */
_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wdangling-pointer\"")
#endif
#endif
// Initialize the state to a TRY state.
M_INLINE void
m_try_init(m_try_t state)
@@ -402,10 +436,8 @@ m_try_init(m_try_t state)
state->kind = M_STATE_TRY;
state->next = m_global_error_list;
m_global_error_list = state;
// setjmp needs to be done in the MACRO.
// setjmp cannot be done here
}
#define m_try_init(s) \
M_LIKELY ((m_try_init(s), m_try_setjmp(((s)->data.buf)) != 1))
// Disable the current TRY block.
M_INLINE void
@@ -429,6 +461,9 @@ m_try_clear(m_try_t state)
// * post: which is called after the constructor
// * final: which is called before the destructor.
// There is two mechanims: calling a CLEAR callback (faster but need compiler extension)
// or using an intermediary jmpbuf_t longjmp (slower but standard compliant).
// We register a call to the CLEAR callback.
// We don't modify m_global_error_list until we have successfully called the INIT operator
// to avoid registering the CLEAR operator on exception whereas the object is not initialized yet.
@@ -461,7 +496,8 @@ m_try_cb_final(m_try_t state)
m_global_error_list = state->next;
}
// Pre initialization function. Save the stack frame for a longjmp
// We register a longjmp here to handle the cleaning of the exception at this function.
// Pre initialization function.
M_INLINE bool
m_try_jump_pre(m_try_t state)
{
@@ -470,15 +506,13 @@ m_try_jump_pre(m_try_t state)
return true;
}
// Post initialization function. Register the stack frame for a longjmp
// Post initialization function. Save the stack frame for a longjmp
M_INLINE void
m_try_jump_post(m_try_t state)
{
M_ASSERT(state->next == m_global_error_list);
m_global_error_list = state;
}
// And call setjmp to register the position in the code.
#define m_try_jump_post(s) \
M_LIKELY ((m_try_jump_post(s), m_try_setjmp(((s)->data.buf)) != 1))
// The object will be cleared.
// We can pop the stack frame of the errors.
@@ -488,6 +522,11 @@ m_try_jump_final(m_try_t state)
m_global_error_list = state->next;
}
#if defined(__GNUC__)
#if __GNUC__ >= 12
_Pragma("GCC diagnostic pop")
#endif
#endif
// Implement the M_LET injection macros, so that the CLEAR operator is called on exception
//
@@ -495,7 +534,7 @@ m_try_jump_final(m_try_t state)
# error M*LIB: Internal error. C++ back-end requested within C implementation.
#elif M_USE_TRY_MECHANISM == 2
// Use of CLANG blocks
// Use of CLANG blocks, and register them as callback with a pointer to the variable
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
for(m_try_t M_C(m_try_state_, name); cont && \
@@ -507,7 +546,7 @@ m_try_jump_final(m_try_t state)
(void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) )
#elif M_USE_TRY_MECHANISM == 3
// Use of GCC nested functions.
// Use of GCC nested functions, and register them as callback with a pointer to the variable
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
for(m_try_t M_C(m_try_state_, name); cont && \
@@ -520,7 +559,7 @@ m_try_jump_final(m_try_t state)
#elif M_USE_TRY_MECHANISM == 4
// STD C compliant (without compiler extension): use of setjmp
// This is the basic implementation in case of compiler unknown.
// This is the basic implementation in case of unknown compiler.
// It uses setjmp/longjmp, and as such, is much slower than
// other implementations.
@@ -534,9 +573,8 @@ m_try_jump_final(m_try_t state)
// Register the stack frame and tests for the longjmp.
// In which case call the CLEAR operator, unstack the error list and rethrow the error.
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
for( ; cont ; m_try_jump_final(M_C(m_try_state_, name))) \
if (m_try_jump_post(M_C(m_try_state_, name)) \
|| (M_CALL_CLEAR(oplist, name), m_try_jump_final(M_C(m_try_state_, name)), m_rethrow(), false))
for(m_try_jump_post(M_C(m_try_state_, name)); cont ; m_try_jump_final(M_C(m_try_state_, name))) \
if (m_try_setjmp(M_C(m_try_state_, name)->data.buffer) != 0) { M_CALL_CLEAR(oplist, name); m_try_jump_final(M_C(m_try_state_, name)); m_rethrow(); } else
#else
# error M*LIB: Invalid value for M_USE_TRY_MECHANISM [1..4]
@@ -553,9 +591,8 @@ m_try_jump_final(m_try_t state)
// Register the stack frame and tests for the longjmp.
// In which case call the CLEAR operator, unstack the error list and rethrow the error.
#define M_DEFER_TRY_INJECT_POST_B(cont, ...) \
for( ; cont ; m_try_jump_final(M_C(m_try_state_, cont))) \
if (m_try_jump_post(M_C(m_try_state_, cont)) \
|| (__VA_ARGS__ , m_try_jump_final(M_C(m_try_state_, cont)), m_rethrow(), false))
for(m_try_jump_post(M_C(m_try_state_, cont)); cont ; m_try_jump_final(M_C(m_try_state_, cont))) \
if (m_try_setjmp(M_C(m_try_state_, cont)->data.buffer) != 0) { __VA_ARGS__; m_try_jump_final(M_C(m_try_state_, cont)); m_rethrow(); } else
#endif /* cplusplus */
@@ -583,11 +620,10 @@ m_try_jump_final(m_try_t state)
// In case of MEMORY FULL errors, throw an error instead of aborting.
#undef M_MEMORY_FULL
#define M_MEMORY_FULL(size) M_THROW(M_ERROR_MEMORY, (intptr_t)(size))
#define M_MEMORY_FULL(type, size) M_THROW(M_ERROR_MEMORY, (intptr_t)sizeof(type), (intptr_t)(size))
// Add attribute volatile if exceptions are enabled
#undef m_volatile
#define m_volatile volatile
#endif
+68 -39
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - TUPLE module
*
* Copyright (c) 2017-2024, 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:
@@ -243,7 +243,8 @@ namespace m_lib {
/* Define the INIT method calling the INIT method for all params */
#define M_TUPL3_DEFINE_INIT(name, ...) \
M_INLINE void M_F(name, _init)(M_F(name,_ct) my) { \
M_P(void, name, _init, M_F(name,_ct) my) { \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_INIT_FUNC , __VA_ARGS__) {} \
}
@@ -252,8 +253,9 @@ namespace m_lib {
/* Define the INIT_SET method calling the INIT_SET method for all params */
#define M_TUPL3_DEFINE_INIT_SET(name, ...) \
M_INLINE void M_F(name, _init_set)(M_F(name,_ct) my , M_F(name,_ct) const org) { \
M_P(void, name, _init_set, M_F(name,_ct) my , M_F(name,_ct) const org) { \
M_TUPL3_CONTRACT(org); \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_INIT_SET_FUNC , __VA_ARGS__) {} \
}
#define M_TUPL3_DEFINE_INIT_SET_FUNC(a) \
@@ -262,15 +264,11 @@ namespace m_lib {
/* Define the INIT_WITH method calling the INIT_SET method for all params. */
#define M_TUPL3_DEFINE_INIT_SET2(name, ...) \
M_INLINE void M_F(name, _init_emplace)(M_F(name,_ct) my \
M_MAP(M_TUPL3_DEFINE_INIT_SET2_PROTO, __VA_ARGS__) \
) { \
M_P(void, name, _init_emplace, M_F(name,_ct) my M_MAP(M_TUPL3_DEFINE_SET2_PROTO, __VA_ARGS__) ) { \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_INIT_SET2_FUNC , __VA_ARGS__) {} \
}
#define M_TUPL3_DEFINE_INIT_SET2_PROTO(a) \
, M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD a
#define M_TUPL3_DEFINE_INIT_SET2_FUNC(a) \
M_CHAIN_OBJ(M_TUPL3_GET_FIELD a, M_TUPL3_GET_OPLIST a, \
my -> M_TUPL3_GET_FIELD a , M_TUPL3_GET_FIELD a )
@@ -278,10 +276,10 @@ namespace m_lib {
/* Define the SET method calling the SET method for all params. */
#define M_TUPL3_DEFINE_SET(name, ...) \
M_INLINE void M_F(name, _set)(M_F(name,_ct) my , \
M_F(name,_ct) const org) { \
M_P(void, name, _set, M_F(name,_ct) my, M_F(name,_ct) const org) { \
M_TUPL3_CONTRACT(my); \
M_TUPL3_CONTRACT(org); \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_SET_FUNC , __VA_ARGS__) \
}
@@ -291,10 +289,9 @@ namespace m_lib {
/* Define the SET_WITH method calling the SET method for all params. */
#define M_TUPL3_DEFINE_SET2(name, ...) \
M_INLINE void M_F(name, _emplace)(M_F(name,_ct) my \
M_MAP(M_TUPL3_DEFINE_SET2_PROTO, __VA_ARGS__) \
) { \
M_P(void, name, _emplace, M_F(name,_ct) my M_MAP(M_TUPL3_DEFINE_SET2_PROTO, __VA_ARGS__) ) { \
M_TUPL3_CONTRACT(my); \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_SET2_FUNC , __VA_ARGS__) \
}
#define M_TUPL3_DEFINE_SET2_PROTO(a) \
@@ -306,8 +303,9 @@ namespace m_lib {
/* Define the CLEAR method calling the CLEAR method for all params. */
#define M_TUPL3_DEFINE_CLEAR(name, ...) \
M_INLINE void M_F(name, _clear)(M_F(name,_ct) my) { \
M_P(void, name, _clear, M_F(name,_ct) my) { \
M_TUPL3_CONTRACT(my); \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_CLEAR_FUNC , __VA_ARGS__) \
}
@@ -343,8 +341,9 @@ namespace m_lib {
#define M_TUPL3_DEFINE_SETTER_FIELD_PROTO(name, a) \
M_INLINE void M_C3(name, _set_, M_TUPL3_GET_FIELD a) \
(M_F(name,_ct) my, M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD a) { \
(M_P_EXPAND M_F(name,_ct) my, M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD a) { \
M_TUPL3_CONTRACT(my); \
M_UNUSED_CONTEXT(); \
M_TUPL3_CALL_SET(a, my ->M_TUPL3_GET_FIELD a, M_TUPL3_GET_FIELD a); \
}
@@ -360,9 +359,10 @@ namespace m_lib {
#define M_TUPL3_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t v \
function_name(M_P_EXPAND name_t v \
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \
M_UNUSED_CONTEXT(); \
M_CALL_CLEAR(oplist, v->name); \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, v->name, exp_emplace_type); \
}
@@ -468,27 +468,26 @@ namespace m_lib {
/* Define a GET_STR method by calling the GET_STR methods for all params */
#define M_TUPL3_DEFINE_GET_STR(name, ...) \
M_INLINE void M_F(name, _get_str)(m_string_t str, \
M_F(name,_ct) const el, \
bool append) { \
M_P(void, name, _get_str, m_string_t str, M_F(name,_ct) const el, bool append) \
{ \
bool comma = false; \
M_TUPL3_CONTRACT(el); \
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, "("); \
M_MAP(M_TUPL3_DEFINE_GET_STR_FUNC , __VA_ARGS__) \
m_string_push_back (str, ')'); \
m_string_push_back M_R(str, ')'); \
}
#define M_TUPL3_DEFINE_GET_STR_FUNC(a) \
if (comma) m_string_push_back (str, ','); \
if (comma) m_string_push_back M_R(str, ','); \
comma = true; \
M_TUPL3_CALL_GET_STR(a, str, el -> M_TUPL3_GET_FIELD a, true); \
/* Define a OUT_STR method by calling the OUT_STR methods for all params */
#define M_TUPL3_DEFINE_OUT_STR(name, ...) \
M_INLINE void M_F(name, _out_str)(FILE *f, \
M_F(name,_ct) const el) { \
M_INLINE void M_F(name, _out_str)(FILE *f, M_F(name,_ct) const el) \
{ \
bool comma = false; \
M_TUPL3_CONTRACT(el); \
M_ASSERT (f != NULL); \
@@ -505,9 +504,11 @@ namespace m_lib {
/* Define a IN_STR method by calling the IN_STR methods for all params */
#define M_TUPL3_DEFINE_IN_STR(name, ...) \
M_INLINE bool M_F(name, _in_str)(M_F(name,_ct) el, FILE *f) { \
M_P(bool, name, _in_str, M_F(name,_ct) el, FILE *f) \
{ \
bool comma = false; \
M_TUPL3_CONTRACT(el); \
M_UNUSED_CONTEXT(); \
M_ASSERT (f != NULL); \
int c = fgetc(f); \
if (c != '(') return false; \
@@ -528,11 +529,11 @@ namespace m_lib {
/* Define a PARSE_STR method by calling the PARSE_STR methods for all params */
#define M_TUPL3_DEFINE_PARSE_STR(name, ...) \
M_INLINE bool M_F(name, _parse_str)(M_F(name,_ct) el, \
const char str[], \
const char **endptr) { \
M_P(bool, name, _parse_str, M_F(name,_ct) el, const char str[], const char **endptr) \
{ \
M_TUPL3_CONTRACT(el); \
M_ASSERT (str != NULL); \
M_UNUSED_CONTEXT(); \
bool success = false; \
bool comma = false; \
int c = *str++; \
@@ -562,9 +563,8 @@ namespace m_lib {
/* Define a OUT_SERIAL method by calling the OUT_SERIAL methods for all params */
#define M_TUPL3_DEFINE_OUT_SERIAL(name, ...) \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, \
M_F(name,_ct) const el) { \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, M_F(name,_ct) const el) \
{ \
M_TUPL3_CONTRACT(el); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
const int field_max = M_NARGS(__VA_ARGS__); \
@@ -574,24 +574,25 @@ namespace m_lib {
int index = 0; \
m_serial_local_t local; \
m_serial_return_code_t ret; \
ret = f->m_interface->write_tuple_start(local, f); \
ret = f->m_interface->write_tuple_start M_R(local, f); \
M_MAP(M_TUPL3_DEFINE_OUT_SERIAL_FUNC , __VA_ARGS__) \
M_ASSERT( index == field_max); \
ret |= f->m_interface->write_tuple_end(local, f); \
ret |= f->m_interface->write_tuple_end M_R(local, f); \
return ret & M_SERIAL_FAIL; \
}
#define M_TUPL3_DEFINE_OUT_SERIAL_FUNC(a) \
f->m_interface->write_tuple_id(local, f, field_name, field_max, index); \
f->m_interface->write_tuple_id M_R(local, f, field_name, field_max, index); \
M_TUPL3_CALL_OUT_SERIAL(a, f, el -> M_TUPL3_GET_FIELD a); \
index++; \
/* Define a IN_SERIAL method by calling the IN_SERIAL methods for all params */
#define M_TUPL3_DEFINE_IN_SERIAL(name, ...) \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(M_F(name,_ct) el, m_serial_read_t f) { \
M_P(m_serial_return_code_t, name, _in_serial, M_F(name,_ct) el, m_serial_read_t f) \
{ \
M_TUPL3_CONTRACT(el); \
M_UNUSED_CONTEXT(); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
int index = -1; \
const int field_max = M_NARGS(__VA_ARGS__); \
@@ -634,8 +635,9 @@ namespace m_lib {
/* Define a MOVE method by calling the MOVE methods for all params */
#define M_TUPL3_DEFINE_MOVE(name, ...) \
M_INLINE void M_F(name, _move)(M_F(name,_ct) el, M_F(name,_ct) org) { \
M_P(void, name, _move, M_F(name,_ct) el, M_F(name,_ct) org) { \
M_TUPL3_CONTRACT(el); \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_MOVE_FUNC , __VA_ARGS__) \
}
@@ -657,8 +659,9 @@ namespace m_lib {
/* Define a RESET method by calling the RESET methods for all params */
#define M_TUPL3_DEFINE_RESET(name, ...) \
M_INLINE void M_F(name, _reset)(M_F(name,_ct) el1) { \
M_P(void, name, _reset, M_F(name,_ct) el1) { \
M_TUPL3_CONTRACT(el1); \
M_UNUSED_CONTEXT(); \
M_MAP(M_TUPL3_DEFINE_RESET_FUNC , __VA_ARGS__) \
} \
@@ -743,6 +746,7 @@ namespace m_lib {
((M_LIB_ERROR(ONE_ARGUMENT_OF_M_TUPL3_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__)))
/* Define the TUPLE oplist */
#ifndef M_USE_CONTEXT
#define M_TUPL3_OPLIST_P3(name, ...) \
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \
INIT_SET(M_F(name, _init_set)), \
@@ -767,6 +771,31 @@ namespace m_lib {
M_IF_METHOD_ALL(RESET, __VA_ARGS__)(RESET(M_F(name, _reset)),), \
EMPLACE_TYPE( ( M_REDUCE2(M_TUPL3_OPLIST_SUBTYPE, M_ID, name, M_SEQ(1, M_NARGS(__VA_ARGS__))) ) ) \
)
#else
#define M_TUPL3_OPLIST_P3(name, ...) \
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(API_0P(INIT(M_F(name,_init))),), \
INIT_SET(API_0P(M_F(name, _init_set))), \
INIT_WITH(API_1(M_TUPL3_INIT_WITH)), \
SET(API_0P(M_F(name,_set))), \
CLEAR(API_0P(M_F(name, _clear))), \
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
OPLIST( (__VA_ARGS__) ), \
M_IF_METHOD_ALL(CMP, __VA_ARGS__)(CMP(M_F(name, _cmp)),), \
M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_F(name, _hash)),), \
M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(API_0P(M_F(name, _get_str))),), \
M_IF_METHOD_ALL(PARSE_STR, __VA_ARGS__)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(API_0P(M_F(name, _in_str))),), \
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_F(name, _out_str)),), \
M_IF_METHOD_ALL(IN_SERIAL, __VA_ARGS__)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_F(name, _init_move)),), \
M_IF_METHOD_ALL(MOVE, __VA_ARGS__)(MOVE(API_0P(M_F(name, _move))),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \
M_IF_METHOD_ALL(RESET, __VA_ARGS__)(RESET(API_0P(M_F(name, _reset))),), \
EMPLACE_TYPE( ( M_REDUCE2(M_TUPL3_OPLIST_SUBTYPE, M_ID, name, M_SEQ(1, M_NARGS(__VA_ARGS__))) ) ) \
)
#endif
/* Support for EMPLACE_TYPE in OPLIST. It refers the created internal type alias */
#define M_TUPL3_OPLIST_SUBTYPE(name, num) \
+100 -59
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - VARIANT module
*
* Copyright (c) 2017-2024, 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:
@@ -194,9 +194,10 @@
typedef \
M_IF_METHOD(GENTYPE, M_VAR1ANT_GET_OPLIST a)( M_GET_GENTYPE M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_GET_TYPE a) \
M_C4(m_var1ant_, name, _gentype_, num); \
typedef const \
M_IF_METHOD(GENTYPE, M_VAR1ANT_GET_OPLIST a)( M_GET_GENTYPE M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_GET_TYPE a) \
M_C4(m_var1ant_, name, _const_gentype_, num);
typedef \
M_IF_METHOD(GENTYPE, M_VAR1ANT_GET_OPLIST a)( const M_GET_GENTYPE M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_GET_TYPE a const) \
M_C4(m_var1ant_, name, _const_gentype_, num); \
typedef M_VAR1ANT_GET_TYPE a M_C4(m_var1ant_, name, _type_, M_VAR1ANT_GET_FIELD a);
#define M_VAR1ANT_DEFINE_UNION_ELE(name, a) \
, M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value)
@@ -223,15 +224,17 @@
/* Define the INIT_SET function. */
#define M_VAR1ANT_DEFINE_INIT_SET(name, ...) \
M_INLINE void M_F(name, _init_set)(M_F(name,_ct) my , \
M_F(name,_ct) const org) { \
M_P(void, name, _init_set, M_F(name,_ct) my, M_F(name,_ct) const org) \
{ \
M_VAR1ANT_CONTRACT(name, org); \
M_UNUSED_CONTEXT(); \
my->type = org->type; \
switch (org->type) { \
M_MAP2(M_VAR1ANT_DEFINE_INIT_SET_FUNC, name, __VA_ARGS__) \
case M_F(name, _EMPTY): /* fallthrough */ \
default: M_ASSUME(org->type == M_F(name, _EMPTY)); break; \
} \
M_VAR1ANT_CONTRACT(name, my); \
}
#define M_VAR1ANT_DEFINE_INIT_SET_FUNC(name, a) \
@@ -243,14 +246,14 @@
/* Define the SET function. */
#define M_VAR1ANT_DEFINE_SET(name, ...) \
M_INLINE void M_F(name, _set)(M_F(name,_ct) my , \
M_F(name,_ct) const org) { \
M_P(void, name, _set, M_F(name,_ct) my, M_F(name,_ct) const org) \
{ \
M_VAR1ANT_CONTRACT(name, my); \
M_VAR1ANT_CONTRACT(name, org); \
if (my->type != org->type) { \
/* Different types: clear previous one and create new */ \
M_F(name, _clear)(my); \
M_F(name, _init_set)(my, org); \
M_F(name, _clear)M_R(my); \
M_F(name, _init_set)M_R(my, org); \
} else { \
/* Same type: optimize the set */ \
switch (org->type) { \
@@ -259,6 +262,8 @@
default: M_ASSUME(org->type == M_F(name, _EMPTY)); break; \
} \
} \
M_ASSERT( my->type == org->type ); \
M_VAR1ANT_CONTRACT(name, my); \
}
#define M_VAR1ANT_DEFINE_SET_FUNC(name, a) \
@@ -270,8 +275,10 @@
/* Define the CLEAR function. */
#define M_VAR1ANT_DEFINE_CLEAR(name, ...) \
M_INLINE void M_F(name, _clear)(M_F(name,_ct) my) { \
M_P(void, name, _clear, M_F(name,_ct) my) \
{ \
M_VAR1ANT_CONTRACT(name, my); \
M_UNUSED_CONTEXT(); \
switch (my->type) { \
M_MAP2(M_VAR1ANT_DEFINE_CLEAR_FUNC, name, __VA_ARGS__) \
case M_F(name, _EMPTY): /* fallthrough */ \
@@ -313,7 +320,9 @@
#define M_VAR1ANT_DEFINE_INIT_FIELD_FUNC(name, a) \
M_INLINE void \
M_C3(name, _init_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my) { \
M_C3(name, _init_, M_VAR1ANT_GET_FIELD a)(M_P_EXPAND M_F(name,_ct) my) \
{ \
M_UNUSED_CONTEXT(); \
/* Reinit variable with the given value */ \
my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
M_VAR1ANT_CALL_INIT(a, my -> value. M_VAR1ANT_GET_FIELD a); \
@@ -326,15 +335,17 @@
#define M_VAR1ANT_DEFINE_INIT_SETTER_FIELD_FUNC(name, num, a) \
M_INLINE void \
M_C3(name, _init_set_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \
M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a ) { \
M_C3(name, _init_set_, M_VAR1ANT_GET_FIELD a)(M_P_EXPAND M_F(name,_ct) my, M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a) \
{ \
M_UNUSED_CONTEXT(); \
my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \
M_VAR1ANT_GET_FIELD a); \
} \
M_INLINE void \
M_C4(m_var1ant_, name, _init_set_, num)(M_F(name,_ct) my, \
M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a ) { \
M_C4(m_var1ant_, name, _init_set_, num)(M_P_EXPAND M_F(name,_ct) my, M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a) \
{ \
M_UNUSED_CONTEXT(); \
my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \
M_VAR1ANT_GET_FIELD a); \
@@ -347,14 +358,14 @@
#define M_VAR1ANT_DEFINE_SETTER_FIELD_FUNC(name, a) \
M_INLINE void \
M_C3(name, _set_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \
M_C3(name, _set_, M_VAR1ANT_GET_FIELD a)(M_P_EXPAND M_F(name,_ct) my, \
M_VAR1ANT_GET_TYPE a const M_VAR1ANT_GET_FIELD a ) { \
M_VAR1ANT_CONTRACT(name, my); \
if (my->type == M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) ) { \
M_VAR1ANT_CALL_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \
M_VAR1ANT_GET_FIELD a); \
} else { \
M_F(name, _clear)(my); \
M_F(name, _clear)M_R(my); \
/* Reinit variable with the given value */ \
my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \
@@ -402,8 +413,7 @@
#define M_VAR1ANT_DEFINE_INIT_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t my \
M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \
function_name(M_P_EXPAND name_t my M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \
{ \
my->type = M_C4(M_PAIR_1 name, _, M_PAIR_2 name, _value); \
M_EMPLACE_CALL_FUNC(ab, init_func, oplist, my -> value. M_PAIR_2 name, exp_emplace_type); \
@@ -411,13 +421,13 @@ M_INLINE void \
#define M_VAR1ANT_DEFINE_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_INLINE void \
function_name(name_t my \
M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \
function_name(M_P_EXPAND name_t my M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \
{ \
/* No optimization done */ \
M_C(M_PAIR_1 name, _clear)(my); \
my->type = M_C4(M_PAIR_1 name, _, M_PAIR_2 name, _value); \
M_C(M_PAIR_1 name, _clear)M_R(my); \
my->type = M_F(M_PAIR_1 name, _EMPTY); \
M_EMPLACE_CALL_FUNC(ab, init_func, oplist, my -> value. M_PAIR_2 name, exp_emplace_type); \
my->type = M_C4(M_PAIR_1 name, _, M_PAIR_2 name, _value); \
} \
/* Define the EQUAL_P function. */
@@ -490,11 +500,9 @@ M_INLINE void \
It can be optimized if both types are the same.
*/
#define M_VAR1ANT_DEFINE_MOVE(name, ...) \
M_INLINE void \
M_F(name, _move)(M_F(name,_ct) el, M_F(name,_ct) org) { \
M_VAR1ANT_CONTRACT(name, el); \
M_VAR1ANT_CONTRACT(name, org); \
M_F(name, _clear)(el); \
M_P(void, name, _move, M_F(name,_ct) el, M_F(name,_ct) org) \
{ \
M_F(name, _clear)M_R(el); \
M_F(name, _init_move)(el , org); \
}
@@ -505,10 +513,10 @@ M_INLINE void \
#define M_VAR1ANT_DEFINE_MOVER_FUNC(name, a) \
M_INLINE void \
M_C3(name, _move_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \
M_C3(name, _move_, M_VAR1ANT_GET_FIELD a)(M_P_EXPAND M_F(name,_ct) my, \
M_VAR1ANT_GET_TYPE a M_VAR1ANT_GET_FIELD a ) { \
M_VAR1ANT_CONTRACT(name, my); \
M_F(name, _clear)(my); \
M_F(name, _clear)M_R(my); \
/* Reinit variable with the given value */ \
my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
M_VAR1ANT_CALL_INIT_MOVE(a, my -> value. M_VAR1ANT_GET_FIELD a, \
@@ -554,40 +562,38 @@ M_INLINE void \
/* Define the GET_STR function */
#define M_VAR1ANT_DEFINE_GET_STR(name, ...) \
M_INLINE void M_F(name, _get_str)(m_string_t str, \
M_F(name,_ct) const el, \
bool append) { \
M_P(void, name, _get_str, m_string_t str, M_F(name,_ct) const el, bool append) \
{ \
M_VAR1ANT_CONTRACT(name, el); \
M_ASSERT (str != NULL); \
void (*func)(m_string_t, const char *); \
void (*func)(M_P_EXPAND m_string_t, const char *); \
func = append ? m_string_cat_cstr : m_string_set_cstr; \
switch (el->type) { \
case M_F(name, _EMPTY): func(str, "@EMPTY@"); break; \
case M_F(name, _EMPTY): func M_R(str, "@EMPTY@"); break; \
M_MAP2(M_VAR1ANT_DEFINE_GET_STR_FUNC , name, __VA_ARGS__) \
default: M_ASSUME(false); break; \
} \
m_string_push_back (str, '@'); \
m_string_push_back M_R(str, '@'); \
}
#define M_VAR1ANT_DEFINE_GET_STR_FUNC(name, a) \
case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \
func(str, "@" M_AS_STR(M_VAR1ANT_GET_FIELD a) "@"); \
func M_R(str, "@" M_AS_STR(M_VAR1ANT_GET_FIELD a) "@"); \
M_VAR1ANT_CALL_GET_STR(a, str, el -> value . M_VAR1ANT_GET_FIELD a, true); \
break;
/* Define the PARSE_STR function */
#define M_VAR1ANT_DEFINE_PARSE_STR(name, ...) \
M_INLINE bool M_F(name, _parse_str)(M_F(name,_ct) el, \
const char str[], \
const char **endp) { \
M_P(bool, name, _parse_str, M_F(name,_ct) el, const char str[], const char **endp) \
{ \
M_VAR1ANT_CONTRACT(name, el); \
M_ASSERT (str != NULL); \
bool success = false; \
char variantTypeBuf[M_USE_IDENTIFIER_ALLOC+1]; \
int c = *str++; \
unsigned int i = 0; \
M_F(name, _reset)(el); \
M_F(name, _reset)M_R(el); \
if (c != '@') goto exit; \
/* First read the name of the type */ \
c = *str++; \
@@ -621,8 +627,8 @@ M_INLINE void \
/* Define the OUT_STR function */
#define M_VAR1ANT_DEFINE_OUT_STR(name, ...) \
M_INLINE void M_F(name, _out_str)(FILE *f, \
M_F(name,_ct) const el) { \
M_INLINE void M_F(name, _out_str)(FILE *f, M_F(name,_ct) const el) \
{ \
M_VAR1ANT_CONTRACT(name, el); \
M_ASSERT (f != NULL); \
switch (el->type) { \
@@ -642,12 +648,12 @@ M_INLINE void \
/* Define the IN_STR function */
#define M_VAR1ANT_DEFINE_IN_STR(name, ...) \
M_INLINE bool M_F(name, _in_str)(M_F(name,_ct) el, \
FILE *f) { \
M_P(bool, name, _in_str, M_F(name,_ct) el, FILE *f) \
{ \
M_VAR1ANT_CONTRACT(name, el); \
M_ASSERT (f != NULL); \
char variantTypeBuf[M_USE_IDENTIFIER_ALLOC+1]; \
M_F(name, _reset)(el); \
M_F(name, _reset)M_R(el); \
if (fgetc(f) != '@') return false; \
/* First read the name of the type */ \
bool b = true; \
@@ -684,9 +690,8 @@ M_INLINE void \
/* Define the OUT_SERIAL function */
#define M_VAR1ANT_DEFINE_OUT_SERIAL(name, ...) \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, \
M_F(name,_ct) const el) { \
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, M_F(name,_ct) const el) \
{ \
M_VAR1ANT_CONTRACT(name, el); \
const int field_max = M_NARGS(__VA_ARGS__); \
static const char *const field_name[] = \
@@ -696,18 +701,18 @@ M_INLINE void \
m_serial_return_code_t ret; \
switch (el->type) { \
case M_F(name, _EMPTY): \
return f->m_interface->write_variant_start(local, f, field_name, field_max, -1); \
return f->m_interface->write_variant_start M_R(local, f, field_name, field_max, -1); \
break; \
M_MAP2(M_VAR1ANT_DEFINE_OUT_SERIAL_FUNC , name, __VA_ARGS__) \
default: M_ASSUME(false); break; \
} \
ret |= f->m_interface->write_variant_end(local, f); \
ret |= f->m_interface->write_variant_end M_R(local, f); \
return ret & M_SERIAL_FAIL; \
}
#define M_VAR1ANT_DEFINE_OUT_SERIAL_FUNC(name, a) \
case M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value): \
ret = f->m_interface->write_variant_start(local, f, field_name, field_max, \
ret = f->m_interface->write_variant_start M_R(local, f, field_name, field_max, \
M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) -1); \
M_VAR1ANT_CALL_OUT_SERIAL(a, f, el -> value . M_VAR1ANT_GET_FIELD a); \
break;
@@ -715,9 +720,8 @@ M_INLINE void \
/* Define the IN_SERIAL function */
#define M_VAR1ANT_DEFINE_IN_SERIAL(name, ...) \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(M_F(name,_ct) el, \
m_serial_read_t f) { \
M_P(m_serial_return_code_t, name, _in_serial, M_F(name,_ct) el, m_serial_read_t f) \
{ \
M_VAR1ANT_CONTRACT(name, el); \
const int field_max = M_NARGS(__VA_ARGS__); \
static const char *const field_name[] = \
@@ -726,7 +730,7 @@ M_INLINE void \
m_serial_local_t local; \
m_serial_return_code_t ret; \
int id = -1; \
M_F(name, _reset)(el); \
M_F(name, _reset)M_R(el); \
ret = f->m_interface->read_variant_start(local, f, field_name, field_max, &id); \
if (ret != M_SERIAL_OK_CONTINUE) return ret; \
M_ASSERT (id >= 0 && id < field_max); \
@@ -749,10 +753,10 @@ M_INLINE void \
/* Define the RESET function */
#define M_VAR1ANT_DEFINE_RESET_FUNC(name, ...) \
M_INLINE void M_F(name, _reset)(M_F(name,_ct) my) \
M_P(void, name, _reset, M_F(name,_ct) my) \
{ \
M_VAR1ANT_CONTRACT(name, my); \
M_F(name, _clear)(my); \
M_F(name, _clear)M_R(my); \
M_F(name, _init)(my); \
} \
@@ -771,6 +775,7 @@ M_INLINE void \
((M_LIB_ERROR(ONE_ARGUMENT_OF_VARIANT_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__)))
/* Define the oplist */
#ifndef M_USE_CONTEXT
#define M_VAR1ANT_OPLIST_P3(name, ...) \
(INIT(M_F(name,_init)), \
INIT_SET(M_F(name, _init_set)), \
@@ -793,7 +798,43 @@ M_INLINE void \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_F(name, _init_move)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(MOVE(M_F(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \
M_IF_METHOD_ALL(FIELD, __VA_ARGS__)(M_VAR1ANT_OPLIST_EMPLACE_TYPE,M_EAT)(name, __VA_ARGS__), \
)
#else
#define M_VAR1ANT_OPLIST_P3(name, ...) \
(INIT(M_F(name,_init)), \
INIT_SET(API_0P(M_F(name, _init_set))), \
SET(API_0P(M_F(name,_set))), \
CLEAR(API_0P(M_F(name, _clear))), \
RESET(API_0P(M_F(name, _reset))), \
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
EMPTY_P(M_F(name,_empty_p)), \
SIZE( M_NARGS(__VA_ARGS__) ), \
INIT_WITH( API_1(M_VAR1ANT_INIT_WITH) ), \
M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_F(name, _hash)),), \
M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_F(name, _equal_p)),), \
M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(API_0P(M_F(name, _get_str))),), \
M_IF_METHOD2_ALL(PARSE_STR, INIT, __VA_ARGS__)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
M_IF_METHOD2_ALL(IN_STR, INIT, __VA_ARGS__)(IN_STR(API_0P(M_F(name, _in_str))),), \
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_F(name, _out_str)),), \
M_IF_METHOD2_ALL(IN_SERIAL, INIT, __VA_ARGS__)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_F(name, _init_move)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(MOVE(API_0P(M_F(name, _move))),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \
M_IF_METHOD_ALL(FIELD, __VA_ARGS__)(M_VAR1ANT_OPLIST_EMPLACE_TYPE,M_EAT)(name, __VA_ARGS__), \
)
#endif
/* Expand to an EMPLACE_TYPE compatible with OPLIST definition
(Use M_REDUCE2 which is already used by M_IF_METHOD_ALL)
*/
#define M_VAR1ANT_OPLIST_EMPLACE_TYPE(name, ...) EMPLACE_TYPE( LIST( \
M_REDUCE2(M_VAR1ANT_OPLIST_EMPLACE_TYPE_EXPAND, M_ID, name, __VA_ARGS__) \
))
#define M_VAR1ANT_OPLIST_EMPLACE_TYPE_EXPAND(name, oplist) \
( M_C(_, M_GET_FIELD oplist), M_C3(name, _init_set_, M_GET_FIELD oplist), \
M_C4(m_var1ant_, name, _type_, M_GET_FIELD oplist) )
/********************************** INTERNAL *********************************/
+26 -19
View File
@@ -1,7 +1,7 @@
/*
* M*LIB / WORKER - Extra worker interface
*
* Copyright (c) 2017-2024, 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:
@@ -142,8 +142,7 @@ typedef struct m_work3r_thread_s {
} m_work3r_thread_ct;
/* Definition of the queue that will record the work orders */
BUFFER_DEF(m_work3r_queue, m_work3r_order_ct, 0,
BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, M_WORK3R_OPLIST)
M_BUFFER_DEF(m_work3r_queue, m_work3r_order_ct, 0, M_BUFFER_QUEUE|M_BUFFER_DEFERRED_POP, M_WORK3R_OPLIST)
/* Definition the global pool of workers */
typedef struct m_worker_s {
@@ -235,9 +234,10 @@ typedef struct m_worker_sync_s {
M_INLINE void \
M_C3(m_work3r_, name, _clear)(struct M_C3(m_worker_, name, _s) *p) \
{ \
M_GLOBAL_CONTEXT(); \
M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_CLEAR, data, __VA_ARGS__) \
/* TODO: Overload */ \
M_MEMORY_DEL(p); \
M_MEMORY_DEL(m_context, p); \
} \
\
M_INLINE void \
@@ -263,15 +263,16 @@ typedef struct m_worker_sync_s {
M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD, data, __VA_ARGS__) \
) \
{ \
M_GLOBAL_CONTEXT(); \
if (!m_work3r_queue_full_p(block->worker->queue_g)) { \
struct M_C3(m_worker_, name, _s) *p = M_MEMORY_ALLOC ( struct M_C3(m_worker_, name, _s)); \
struct M_C3(m_worker_, name, _s) *p = M_MEMORY_ALLOC (m_context, struct M_C3(m_worker_, name, _s)); \
if (M_UNLIKELY_NOMEM(p == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_C3(m_worker_, name, _s))); \
M_MEMORY_FULL(struct M_C3(m_worker_, name, _s), 1); \
} \
p->callback = callback; \
M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_COPY, data, __VA_ARGS__) \
M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_INIT_SET, data, __VA_ARGS__) \
const m_work3r_order_ct w = { block, p, M_C3(m_work3r_, name, _callback) M_WORK3R_EXTRA_ORDER }; \
if (m_work3r_queue_push (block->worker->queue_g, w) == true) { \
if (m_work3r_queue_push_blocking (block->worker->queue_g, w, false) == true) { \
atomic_fetch_add (&block->num_spawn, 1); \
return; \
} \
@@ -288,7 +289,7 @@ typedef struct m_worker_sync_s {
#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD(data, num, oplist) \
M_GET_TYPE oplist M_C(param, num)
#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_COPY(data, num, oplist) \
#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_INIT_SET(data, num, oplist) \
M_CALL_INIT_SET(oplist, p-> M_C(field, num), M_C(param, num) );
#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_ALONE(data, num, oplist) \
@@ -361,6 +362,7 @@ m_work3r_thread(void *arg)
{
// Get back the given argument
struct m_worker_s *g = M_ASSIGN_CAST(struct m_worker_s *, arg);
M_GLOBAL_CONTEXT();
while (true) {
m_work3r_order_ct w;
// If needed, reset the global state of the worker
@@ -369,7 +371,7 @@ m_work3r_thread(void *arg)
}
// Waiting for data
M_WORK3R_DEBUG ("Waiting for data (queue: %lu / %lu)\n", m_work3r_queue_size(g->queue_g), m_work3r_queue_capacity(g->queue_g));
m_work3r_queue_pop(&w, g->queue_g);
m_work3r_queue_pop (&w, g->queue_g);
// We received a work order
// Note: that the work order is still present in the queue
// preventing further work order to be pushed in the queue until it finishes doing the work
@@ -409,12 +411,12 @@ m_worker_init(m_worker_t g, int numWorker, unsigned int extraQueue, void (*reset
// numWorker can still be 0 if it is a single core cpu (no worker available)
M_ASSERT(numWorker >= 0);
size_t numWorker_st = (size_t) numWorker;
g->worker = M_MEMORY_REALLOC(m_work3r_thread_ct, NULL, numWorker_st);
M_GLOBAL_CONTEXT();
g->worker = M_MEMORY_REALLOC(m_context, m_work3r_thread_ct, NULL, 0, numWorker_st);
if (M_UNLIKELY_NOMEM (g->worker == NULL)) {
M_MEMORY_FULL(sizeof (m_work3r_thread_ct) * numWorker_st);
return;
M_MEMORY_FULL(m_work3r_thread_ct, numWorker_st);
}
m_work3r_queue_init(g->queue_g, numWorker_st + extraQueue);
m_work3r_queue_init (g->queue_g, numWorker_st + extraQueue);
g->numWorker_g = (unsigned int) numWorker_st;
g->resetFunc_g = resetFunc;
g->clearFunc_g = clearFunc;
@@ -441,6 +443,7 @@ M_INLINE void
m_worker_clear(m_worker_t g)
{
M_ASSERT (m_work3r_queue_empty_p (g->queue_g));
M_GLOBAL_CONTEXT();
// Push the terminate order on the queue
for(unsigned int i = 0; i < g->numWorker_g; i++) {
m_work3r_order_ct w = M_WORK3R_EMPTY_ORDER;
@@ -454,10 +457,10 @@ m_worker_clear(m_worker_t g)
m_thread_join(g->worker[i].id);
}
// Clear memory
M_MEMORY_FREE(g->worker);
M_MEMORY_FREE(m_context, m_work3r_thread_ct, g->worker, g->numWorker_g);
m_mutex_clear(g->lock);
m_cond_clear(g->a_thread_ends);
m_work3r_queue_clear(g->queue_g);
m_work3r_queue_clear (g->queue_g);
}
/* Start a new collaboration between workers of pool 'g'
@@ -479,8 +482,9 @@ M_INLINE void
m_worker_spawn(m_worker_sync_t block, void (*func)(void *data), void *data)
{
const m_work3r_order_ct w = { block, data, func M_WORK3R_EXTRA_ORDER };
M_GLOBAL_CONTEXT();
if (M_UNLIKELY (!m_work3r_queue_full_p(block->worker->queue_g))
&& m_work3r_queue_push (block->worker->queue_g, w) == true) {
&& m_work3r_queue_push_blocking (block->worker->queue_g, w, false) == true) {
M_WORK3R_DEBUG ("Sending data to thread: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn);
atomic_fetch_add (&block->num_spawn, 1);
return;
@@ -497,8 +501,9 @@ M_INLINE void
m_work3r_spawn_block(m_worker_sync_t block, void (^func)(void *data), void *data)
{
const m_work3r_order_ct w = { block, data, NULL, func };
M_GLOBAL_CONTEXT();
if (M_UNLIKELY (!m_work3r_queue_full_p(block->worker->queue_g))
&& m_work3r_queue_push (block->worker->queue_g, w) == true) {
&& m_work3r_queue_push_blocking (block->worker->queue_g, w, false) == true) {
M_WORK3R_DEBUG ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn);
atomic_fetch_add (&block->num_spawn, 1);
return;
@@ -516,8 +521,9 @@ M_INLINE void
m_work3r_spawn_function(m_worker_sync_t block, std::function<void(void *data)> func, void *data)
{
const m_work3r_order_ct w = { block, data, NULL, func };
M_GLOBAL_CONTEXT();
if (M_UNLIKELY (!m_work3r_queue_full_p(block->worker->queue_g))
&& m_work3r_queue_push (block->worker->queue_g, w) == true) {
&& m_work3r_queue_push_blocking (block->worker->queue_g, w, false) == true) {
M_WORK3R_DEBUG ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn);
atomic_fetch_add (&block->num_spawn, 1);
return;
@@ -558,6 +564,7 @@ M_INLINE void
m_worker_flush(m_worker_t g)
{
m_work3r_order_ct w;
M_GLOBAL_CONTEXT();
while (m_work3r_queue_pop_blocking (&w, g->queue_g, false) == true) {
m_work3r_exec(&w);
m_work3r_queue_pop_release(g->queue_g);
+1 -1
View File
@@ -1,4 +1,4 @@
REM Copyright (c) 2017-2024, Patrick Pelissier
REM Copyright (c) 2017-2025, Patrick Pelissier
REM All rights reserved.
REM
REM Redistribution and use in source and binary forms, with or without
+4 -1
View File
@@ -188,6 +188,7 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-ARRAY test-marray.c.c test-marray.synt \
M-BITSET ../m-bitset.h test-mbitset.synt \
M-BBPTREE test-mbptree.c test-mbptree.synt \
M-BSTRING ../m-bstring.h test-mbstring.synt \
M-BUFFER test-mbuffer.c.c test-mbuffer.synt \
M-CONCURRENT test-mconcurrent.c.c test-mconcurrent.synt \
M-CORE ../m-core.h test-mcore.synt \
@@ -197,18 +198,20 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-GENINT ../m-genint.h test-mgenint.synt \
M-I-LIST test-milist.c.c test-milist.synt \
M-I-SHARED test-mishared.c.c test-mishared.synt \
M-QUEUE test-mqueue.c.c test-mqueue.synt \
M-LIST test-mlist.c.c test-mlist.synt \
M-MEMPOOL test-mmempool.c.c test-mmempool.synt \
M-PRIOQUEUE test-mprioqueue.c.c test-mprioqueue.synt \
M-RBTREE test-mrbtree.c.c test-mrbtree.synt \
M-SERIAL-BIN ../m-serial-bin.h test-mserial-bin.synt \
M-SERIAL-JSON ../m-serial-json.h test-mserial-json.synt \
M-SHARED-PTR test-mshared-ptr.c.c test-mshared-ptr.synt \
M-SHARED test-mshared.c.c test-mshared.synt \
M-SNAPSHOT test-msnapshot.c.c test-msnapshot.synt \
M-STRING ../m-string.h test-mstring.synt \
M-TREE test-mtree.c.c test-mtree.synt \
M-THREAD ../m-thread.h test-mmutex.synt \
M-TRY ../m-try.h test-mtry.synt \
M-TRY ../m-try.h test-mtry.synt \
M-TUPLE test-mtuple.c.c test-mtuple.synt \
M-VARIANT test-mvariant.c.c test-mvariant.synt \
M-WORKER ../m-worker.h test-mworker.synt
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024-2024, Patrick Pelissier
* Copyright (c) 2021-2025-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:
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
@@ -56,4 +56,5 @@ void cmp_cont(deque_float_t a, std::deque<float> b)
#define DEFAULT_NUMBER 1000000
#define HAVE_PUSH_FRONT
#define HAVE_POP_FRONT
#define HAVE_POP_BACK
#include "check-generic.hpp"
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+10 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024 Patrick Pelissier
* Copyright (c) 2021-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:
@@ -134,7 +134,15 @@ void test(int select_test, int index,
break;
#endif
#if M_TEST_METHOD_P(POP, CONT_OPL)
#ifdef HAVE_POP_BACK
case 2:
if (!c1.empty()) {
M_C(CONT_NAME, _pop_back)(&b0, c0);
b1 = c1.back();
c1.pop_back();
}
break;
#elif M_TEST_METHOD_P(POP, CONT_OPL)
case 2:
if (!c1.empty()) {
M_CALL_POP(CONT_OPL, &b0, c0);
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024-2024, Patrick Pelissier
* Copyright (c) 2021-2025-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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Patrick Pelissier
* Copyright (c) 2021-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:
+4 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -125,6 +125,9 @@
#define M_TR33_CONTRACT(...) (void)0
#undef M_TR33_IT_CONTRACT
#define M_TR33_IT_CONTRACT(...) (void)0
#undef M_QUEU3_CONTRACT
#define M_QUEU3_CONTRACT(...) (void)0
/* Overwrite M_UNLIKELY_NOMEM so that it can never fails, to avoid spurious coverage */
#undef M_UNLIKELY_NOMEM
#define M_UNLIKELY_NOMEM(...) 0
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Test that the array module properly support exceptions
*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Test that the bitset module properly support exceptions
*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Test that the deque module properly support exceptions
*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Test that the list module properly support exceptions
*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Test that the string module properly support exceptions
*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,6 +20,7 @@
* (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 "test-obj.h"
#include "m-list.h"
#include "m-i-list.h"
#include "m-array.h"
@@ -29,8 +30,6 @@
#include "m-tuple.h"
#include "m-algo.h"
#include "test-obj.h"
typedef struct over_s {
unsigned long data;
ILIST_INTERFACE(ilist_over, over_s);
+4 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -33,9 +33,9 @@ END_COVERAGE
#define ARRAY_UINT_OPLIST ARRAY_OPLIST(array_uint)
// Array with the minimum number of methods.
ARRAY_DEF(array_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
ARRAY_DEF(array_min2_z, testobj_t, (INIT(testobj_init), CLEAR(testobj_clear)))
ARRAY_DEF(array_min3_z, testobj_t, (CLEAR(testobj_clear)))
ARRAY_DEF(array_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear), INIT_MOVE(M_COPY_A1_DEFAULT) ))
ARRAY_DEF(array_min2_z, testobj_t, (INIT(testobj_init), CLEAR(testobj_clear), INIT_MOVE(M_COPY_A1_DEFAULT) ) )
ARRAY_DEF(array_min3_z, testobj_t, (CLEAR(testobj_clear), INIT_MOVE(M_COPY_A1_DEFAULT)))
ARRAY_DEF(array_ulong, uint64_t)
ARRAY_DEF(array_string, string_t)
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,10 +20,9 @@
* (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 "test-obj.h"
#include "m-string.h"
#include "coverage.h"
#include "m-bitset.h"
static void test1(void)
+2 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,11 +20,9 @@
* (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 <stdlib.h>
#include "test-obj.h"
#include "m-string.h"
#include "m-bptree.h"
#include "test-obj.h"
#include "coverage.h"
START_COVERAGE
+324
View File
@@ -0,0 +1,324 @@
/*
* 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.
*/
#include "test-obj.h"
#include "coverage.h"
#include "m-bstring.h"
static void test0(void)
{
bstring_t b;
bstring_init(b);
assert( bstring_empty_p(b) );
assert( bstring_size(b) == 0);
assert( bstring_capacity(b) == 0);
assert( bstring_hash(b) != 0);
bstring_clear(b);
bstring_init(b);
assert( bstring_empty_p(b) );
bstring_push_back(b, 23);
assert( !bstring_empty_p(b) );
assert( bstring_size(b) == 1);
assert( bstring_capacity(b) != 0);
assert( bstring_hash(b) != 0);
bstring_reset(b);
assert( bstring_empty_p(b) );
assert( bstring_size(b) == 0);
assert( bstring_capacity(b) != 0);
assert( bstring_hash(b) != 0);
bstring_clear(b);
}
static void test1(void)
{
bstring_t b;
uint8_t u;
bstring_init(b);
bstring_push_back(b, 23);
assert( !bstring_empty_p(b) );
u = bstring_get_byte(b, 0);
assert(u == 23);
bstring_set_byte(b, 0, 24);
u = bstring_get_byte(b, 0);
assert(u == 24);
bstring_set_byte(b, 0, 25);
u = bstring_pop_back(b);
assert(u == 25);
for(int i = 0; i < 100; i++)
bstring_push_back(b, (uint8_t) i);
for(int i = 0; i < 100; i++) {
u = bstring_get_byte(b, (size_t) i);
assert(u == i);
}
for(int i = 0; i < 100; i++) {
u = bstring_pop_front(b);
assert(u == i);
}
assert( bstring_empty_p(b) );
assert( bstring_size(b) == 0 );
for(int i = 0; i < 100; i++)
bstring_push_back(b, (uint8_t) i);
for(int i = 99; i >= 0 ; i--) {
u = bstring_pop_back(b);
assert(u == i);
}
assert( bstring_empty_p(b) );
assert( bstring_size(b) == 0 );
bstring_resize(b, 400);
assert( !bstring_empty_p(b) );
assert( bstring_size(b) == 400 );
for(int i = 0; i < 400; i++) {
u = bstring_get_byte(b, (size_t) i);
assert(u == 0);
bstring_set_byte(b, (size_t) i, (uint8_t) i);
}
bstring_resize(b, 100);
assert( !bstring_empty_p(b) );
assert( bstring_size(b) == 100 );
for(int i = 0; i < 100; i++) {
u = bstring_get_byte(b, (size_t) i);
assert(u == i);
}
bstring_resize(b, 200);
assert( !bstring_empty_p(b) );
assert( bstring_size(b) == 200 );
for(int i = 0; i < 100; i++) {
u = bstring_get_byte(b, (size_t) i);
assert(u == i);
u = bstring_get_byte(b, (size_t) i+100);
assert(u == 0);
}
bstring_reserve(b, 0);
assert( bstring_size(b) == 200 );
bstring_resize(b, 0);
assert( bstring_size(b) == 0 );
assert( bstring_capacity(b) != 0 );
for(int i = 0; i < 1000; i++) {
bstring_push_back(b, (uint8_t) i);
u = bstring_pop_front(b);
assert(u == (uint8_t) i);
}
bstring_reserve(b, 0);
assert( bstring_size(b) == 0 );
assert( bstring_capacity(b) == 0 );
bstring_push_back(b, 23);
assert( !bstring_empty_p(b) );
bstring_reserve(b, 1000);
assert( bstring_size(b) == 1);
assert( bstring_capacity(b) == 1000 );
bstring_push_back(b, 25);
bstring_push_back(b, 26);
const uint8_t *p = bstring_view(b, 0, 3);
assert( p[0] == 23);
assert( p[1] == 25);
assert( p[2] == 26);
p = bstring_view(b, 1, 2);
assert( p[0] == 25);
assert( p[1] == 26);
uint8_t *w = bstring_acquire_access(b, 0, 3);
assert( w[0] == 23);
assert( w[1] == 25);
assert( w[2] == 26);
w[1] = 24;
bstring_release_access(b);
p = bstring_view(b, 1, 2);
assert( p[0] == 24);
assert( p[1] == 26);
bstring_clear(b);
}
static void test2(void)
{
bstring_t b1, b2;
bstring_init(b1);
for(int i = 0; i < 200; i++)
bstring_push_back(b1, (uint8_t) i);
bstring_init_set(b2, b1);
assert (bstring_equal_p(b1, b2));
assert (bstring_cmp(b1, b2) == 0);
bstring_push_back(b1, 200);;
assert (!bstring_equal_p(b1, b2));
assert (bstring_cmp(b1, b2) > 0);
bstring_push_back(b2, 201);;
assert (!bstring_equal_p(b1, b2));
assert (bstring_cmp(b1, b2) < 0);
bstring_set_byte(b2, 200, 200);
bstring_push_back(b2, 202);;
assert (!bstring_equal_p(b1, b2));
assert (bstring_cmp(b1, b2) < 0);
bstring_swap(b1, b2);
assert (bstring_cmp(b1, b2) > 0);
bstring_swap(b1, b2);
bstring_set(b1, b2);
assert (bstring_equal_p(b1, b2));
assert (bstring_cmp(b1, b2) == 0);
bstring_splice(b2, b1);
assert(bstring_size(b2) == 202*2);
assert(bstring_size(b1) == 0);
bstring_clear(b1);
bstring_init_move(b1, b2);
assert(bstring_size(b1) == 202*2);
bstring_init(b2);
bstring_move(b2,b1);
assert(bstring_size(b2) == 202*2);
bstring_clear(b2);
}
static void test3(void)
{
bstring_t b;
uint8_t tab1[] = { 1, 2, 3};
uint8_t tab2[] = { 4, 5, 6};
uint8_t tab3[] = { 1, 2, 3, 4, 5, 6};
uint8_t tab4[] = { 1, 2, 3, 4, 5, 6, 7};
uint8_t tmp[3];
bstring_init(b);
bstring_push_back_bytes(b, sizeof tab1, tab1);
assert( bstring_size(b) == sizeof tab1);
assert( bstring_equal_bytes_p(b, sizeof tab1, tab1) );
assert( bstring_cmp_bytes(b, sizeof tab1, tab1) == 0);
bstring_push_back_bytes(b, sizeof tab2, tab2);
assert( bstring_size(b) == sizeof tab1 + sizeof tab2);
assert( bstring_equal_bytes_p(b, sizeof tab3, tab3) );
assert( bstring_equal_bytes_p(b, 1, tab3) == false);
assert( bstring_cmp_bytes(b, sizeof tab3, tab3) == 0);
assert( bstring_cmp_bytes(b, 1, tab3) > 0);
assert( bstring_cmp_bytes(b, sizeof tab4, tab4) < 0);
assert( bstring_cmp_bytes(b, sizeof tab2, tab2) < 0);
bstring_pop_back_bytes(sizeof tmp, tmp, b);
assert(tmp[0] == 4 && tmp[1] == 5 && tmp[2] == 6);
assert( bstring_size(b) == 3);
bstring_pop_back_bytes(sizeof tmp, tmp, b);
assert(tmp[0] == 1 && tmp[1] == 2 && tmp[2] == 3);
assert( bstring_size(b) == 0);
bstring_push_back_bytes(b, sizeof tab3, tab3);
bstring_pop_front_bytes(sizeof tmp, tmp, b);
assert(tmp[0] == 1 && tmp[1] == 2 && tmp[2] == 3);
assert( bstring_size(b) == 3);
bstring_pop_front_bytes(sizeof tmp, tmp, b);
assert(tmp[0] == 4 && tmp[1] == 5 && tmp[2] == 6);
assert( bstring_size(b) == 0);
bstring_clear(b);
}
static void test4(void)
{
bstring_t b;
uint8_t tab1[] = { 1, 2, 6};
uint8_t tab2[] = { 3, 4, 5};
uint8_t tab3[] = { 1, 2, 3, 4, 5, 6};
uint8_t tmp[3];
bstring_init(b);
bstring_push_back_bytes(b, sizeof tab1, tab1);
bstring_push_bytes_at(b, 2, sizeof tab2, tab2);
assert(bstring_size(b) == 6);
assert( bstring_equal_bytes_p(b, sizeof tab3, tab3));
bstring_pop_bytes_at(3, tmp, b, 1);
assert(tmp[0] == 2 && tmp[1] == 3 && tmp[2] == 4);
assert(bstring_size(b) == 3);
bstring_clear(b);
}
static void test_io(void)
{
uint8_t tab1[] = { 1, 2, 3};
bstring_t b;
bstring_init(b);
FILE *f = fopen("a-mbstring.dat", "wb");
if (!f) abort();
size_t n = bstring_fwrite(f, b);
assert(n == 0);
fclose (f);
bstring_push_back_bytes(b, sizeof tab1, tab1);
f = fopen("a-mbstring.dat", "rb");
if (!f) abort();
bool success = bstring_fread(b, f, 0);
assert(success);
assert(bstring_size(b) == 0);
fclose (f);
bstring_push_back_bytes(b, sizeof tab1, tab1);
f = fopen("a-mbstring.dat", "wb");
if (!f) abort();
n = bstring_fwrite(f, b);
assert(n == sizeof tab1);
fclose (f);
f = fopen("a-mbstring.dat", "rb");
if (!f) abort();
success = bstring_fread(b, f, sizeof tab1);
assert(success);
assert(bstring_size(b) == sizeof tab1);
assert(m_bstring_equal_bytes_p(b, sizeof tab1, tab1));
fclose (f);
bstring_clear(b);
}
int main(void)
{
test0();
test1();
test2();
test3();
test4();
test_io();
exit(0);
}
+36 -117
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,25 +20,22 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "m-buffer.h"
#include "test-obj.h"
#include "m-buffer.h"
#include "coverage.h"
START_COVERAGE
// Define a fixed queue of unsigned int
BUFFER_DEF(buffer_uint, unsigned int, 10, BUFFER_QUEUE|BUFFER_BLOCKING)
BUFFER_DEF(buffer_uint, unsigned int, 10, BUFFER_QUEUE)
QUEUE_MPMC_DEF(queue_uint, unsigned int, BUFFER_QUEUE)
QUEUE_SPSC_DEF(squeue_uint, unsigned int, BUFFER_QUEUE)
END_COVERAGE
// Define a variable stack of float
BUFFER_DEF(buffer_floats, float, 0, BUFFER_STACK|BUFFER_BLOCKING)
BUFFER_DEF(buffer_floats, float, 0, BUFFER_STACK)
// Define a fixed stack of char
BUFFER_DEF(buffer_char, char, 10, BUFFER_STACK|BUFFER_UNBLOCKING)
// Define a fixed queue of long long
BUFFER_DEF(buffer_llong, long long, 16, BUFFER_QUEUE|BUFFER_THREAD_UNSAFE|BUFFER_UNBLOCKING)
BUFFER_DEF(buffer_char, char, 10, BUFFER_STACK)
// Define a buffer of complex structure.
BUFFER_DEF(buffer_mpz, testobj_t, 32, BUFFER_QUEUE, TESTOBJ_OPLIST)
@@ -170,8 +167,7 @@ static void test_global(void)
assert (buffer_uint_size(g_buffB) == 5);
for(unsigned int i = 0; i < 5;i++) {
unsigned int j;
bool b= buffer_uint_pop(&j, g_buffB);
assert(b);
buffer_uint_pop(&j, g_buffB);
assert(j == i);
}
assert (buffer_uint_empty_p(g_buffB));
@@ -182,8 +178,7 @@ static void test_global(void)
assert (buffer_uint_size(g_buffB) == 5);
for(unsigned int i = 0; i < 5;i++) {
unsigned int j;
bool b= buffer_uint_pop(&j, g_buffB);
assert(b);
buffer_uint_pop(&j, g_buffB);
assert(j == i);
}
assert (buffer_uint_empty_p(g_buffB));
@@ -231,56 +226,25 @@ static void test_stack2(void)
buffer_char_init(buff, 10);
for(int i = 0; i < 10; i++) {
c = (char) i;
b = buffer_char_push(buff, c);
b = buffer_char_push_blocking(buff, c, false);
assert (b == true);
assert (buffer_char_empty_p(buff) == false);
}
b = buffer_char_push(buff, c);
b = buffer_char_push_blocking(buff, c, false);
assert (b == false);
assert (buffer_char_full_p(buff) == true);
for(int i = 0; i < 10; i++) {
buffer_char_pop(&c, buff);
b = buffer_char_pop_blocking(&c, buff, false);
assert(b);
assert(c == 9-i);
assert (buffer_char_full_p(buff) == false);
}
b = buffer_char_pop(&c, buff);
b = buffer_char_pop_blocking(&c, buff, false);
assert (b == false);
assert (buffer_char_empty_p(buff) == true);
buffer_char_clear(buff);
}
static void test_no_thread(void)
{
buffer_llong_t buff;
bool b;
long long c;
buffer_llong_init(buff, 16);
for(int i = 0; i < 16; i++) {
c = i;
b = buffer_llong_push(buff, c);
assert (b == true);
assert (buffer_llong_empty_p(buff) == false);
}
b = buffer_llong_push(buff, c);
assert (b == false);
assert (buffer_llong_full_p(buff) == true);
for(int i = 0; i < 16; i++) {
buffer_llong_pop(&c, buff);
assert(c == i);
assert (buffer_llong_full_p(buff) == false);
}
b = buffer_llong_pop(&c, buff);
assert (b == false);
assert (buffer_llong_empty_p(buff) == true);
b = buffer_llong_push(buff, c);
assert (b == true);
buffer_llong_reset(buff);
assert (buffer_llong_empty_p(buff) == true);
buffer_llong_clear(buff);
}
static void test_emplace(void)
{
buffer_mpz_t b;
@@ -295,77 +259,44 @@ static void test_emplace(void)
}
/********************************************************************************************/
/* Test intrusive shared pointer + buffer */
#include "m-i-shared.h"
/* Test integration shared pointer + buffer */
#include "m-shared-ptr.h"
// Tiny test structure
typedef struct test_s {
ISHARED_PTR_INTERFACE(ishared_itest, struct test_s);
char buffer[52];
char bigbuffer[1000000];
} test_t;
static void test_init(test_t *p) { memset(p->buffer, 0x00, 52); }
static void test_clear(test_t *p) { memset(p->buffer, 0xFF, 52); }
ISHARED_PTR_DEF(ishared_itest, test_t,
(INIT(test_init M_IPTR), CLEAR(test_clear M_IPTR), DEL(free)))
SHARED_PTR_DEF(shared_test, test_t, (INIT(test_init M_IPTR), CLEAR(test_clear M_IPTR)))
typedef struct test2_s {
ISHARED_PTR_INTERFACE(ishared_itest2, struct test_s);
char buffer[52];
} test2_t;
static inline void ishared_itest2_init_data(test2_t *d)
{
memset(d->buffer, 0, sizeof (d->buffer));
}
ISHARED_PTR_DEF(ishared_itest2, test2_t, (INIT(API_2(ishared_itest2_init_data)), CLEAR(M_NOTHING_DEFAULT)))
typedef struct test3_s {
ISHARED_PTR_INTERFACE(ishared_itest3, struct test_s);
char buffer[52];
} test3_t;
ISHARED_PTR_DEF(ishared_itest3, test3_t, (CLEAR(M_NOTHING_DEFAULT)))
typedef struct test4_s {
ISHARED_PTR_INTERFACE(ishared_itest4, struct test_s);
char buffer[52];
} test4_t;
ISHARED_PTR_DEF(ishared_itest4, test4_t, (INIT(0), DEL(0), INIT(M_MEMSET_DEFAULT), CLEAR(M_NOTHING_DEFAULT)))
static test_t *test_new(void)
{
test_t *p = M_ASSIGN_CAST(test_t*, malloc (sizeof (test_t)));
assert (p != NULL);
test_init (p);
return ishared_itest_init(p);
}
BUFFER_DEF(buffer_itest, test_t *, 16, BUFFER_PUSH_INIT_POP_MOVE,
ISHARED_PTR_OPLIST(ishared_itest))
BUFFER_DEF(buffer_itest, shared_test_t *, 16, BUFFER_PUSH_INIT_POP_MOVE, SHARED_PTR_OPLIST(shared_test, (INIT(1), CLEAR(1))))
static buffer_itest_t comm1;
static buffer_itest_t comm2;
static void test_conso1(void *arg)
{
test_t *p;
shared_test_t *p;
assert (arg == NULL);
for(int i = 0; i < 10;i++) {
buffer_itest_pop(&p, comm1);
for(int j = 0; j < 52; j++)
assert (p->buffer[j] == (char) ((j * j * 17) + j * 42 + 1));
ishared_itest_clear(p);
assert (shared_test_ref(p)->buffer[j] == (char) ((j * j * 17) + j * 42 + 1));
shared_test_release(p);
}
}
static void test_conso2(void *arg)
{
test_t *p;
shared_test_t *p;
assert (arg == NULL);
for(int i = 0; i < 10;i++) {
buffer_itest_pop(&p, comm2);
for(int j = 0; j < 52; j++)
assert (p->buffer[j] == (char) ((j * j * 17) + j * 42 + 1));
ishared_itest_clear(p);
assert (shared_test_ref(p)->buffer[j] == (char) ((j * j * 17) + j * 42 + 1));
shared_test_release(p);
}
}
@@ -373,13 +304,13 @@ static void test_prod(void *arg)
{
assert (arg == NULL);
for(unsigned int i = 0; i < 10;i++) {
test_t *p = test_new();
shared_test_t *p = shared_test_new();
for(int j = 0; j < 52; j++) {
p->buffer[j] = (char) ((j * j * 17) + j * 42 + 1);
shared_test_ref(p)->buffer[j] = (char) ((j * j * 17) + j * 42 + 1);
}
buffer_itest_push(comm1, p);
buffer_itest_push(comm2, p);
ishared_itest_clear(p);
shared_test_clear(p);
}
}
@@ -582,33 +513,25 @@ static void test_spsc(void)
static void test_double1(void)
{
bool b;
M_LET(buffer, BufferDouble1) {
b = BufferDouble1_push(buffer, 0.0);
assert(b);
b= BufferDouble1_push(buffer, 1.0);
assert(b);
BufferDouble1_push(buffer, 0.0);
BufferDouble1_push(buffer, 1.0);
double d;
b = BufferDouble1_pop(&d, buffer);
assert(b);
BufferDouble1_pop(&d, buffer);
assert(d == 0.0);
b = BufferDouble1_pop(&d, buffer);
assert(b);
BufferDouble1_pop(&d, buffer);
assert(d == 1.0);
}
}
static void test_double2(void)
{
bool b;
BufferDouble2 buffer;
BufferDouble2_init(buffer, 2);
b = BufferDouble2_push(buffer, 0.0);
assert(b);
b= BufferDouble2_push(buffer, 1.0);
assert(b);
BufferDouble2_push(buffer, 0.0);
BufferDouble2_push(buffer, 1.0);
double d;
b = BufferDouble2_pop(&d, buffer);
bool b = BufferDouble2_pop(&d, buffer);
assert(b);
assert(d == 0.0);
b = BufferDouble2_pop(&d, buffer);
@@ -619,15 +542,12 @@ static void test_double2(void)
static void test_double3(void)
{
bool b;
BufferDouble3 buffer;
BufferDouble3_init(buffer, 2);
b = BufferDouble3_push(buffer, 0.0);
assert(b);
b= BufferDouble3_push(buffer, 1.0);
assert(b);
BufferDouble3_push(buffer, 0.0);
BufferDouble3_push(buffer, 1.0);
double d;
b = BufferDouble3_pop(&d, buffer);
bool b = BufferDouble3_pop(&d, buffer);
assert(b);
assert(d == 0.0);
b = BufferDouble3_pop(&d, buffer);
@@ -668,7 +588,6 @@ int main(void)
test_global();
test_stack();
test_stack2();
test_no_thread();
test_emplace();
test_global_ishared();
test_queue(1000000, 2, 2148371710223136ULL);
-208
View File
@@ -1,208 +0,0 @@
/*
* M*LIB - Test for Concurrent memory pool allocator
*
* Copyright (c) 2017-2024, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "m-c-mempool.h"
#include "m-thread.h"
C_MEMPOOL_DEF(lf_mempool, int)
m_gc_t gc;
/**********************************************************************/
#define MAX_VALUE 100
#define MAX_THREAD 4
lf_mempool_t g;
typedef struct {
int step;
int *ptr[MAX_VALUE];
int value[MAX_VALUE];
} p_t;
p_t g_tab[MAX_THREAD];
static void do_stuff(m_gc_tid_t id)
{
// Mix of local and more global allocation
int *d[MAX_VALUE];
for(int i = 0; i < MAX_VALUE; i++) {
d[i] = lf_mempool_new(g, id);
*d[i] = i*i;
}
switch (g_tab[id].step)
{
case 0:
for(int i = 0; i < 50; i++) {
g_tab[id].value[i] += i;
g_tab[id].ptr[i] = lf_mempool_new(g, id);
*g_tab[id].ptr[i] = g_tab[id].value[i];
}
g_tab[id].step = 1;
break;
case 1:
for(int i = 50; i < MAX_VALUE; i++) {
g_tab[id].value[i] += i;
g_tab[id].ptr[i] = lf_mempool_new(g, id);
*g_tab[id].ptr[i] = g_tab[id].value[i];
}
g_tab[id].step = 2;
break;
case 2:
for(int i = 0; i < 50; i++) {
assert(g_tab[id].ptr[i] != NULL);
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
lf_mempool_del(g, g_tab[id].ptr[i], id);
g_tab[id].ptr[i] = NULL;
}
g_tab[id].step = 3;
break;
case 3:
for(int i = 50; i < MAX_VALUE; i++) {
assert(g_tab[id].ptr[i] != NULL);
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
lf_mempool_del(g, g_tab[id].ptr[i], id);
g_tab[id].ptr[i] = NULL;
}
g_tab[id].step = 0;
break;
default:
break;
}
for(int i = 0; i < MAX_VALUE; i++) {
assert (*d[i] == i*i);
lf_mempool_del(g, d[i], id);
}
}
static void thread1(void *arg)
{
(void)arg;
/* Attach this thread to the GC */
m_gc_tid_t id = m_gc_attach_thread(gc);
for(int n = 0; n < 100000; n ++) {
/* Awake the thread: now we can alloc nodes */
m_gc_awake(gc, id);
/* Do some stuff */
do_stuff(id);
/* Sleep the thread. A Garbage Collect may occur if possible */
m_gc_sleep(gc, id);
}
/* Detach the thread from the GC */
m_gc_detach_thread(gc, id);
}
static void thread2(void *arg)
{
(void)arg;
m_gc_tid_t id = m_gc_attach_thread(gc);
for(int n = 0; n < 10000; n ++) {
m_gc_awake(gc, id);
do_stuff(id);
m_thread_sleep(1);
m_gc_sleep(gc, id);
}
m_gc_detach_thread(gc, id);
}
static void test(void)
{
/* First create the Garbage Collector */
m_gc_init (gc, MAX_THREAD);
/* Create a mempool and attach it to the Garbage Collector */
lf_mempool_init(g, gc, 256, MAX_THREAD);
m_thread_t id[MAX_THREAD];
for(int i = 0; i < MAX_THREAD; i+=2) {
m_thread_create(id[i], thread1, NULL);
m_thread_create(id[i+1], thread2, NULL);
}
for(int i = 0; i < MAX_THREAD; i++) {
m_thread_join(id[i]);
}
/* Destroy the mempool */
lf_mempool_clear(g);
/* Destroy the Garbage Collector */
m_gc_clear (gc);
}
static void test2(void)
{
/* First create the Garbage Collector */
m_gc_init (gc, MAX_THREAD);
/* Create a mempool and attach it to the Garbage Collector */
lf_mempool_init(g, gc, 256, MAX_THREAD);
/* Create another mempool and attach it to the Garbage Collector */
lf_mempool_t g2;
lf_mempool_init(g2, gc, 256, MAX_THREAD);
/* Register VLA */
m_vlapool_t vla;
m_vlapool_init(vla, gc);
/* Attach the thread */
m_gc_tid_t id = m_gc_attach_thread(gc);
/* Awake the thread: now we can alloc nodes */
m_gc_awake(gc, id);
/* Test some allocation */
void *ptr = m_vlapool_new(vla, id, sizeof (int));
*(int*)ptr = 42;
m_vlapool_del(vla, ptr, id);
int *pi = lf_mempool_new(g, id);
*pi = 44;
lf_mempool_del(g, pi, id);
int *pi2 = lf_mempool_new(g2, id);
*pi2 = 46;
lf_mempool_del(g2, pi2, id);
/* Test that the data remain accessible
as it has only be logically deleted */
assert (*(int*)ptr == 42);
assert (*pi == 44);
assert (*pi2 == 46);
/* Sleep the thread */
m_gc_sleep(gc, id);
/* Detach the thread */
m_gc_detach_thread(gc, id);
/* Clear everything */
m_vlapool_clear(vla);
lf_mempool_clear(g);
lf_mempool_clear(g2);
m_gc_clear(gc);
}
int main(void)
{
test();
test2();
exit(0);
}
-250
View File
@@ -1,250 +0,0 @@
/*
* Copyright (c) 2017-2024, 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 "test-obj.h"
#include "coverage.h"
#include "m-array.h"
#include "m-bptree.h"
#include "m-deque.h"
#include "m-dict.h"
#include "m-list.h"
#include "m-prioqueue.h"
#include "m-rbtree.h"
#include "m-tuple.h"
#include "m-variant.h"
#include "m-concurrent.h"
TUPLE_DEF2(point, (x, int), (y, int))
#define M_OPL_point_t() TUPLE_OPLIST(point, M_BASIC_OPLIST, M_BASIC_OPLIST)
START_COVERAGE
CONCURRENT_DEF(ppoint, point_t)
END_COVERAGE
VARIANT_DEF2(dimension, (x, int), (y, float))
#define M_OPL_dimension_t() VARIANT_OPLIST(dimension, M_BASIC_OPLIST, M_BASIC_OPLIST)
CONCURRENT_DEF(pdimension, dimension_t)
ARRAY_DEF(array1, int)
CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1))
DICT_DEF2(dict1, int, int)
CONCURRENT_DEF(pdict1, dict1_t, DICT_OPLIST(dict1))
DICT_SET_DEF(dict2, int)
CONCURRENT_DEF(pdict2, dict2_t, DICT_SET_OPLIST(dict2))
LIST_DEF(list1, int)
CONCURRENT_DEF(plist1, list1_t, LIST_OPLIST(list1))
LIST_DUAL_PUSH_DEF(list2, int)
CONCURRENT_DEF(plist2, list2_t, LIST_OPLIST(list2))
DEQUE_DEF(deque1, int)
CONCURRENT_DEF(pdeque1, deque1_t, DEQUE_OPLIST(deque1))
PRIOQUEUE_DEF(prio1, int)
CONCURRENT_DEF(pprio1, prio1_t, PRIOQUEUE_OPLIST(prio1))
BPTREE_DEF2(bptree1, 10, int, int)
CONCURRENT_DEF(pbtree1, bptree1_t, BPTREE_OPLIST2(bptree1, M_BASIC_OPLIST, M_BASIC_OPLIST))
BPTREE_DEF(bptree2, 10, int)
CONCURRENT_DEF(pbtree2, bptree2_t, BPTREE_OPLIST(bptree2))
RBTREE_DEF(rbtree1, int)
CONCURRENT_DEF(prbtree1, rbtree1_t, RBTREE_OPLIST(rbtree1))
DICT_DEF2(string_pool, string_t, string_t)
#define STRING_POOL_OPLIST \
DICT_OPLIST(string_pool, STRING_OPLIST, STRING_OPLIST)
#define M_OPL_string_pool_t() STRING_POOL_OPLIST
CONCURRENT_DEF(string_pool_ts, string_pool_t, STRING_POOL_OPLIST)
#define STRING_POOL_TS_OPLIST \
CONCURRENT_OPLIST(string_pool_ts, STRING_POOL_OPLIST)
#define M_OPL_string_pool_ts_t() STRING_POOL_TS_OPLIST
TUPLE_DEF2(my_tuple, (pool, string_pool_ts_t))
#define MY_TUPLE_OPLIST TUPLE_OPLIST(my_tuple, STRING_POOL_TS_OPLIST)
#define M_OPL_my_tuple_t() MY_TUPLE_OPLIST
ARRAY_DEF(key_list, string_t)
#define M_OPL_key_list_t() ARRAY_OPLIST(key_list, STRING_OPLIST)
CONCURRENT_DEF(key_list_ts, key_list_t)
CONCURRENT_RP_DEF(rparray1, array1_t, ARRAY_OPLIST(array1))
CONCURRENT_RP_DEF(rpdict1, dict1_t, DICT_OPLIST(dict1))
CONCURRENT_DEF_AS(ConcurrentDouble, ConcurrentDouble, double)
CONCURRENT_RP_DEF_AS(ConcurrentRpDouble, ConcurrentRpDouble, double)
/* OA dict needs more operators than the default ones to work */
static inline bool int_oor_equal_p(int s, unsigned char n)
{
return s == -n;
}
static inline void int_oor_set(int *s, unsigned char n)
{
*s = -n;
}
#define INT_OA_OPLIST \
M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(int_oor_equal_p), OOR_SET(API_2(int_oor_set)))
DICT_OA_DEF2(dict3, int, INT_OA_OPLIST, int, M_BASIC_OPLIST)
CONCURRENT_DEF(pdict3, dict3_t, DICT_OPLIST(dict3))
/********************************/
parray1_t arr;
static void conso(void *p)
{
assert (p == NULL);
for(int i = 0; i < 1000; i++) {
int j;
bool b = parray1_pop_blocking(&j, arr, true);
assert (b);
assert (j == i);
}
}
static void test_thread(void)
{
m_thread_t idx;
parray1_init(arr);
m_thread_create (idx, conso, NULL);
m_thread_sleep(1000);
for(int i = 0; i < 1000; i++) {
parray1_push (arr, i);
while (!parray1_empty_p(arr));
}
m_thread_join(idx);
parray1_clear(arr);
}
static void test_basic(void)
{
pdict1_t dict;
pdict1_init(dict);
int z;
pdict1_set_at(dict, 2, 3);
pdict1_get_copy(&z, dict, 2);
assert (z == 3);
z = 1;
bool b = pdict1_get_blocking(&z, dict, 2, false);
assert (b);
assert (z == 3);
assert (pdict1_size(dict) == 1);
pdict1_clear(dict);
M_LET(str, string_t)
M_LET(pool, string_pool_ts_t) {
string_pool_ts_set_at (pool, STRING_CTE("A"), STRING_CTE("B"));
b = string_pool_ts_get_copy (&str, pool, STRING_CTE("A"));
assert (b);
assert (string_equal_str_p (str, "B"));
}
M_LET(tuple, my_tuple_t) {
assert (string_pool_ts_empty_p(tuple->pool));
}
}
rparray1_t rarr;
static void conso_rp(void *p)
{
assert (p == NULL);
for(int i = 0; i < 1000; i++) {
int j;
bool b = rparray1_pop_blocking(&j, rarr, true);
assert (b);
assert (j == i);
}
}
static void test_rp_thread(void)
{
m_thread_t idx;
rparray1_init(rarr);
m_thread_create (idx, conso_rp, NULL);
m_thread_sleep(1000);
for(int i = 0; i < 1000; i++) {
rparray1_push (rarr, i);
while (!rparray1_empty_p(rarr));
}
m_thread_join(idx);
rparray1_clear(rarr);
}
static void test_rp_basic(void)
{
rpdict1_t dict;
rpdict1_init(dict);
int z = 0;
rpdict1_set_at(dict, 2, 3);
rpdict1_get_copy(&z, dict, 2);
assert (z == 3);
z = 1;
bool b = rpdict1_get_blocking(&z, dict, 2, false);
assert (b);
assert (z == 3);
assert (rpdict1_size(dict) == 1);
rpdict1_clear(dict);
}
static void test_double(void)
{
ConcurrentDouble d;
ConcurrentDouble_init(d);
ConcurrentDouble_reset(d);
ConcurrentDouble_clear(d);
ConcurrentRpDouble r;
ConcurrentRpDouble_init(r);
ConcurrentRpDouble_reset(r);
ConcurrentRpDouble_clear(r);
}
static void test_emplace(void)
{
key_list_ts_t ts;
key_list_ts_init(ts);
key_list_ts_emplace(ts, "HELLO");
M_LET(s, string_t) {
key_list_ts_pop(&s, ts);
assert(string_equal_str_p(s, "HELLO"));
}
key_list_ts_clear(ts);
}
int main(void)
{
test_basic();
test_rp_basic();
test_thread();
test_rp_thread();
test_double();
test_emplace();
exit(0);
}
+12 -26
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -29,9 +29,8 @@
#define M_F(a,b) M_OVERRIDE_F(a,b)
#define M_OVERRIDE__method() , _get_method ,
#define M_OVERRIDE__method2() , _get_method2
#include "m-core.h"
#include "test-obj.h"
#include "m-core.h"
static bool tmp_init(void) { return true; }
static bool tmp_get_method(void) { return true; }
@@ -747,65 +746,54 @@ typedef struct { int b,c; } ts;
static void f_ti(ti x)
{
ti y;
M_MOVE_A1_DEFAULT(y,x);
M_COPY_A1_DEFAULT(y,x);
assert(y[0] == 9);
assert(x[0] == 0);
}
static void f_ts(ts x)
{
ts y;
M_MOVE_DEFAULT(y,x);
M_SET_DEFAULT(y,x);
assert(y.b == 2);
assert(y.c == 3);
assert(x.b == 0);
assert(x.c == 0);
}
static void f_ts2(ts *x)
{
ts y;
M_MOVE_DEFAULT(y,*x);
M_SET_DEFAULT(y,*x);
assert(y.b == 2);
assert(y.c == 3);
assert(x->b == 0);
assert(x->c == 0);
}
static void test_move_default(void)
{
int o, p;
o = 9;
M_MOVE_DEFAULT(p, o);
M_SET_DEFAULT(p, o);
assert(p == 9);
assert(o == 0);
double u, k;
k = -1.0;
u = 9.5;
M_MOVE_DEFAULT(k, u);
M_SET_DEFAULT(k, u);
assert(k == 9.5);
assert(u == 0.0); // NOTE: memset(0) sets an IEEE-754 number to 0.0
int *ptr = &o, *m;
M_MOVE_DEFAULT(m, ptr);
assert( ptr == NULL);
assert( m == &o);
M_SET_DEFAULT(m, ptr);
(void)m;
ti x, y;
x[0] = 9;
M_MOVE_A1_DEFAULT(y, x);
M_COPY_A1_DEFAULT(y, x);
assert(y[0] == 9);
assert(x[0] == 0);
x[0] = 9;
f_ti(x);
assert(x[0] == 0);
ts y2, x2 = { 2, 3};
M_MOVE_DEFAULT(y2, x2);
M_SET_DEFAULT(y2, x2);
assert(y2.b == 2 && y2.c == 3);
assert(x2.b == 0 && x2.c == 0);
x2.b = 2; x2.c = 3;
f_ts(x2);
@@ -813,8 +801,6 @@ static void test_move_default(void)
assert(x2.c == 3);
f_ts2(&x2);
assert(x2.b == 0);
assert(x2.c == 0);
}
static void test_builtin(void)
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -35,7 +35,7 @@ END_COVERAGE
DEQUE_DEF(deque_mpz, testobj_t, TESTOBJ_OPLIST)
// Deque with the minimum number of methods.
DEQUE_DEF(deque_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
DEQUE_DEF(deque_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear), INIT_MOVE(M_COPY_A1_DEFAULT) ))
// Dequeue of an enumerate
typedef enum {
+4 -5
View File
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -21,17 +21,16 @@
* (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 <stdint.h>
#include <assert.h>
#include "test-obj.h"
#include "m-dict.h"
#include "m-array.h"
#include "m-string.h"
#include "test-obj.h"
#include "coverage.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; }
static inline void update_value (int *p, const int p1, const int p2) { *p = p1 + p2; }
#include "coverage.h"
START_COVERAGE
DICT_DEF2(dict_str, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
DICT_OA_DEF2(dict_oa_int, int, M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)), int, M_OPEXTEND(M_BASIC_OPLIST, ADD(update_value M_IPTR)))
+1 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,7 +20,6 @@
* (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 "test-obj.h"
#include "m-string.h"
#include "m-funcobj.h"
+4 -8
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -21,19 +21,15 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdbool.h>
#include <assert.h>
#include "coverage.h"
#include "test-obj.h"
#include "m-string.h"
#include "m-array.h"
#include "m-list.h"
#include "m-generic.h"
#include "coverage.h"
// Generic is not supported if not C11
// TCC has some issues in the preprocessing.
// See https://lists.nongnu.org/archive/html/tinycc-devel/2024-12/msg00004.html
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__TINYC__)
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
ARRAY_DEF(array_int, int)
#define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST)
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,12 +20,11 @@
* (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 "test-obj.h"
#include "m-thread.h"
#include "m-atomic.h"
#include "m-core.h"
#include "coverage.h"
#include "m-genint.h"
#define MAX_N 256
+4 -5
View File
@@ -1,7 +1,7 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2024, 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:
@@ -22,9 +22,9 @@
* (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 "test-obj.h"
#include "m-i-list.h"
#include "coverage.h"
typedef struct test_s {
int n;
@@ -37,12 +37,11 @@ static inline bool test_equal_p(const test_t *i1, const test_t *i2)
return i1->n == i2->n;
}
#include "coverage.h"
START_COVERAGE
ILIST_DEF(ilist_tname, test_t, M_OPEXTEND(M_POD_OPLIST, EQUAL(API_6(test_equal_p))))
END_COVERAGE
ILIST_DEF(ilist_free, test_t, (DEL(free)))
ILIST_DEF(ilist_free, test_t, (DEL(API(free, NONE, ARG2))))
typedef struct testd_s {
double n;
-153
View File
@@ -1,153 +0,0 @@
/*
* Copyright (c) 2017-2024, 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 <assert.h>
#include "test-obj.h"
#include "m-i-shared.h"
#include "m-thread.h"
#include "coverage.h"
typedef struct ishared_s {
ISHARED_PTR_INTERFACE(ishared1, struct ishared_s);
ISHARED_PTR_INTERFACE(ishared2, struct ishared_s);
int data[100];
} ishared_t[1];
static bool ishared_init_sleep = false;
static void ishared_init_data(struct ishared_s *x)
{
for(int i = 0 ; i < 100; i++)
x->data[i] = i * i;
if (ishared_init_sleep) {
// Slow down init
m_thread_sleep(100);
}
}
static void ishared_test(struct ishared_s *x)
{
for(int i = 0 ; i < 100; i++) {
assert (x->data[i] == i*i);
}
}
START_COVERAGE
ISHARED_PTR_DEF(ishared1, struct ishared_s, (INIT(API_2(ishared_init_data))))
ISHARED_PTR_DEF(ishared2, struct ishared_s, (INIT(API_2(ishared_init_data)), NEW(0), DEL(0)) )
END_COVERAGE
/*ISHARED_PTR_DEF_AS(SharedDouble, SharedDouble, double) */
static void test_single(void)
{
ishared1_t p1, p2;
p1 = ishared1_init_new();
ishared_test(p1);
p2 = ishared1_init_set(p1);
ishared_test(p2);
ishared1_clear(p1);
ishared_test(p2);
p1 = ishared1_init(NULL);
ishared1_set(&p1, p2);
ishared1_reset(&p2);
assert(p2 == NULL);
ishared_test(p1);
ishared1_clear(p1);
p1 = ishared1_init_set(p2);
assert(p1 == NULL);
ishared1_clear(p1);
ishared1_clear(p2);
struct ishared_s *x = (struct ishared_s *) malloc(sizeof (struct ishared_s));
assert(x != NULL);
ishared_init_data(x);
p1 = ishared1_init(x);
ishared_test(p1);
ishared1_clear(p1);
}
static struct ishared_s g_var = {
ISHARED_PTR_STATIC_INIT(ishared1, struct ishared_s),
ISHARED_PTR_STATIC_INIT(ishared2, struct ishared_s),
{ 0 }
};
static void test_static(void)
{
ishared2_t p1, p2;
p1 = ishared2_init_once(NULL);
assert(p1 == NULL);
p1 = ishared2_init_once(&g_var);
ishared_test(p1);
p2 = ishared2_init_once(&g_var);
ishared_test(p2);
ishared2_clear(p1);
ishared_test(p2);
ishared2_clear(p2);
}
#define MAX_THREAD 128
#define MAX_VAR 5
#define VAL(x) { ISHARED_PTR_STATIC_INIT(ishared1, struct ishared_s), ISHARED_PTR_STATIC_INIT(ishared2, struct ishared_s), { 0 } }
static struct ishared_s g_tab[MAX_VAR] = {
M_MAP_C(VAL, M_SEQ(1, MAX_VAR))
};
static void func(void *arg)
{
int id = (int)(intptr_t) arg;
int v = id % MAX_VAR;
ishared2_t p = ishared2_init_once(&g_tab[v]);
for(int i = 0; i < 100; i++) {
ishared_test(p);
}
ishared2_clear(p);
}
static void test_shared(void)
{
m_thread_t id[MAX_THREAD];
ishared_init_sleep = true;
for(int i = 0; i < MAX_THREAD; i++) {
m_thread_create(id[i], func, (void*) (intptr_t) i);
}
for(int i = 0; i < MAX_THREAD; i++) {
m_thread_join(id[i]);
}
ishared_init_sleep = false;
}
int main(void)
{
test_single();
test_static();
test_shared();
exit(0);
}
+4 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,12 +20,11 @@
* (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 "test-obj.h"
#include "m-string.h"
#include "m-list.h"
#include "coverage.h"
START_COVERAGE
LIST_DEF(list_uint, unsigned int)
LIST_DEF(list_mpz, testobj_t, TESTOBJ_OPLIST)
@@ -36,8 +35,8 @@ LIST_DUAL_PUSH_DEF(list2_mpz, testobj_t, TESTOBJ_OPLIST)
#define LIST2_DOUBLE_OPLIST LIST_OPLIST(list2_double)
// Array with the minimum number of methods.
LIST_DEF(list_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
LIST_DUAL_PUSH_DEF(list2_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
LIST_DEF(list_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear), INIT_MOVE(M_COPY_A1_DEFAULT) ))
LIST_DUAL_PUSH_DEF(list2_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear), INIT_MOVE(M_COPY_A1_DEFAULT)))
LIST_DEF(list_string, string_t)
#define M_OPL_list_string_t() LIST_OPLIST(list_string, STRING_OPLIST)
-109
View File
@@ -1,109 +0,0 @@
/*
* Copyright (c) 2017-2024, 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 <stdlib.h>
#include "m-mempool.h"
#include "coverage.h"
START_COVERAGE
MEMPOOL_DEF(mempool_uint, unsigned int)
END_COVERAGE
MEMPOOL_DEF_AS(MempoolDouble, MempoolDouble, unsigned int)
static void test(void)
{
mempool_uint_t m;
mempool_uint_init(m);
unsigned int *tab[100000];
for(unsigned int i = 0; i < 100000; i++) {
tab[i] = mempool_uint_alloc(m);
*tab[i] = i;
}
for(unsigned int i = 0; i < 100000; i++) {
assert (*tab[i] == i);
}
for(unsigned int i = 0; i < 100000; i+=2) {
mempool_uint_free(m, tab[i]);
tab[i] = NULL;
}
for(unsigned int i = 1; i < 100000; i+=2) {
assert (*tab[i] == i);
}
for(unsigned int i = 0; i < 100000; i+=2) {
tab[i] = mempool_uint_alloc(m);
*tab[i] = i;
}
for(unsigned int i = 0; i < 100000; i++) {
assert (*tab[i] == i);
}
for(unsigned int i = 0; i < 100000; i++) {
mempool_uint_free(m, tab[i]);
tab[i] = NULL;
}
mempool_uint_clear(m);
}
static void test_double(void)
{
MempoolDouble m;
MempoolDouble_init(m);
unsigned int *tab[100000];
for(unsigned int i = 0; i < 100000; i++) {
tab[i] = MempoolDouble_alloc(m);
*tab[i] = i;
}
for(unsigned int i = 0; i < 100000; i++) {
assert (*tab[i] == i);
}
for(unsigned int i = 0; i < 100000; i+=2) {
MempoolDouble_free(m, tab[i]);
tab[i] = NULL;
}
for(unsigned int i = 1; i < 100000; i+=2) {
assert (*tab[i] == i);
}
for(unsigned int i = 0; i < 100000; i+=2) {
tab[i] = MempoolDouble_alloc(m);
*tab[i] = i;
}
for(unsigned int i = 0; i < 100000; i++) {
assert (*tab[i] == i);
}
for(unsigned int i = 0; i < 100000; i++) {
MempoolDouble_free(m, tab[i]);
tab[i] = NULL;
}
MempoolDouble_clear(m);
}
int main(void)
{
test();
test_double();
exit(0);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
+2 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,13 +20,11 @@
* (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 <assert.h>
#include "test-obj.h"
#include "m-string.h"
#include "m-prioqueue.h"
#include "coverage.h"
START_COVERAGE
PRIOQUEUE_DEF(int_pqueue, int)
END_COVERAGE
+171
View File
@@ -0,0 +1,171 @@
/*
* 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.
*/
#include "test-obj.h"
#include "m-queue.h"
#include "coverage.h"
START_COVERAGE
STACK_DEF(stack_obj, testobj_t, 0, TESTOBJ_OPLIST)
END_COVERAGE
M_QUEUE_DEF(queue_obj, testobj_t, 10, TESTOBJ_OPLIST)
M_STACK_DEF(stack_int, int, 15)
QUEUE_DEF(buffer_llong, long long, 0)
static void test_obj(void)
{
stack_obj_t b1, b2;
testobj_t o;
bool b;
testobj_init(o);
stack_obj_init(b1, 24);
assert( stack_obj_empty_p(b1) == true);
assert( stack_obj_full_p(b1) == false);
assert( stack_obj_size(b1) == 0);
assert( stack_obj_capacity(b1) == 24);
stack_obj_reset(b1);
assert( stack_obj_empty_p(b1) == true);
assert( stack_obj_full_p(b1) == false);
assert( stack_obj_size(b1) == 0);
for(unsigned i = 0; i < 24; i++) {
testobj_set_ui(o, i);
assert( stack_obj_full_p(b1) == false);
b = stack_obj_push(b1, o);
assert(b);
assert( stack_obj_empty_p(b1) == false);
assert( stack_obj_size(b1) == i+1);
}
assert( stack_obj_full_p(b1) == true);
assert( stack_obj_size(b1) == 24);
b = stack_obj_push(b1, o);
assert(!b);
assert( stack_obj_size(b1) == 24);
for(unsigned i = 23; i < 24; i--) {
testobj_set_ui(o, i);
assert( stack_obj_empty_p(b1) == false);
b = stack_obj_pop(&o, b1);
assert(b);
assert( stack_obj_full_p(b1) == false);
assert( stack_obj_size(b1) == i);
assert( testobj_cmp_ui(o, i) == 0);
}
assert( stack_obj_empty_p(b1) == true);
assert( stack_obj_size(b1) == 0);
b = stack_obj_pop(&o, b1);
assert(!b);
assert( stack_obj_size(b1) == 0);
for(unsigned i = 0; i < 24; i++) {
testobj_set_ui(o, i);
assert( stack_obj_full_p(b1) == false);
b = stack_obj_push(b1, o);
assert(b);
assert( stack_obj_empty_p(b1) == false);
assert( stack_obj_size(b1) == i+1);
}
stack_obj_init_set(b2, b1);
stack_obj_reset(b1);
assert( stack_obj_empty_p(b1) == true);
assert( stack_obj_size(b1) == 0);
b = stack_obj_pop(&o, b1);
assert(!b);
assert( stack_obj_size(b1) == 0);
assert( stack_obj_full_p(b2) == true);
assert( stack_obj_size(b2) == 24);
b = stack_obj_push(b2, o);
assert(!b);
assert( stack_obj_size(b2) == 24);
stack_obj_set(b1, b2);
assert( stack_obj_size(b1) == 24);
stack_obj_clear(b1);
stack_obj_clear(b2);
testobj_clear(o);
}
static void test_queue_llong(void)
{
buffer_llong_t buff;
bool b;
long long c;
buffer_llong_init(buff, 16);
assert (buffer_llong_empty_p(buff) == true);
for(int i = 0; i < 16; i++) {
assert (buffer_llong_full_p(buff) == false);
c = i;
b = buffer_llong_push(buff, c);
assert (b == true);
assert (buffer_llong_empty_p(buff) == false);
}
assert (buffer_llong_full_p(buff) == true);
b = buffer_llong_push(buff, c);
assert (b == false);
for(int i = 0; i < 16; i++) {
assert (buffer_llong_empty_p(buff) == false);
buffer_llong_pop(&c, buff);
assert(c == i);
assert (buffer_llong_full_p(buff) == false);
}
assert (buffer_llong_empty_p(buff) == true);
b = buffer_llong_pop(&c, buff);
assert (b == false);
b = buffer_llong_push(buff, c);
assert (b == true);
assert (buffer_llong_empty_p(buff) == false);
buffer_llong_reset(buff);
assert (buffer_llong_empty_p(buff) == true);
buffer_llong_clear(buff);
}
static void test_emplace(void)
{
stack_obj_t b;
stack_obj_init(b, 32);
stack_obj_emplace_str(b, "56");
testobj_t o;
testobj_init(o);
stack_obj_pop(&o, b);
assert( testobj_cmp_ui(o, 56) == 0 );
testobj_clear(o);
stack_obj_clear(b);
}
int main(void)
{
test_obj();
test_queue_llong();
test_emplace();
testobj_final_check();
exit(0);
}
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,10 +20,10 @@
* (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 "test-obj.h"
#include "m-string.h"
#include "m-rbtree.h"
#include "coverage.h"
static bool uint_in_str(unsigned int *u, FILE *f)
{
@@ -49,7 +49,6 @@ static void uint_get_str(string_t str, unsigned int u, bool append)
(append ? string_cat_printf : string_printf) (str, "%u", u);
}
#include "coverage.h"
START_COVERAGE
RBTREE_DEF(rbtree_uint, unsigned int, M_OPEXTEND(M_BASIC_OPLIST, IN_STR(uint_in_str M_IPTR), OUT_STR(uint_out_str), GET_STR(uint_get_str), PARSE_STR(uint_parse_str M_IPTR)) )
END_COVERAGE
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,6 +20,7 @@
* (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 "test-obj.h"
#include "m-tuple.h"
#include "m-array.h"
#include "m-variant.h"
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2024, 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:
@@ -20,6 +20,7 @@
* (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 "test-obj.h"
#include "m-tuple.h"
#include "m-array.h"
#include "m-variant.h"
+552
View File
@@ -0,0 +1,552 @@
/*
* 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.
*/
#include "test-obj.h"
#include "m-shared-ptr.h"
#include "m-string.h"
#include "m-array.h"
#include "m-bptree.h"
#include "m-deque.h"
#include "m-dict.h"
#include "m-list.h"
#include "m-prioqueue.h"
#include "m-rbtree.h"
#include "m-tuple.h"
#include "m-variant.h"
#include "coverage.h"
// TEST WITH INT
SHARED_PTR_DECL_AS(shared_int, SharedInt, M_BASIC_OPLIST)
// TEST WITH DOUBLE
SHARED_PTR_DECL_AS(shared_double, SharedDouble, M_BASIC_OPLIST)
extern SharedDouble *shared_double_new_from(double); // Workaround for test.
static void test_double(void)
{
SharedDouble *p = shared_double_new();
SharedDouble *q = shared_double_new_from(2.0);
SharedDouble *r = shared_double_new_from(1.0);
SharedDouble *rr = shared_double_new_from(3.0);
shared_double_add(p, q, r);
assert(shared_double_equal_p(p, rr));
shared_double_sub(p, q, r);
assert(shared_double_equal_p(p, r));
shared_double_mul(p, q, r);
assert(shared_double_equal_p(p, q));
shared_double_div(p, q, r);
assert(shared_double_equal_p(p, q));
shared_double_reset(p);
shared_double_release(p);
shared_double_release(q);
shared_double_release(r);
shared_double_release(rr);
}
// TEST WITH STRING
static inline void string_add(string_t d, const string_t a, const string_t b)
{
int ia = atoi(string_get_cstr(a));
int ib = atoi(string_get_cstr(b));
string_printf(d, "%d", ia+ib);
}
static inline void string_sub(string_t d, const string_t a, const string_t b)
{
int ia = atoi(string_get_cstr(a));
int ib = atoi(string_get_cstr(b));
string_printf(d, "%d", ia-ib);
}
static inline void string_mul(string_t d, const string_t a, const string_t b)
{
int ia = atoi(string_get_cstr(a));
int ib = atoi(string_get_cstr(b));
string_printf(d, "%d", ia*ib);
}
static inline void string_div(string_t d, const string_t a, const string_t b)
{
int ia = atoi(string_get_cstr(a));
int ib = atoi(string_get_cstr(b));
string_printf(d, "%d", ia/ib);
}
#define STR_OPL M_OPEXTEND(STRING_OPLIST, POP(string_pop_u), GET_SIZE(string_length_u), ADD(string_add), SUB(string_sub), MUL(string_mul), DIV(string_div))
SHARED_PTR_DECL(shared_string, STR_OPL)
static int str_callback(void *data, const string_unicode_t *u)
{
string_unicode_t *pu = (string_unicode_t *) data;
*pu += *u;
return 0;
}
static int str_callback2(void *data, const string_unicode_t *u)
{
string_unicode_t *pu = (string_unicode_t *) data;
*pu += *u;
return 1;
}
static void test_string(void)
{
shared_string_t *p = shared_string_new();
shared_string_t *q = shared_string_make("Hello world");
shared_string_t *r = shared_string_acquire(p);
assert(p == r);
assert(shared_string_equal_p(r, p));
assert(!shared_string_equal_p(r, q));
shared_string_t *p2 = shared_string_clone(p);
assert(p2 != p);
assert(shared_string_equal_p(p2, p));
bool b = shared_string_empty_p(p);
assert(b);
shared_string_clear(p);
b = shared_string_empty_p(r);
assert(b);
shared_string_copy(q, r);
assert(shared_string_equal_p(r, q));
shared_string_set(&q, r);
assert(shared_string_equal_p(r, q));
shared_string_t *r2 = shared_string_make("Hello world!");
shared_string_copy(q, r2);
assert(shared_string_equal_p(r2, q));
shared_string_set(&q, r);
assert(shared_string_equal_p(r, q));
assert(shared_string_cmp(r, q) == 0);
assert(shared_string_cmp(q, r) == 0);
assert(shared_string_cmp(q, p) == 0);
string_unicode_t u;
b = shared_string_try_pop(&u, p2);
assert(!b);
b = shared_string_try_push(p2, 'X');
assert(b);
shared_string_push(p2, 'X');
assert(shared_string_size(p2) == 2);
b = shared_string_try_pop(&u, p2);
assert(b);
assert(shared_string_cmp(q, p2) < 0);
assert(shared_string_cmp(p2, q) > 0);
shared_string_push(p2, 'X');
u = 0;
int res = shared_string_for_each(p2, str_callback, (void*) &u);
assert(res == 0);
assert(u == 2* 'X');
res = shared_string_for_each(p2, str_callback2, (void*) &u);
assert(res == 1);
assert(u == 3* 'X');
shared_string_reset(q);
assert(!shared_string_equal_p(r2, q));
assert(shared_string_empty_p(q));
assert(shared_string_hash(q) != 0);
shared_string_swap(r2, r2);
shared_string_swap(r2, q);
assert(shared_string_empty_p(r2));
assert(!shared_string_empty_p(q));
shared_string_swap(q, r2);
assert(!shared_string_empty_p(r2));
assert(shared_string_empty_p(q));
// todo: shared_string_splice
// todo: shared_string_erase
shared_string_release(r2);
shared_string_clear(q);
shared_string_release(r);
shared_string_clear(p2);
}
static void test_string_io(void)
{
shared_string_t *p = shared_string_new();
shared_string_t *q = shared_string_make("Hello world");
FILE *f = m_core_fopen ("a-mshared-ptr.dat", "wt");
if (!f) abort();
shared_string_out_str(f, q);
fclose (f);
f = m_core_fopen ("a-mshared-ptr.dat", "rt");
if (!f) abort();
bool b = shared_string_in_str (p, f);
assert (b == true);
assert (shared_string_equal_p (p, q));
fclose(f);
shared_string_reset(p);
M_LET(str, STRING_OPLIST) {
shared_string_get_str(str, q, false);
assert (string_cmp_str (str, "\"Hello world\"") == 0);
const char *sp;
b = shared_string_parse_str(p, string_get_cstr(str), &sp);
assert(b);
assert(*sp == 0);
assert(shared_string_equal_p(p, q));
}
shared_string_clear(p);
shared_string_release(q);
}
// TEST WITH ARRAY
ARRAY_DEF(array, int)
// Provide a FULL_P operator
static inline bool array_full_p(const array_t a) { return array_size(a) >= 10; }
#define M_OPL_array_t() M_OPEXTEND( ARRAY_OPLIST(array, M_BASIC_OPLIST), SUBTYPE(int), FULL_P(array_full_p) )
SHARED_PTR_DECL(shared_array, M_OPL_array_t() )
static int callback1(void *data, const int *el)
{
int *s_ptr = (int *) data;
*s_ptr += *el;
return 0;
}
static int callback2(void *data, const int *el)
{
int *s_ptr = (int *) data;
return *s_ptr == *el;
}
static int callback3(void *data, int *el)
{
int *s_ptr = (int *) data;
*el *= *s_ptr;
return 0;
}
static void test_array(void)
{
shared_array_t *p = shared_array_new();
bool b;
int j;
for(int i = 0 ; i < 10; i++) {
assert(!shared_array_full_p(p));
assert(shared_array_size(p) == (size_t) i);
b = shared_array_try_push(p, i);
assert(b);
}
// We can only push 10 elements before being full.
b = shared_array_try_push(p, 11);
assert(!b);
assert(shared_array_full_p(p));
shared_array_get(&j, p, 4);
assert( j == 4);
shared_array_set_at(p, 4, 5);
shared_array_get(&j, p, 4);
assert( j == 5);
shared_array_set_at(p, 4, 4);
shared_array_get(&j, p, 4);
assert( j == 4);
int s = 0;
int r = shared_array_for_each(p, callback1, &s);
assert(r == 0);
assert(s == 9*10/2);
s = 4;
r = shared_array_for_each(p, callback2, &s);
assert(r == 1);
s = 2;
r = shared_array_apply(p, callback3, &s);
assert(r == 0);
s = 0;
r = shared_array_for_each(p, callback1, &s);
assert(r == 0);
assert(s == 9*10);
for(int i = 9 ; i >= 0; i--) {
b = shared_array_try_pop(&j, p);
assert(b);
assert(i * 2== j);
}
b = shared_array_try_pop(&j, p);
assert(!b);
shared_array_safe_get(&r, p, 23);
assert(r == 0);
assert(shared_array_size(p) == 24);
shared_array_erase(p, 10);
assert(shared_array_size(p) == 23);
b = shared_array_get(&r, p, 22);
assert(b);
assert(r == 0);
shared_array_release(p);
}
ARRAY_DEF(array_str, string_t)
SHARED_PTR_DECL(shared_array_str, ARRAY_OPLIST(array_str, M_STRING_OPLIST))
static void test_array_string(void)
{
shared_array_str_t *p = shared_array_str_new();
M_LET( (x, ("Hello")), string_t)
shared_array_str_push(p, x);
string_t tmp;
string_init_set_str(tmp, "World");
shared_array_str_push_move(p, &tmp); // No need to clear tmp
shared_array_str_emplace(p, "!");
bool b = shared_array_str_try_emplace(p, " ");
assert(b);
string_init_set_str(tmp, "!");
b = shared_array_str_try_push_move(p, &tmp); // No need to clear tmp
assert(b);
shared_array_str_release(p);
}
// TEST WITH LIST
LIST_DEF(list, float)
#define M_OPL_list_t() LIST_OPLIST(list, M_BASIC_OPLIST)
SHARED_WEAK_PTR_DECL(shared_list, M_OPL_list_t())
SHARED_WEAK_PTR_DEF_EXTERN(shared_list, list_t)
LIST_DUAL_PUSH_DEF(list2, int)
SHARED_WEAK_PTR_DEF(shared_list2, list2_t, LIST_OPLIST(list2))
// TEST WITH DICT
DICT_DEF2(dict, float, float)
#define M_OPL_dict_t() DICT_OPLIST(dict, M_BASIC_OPLIST, M_BASIC_OPLIST)
SHARED_WEAK_PTR_DECL_AS(shared_dict, shared_dict_t, M_OPL_dict_t())
SHARED_WEAK_PTR_DEF_EXTERN_AS(shared_dict, shared_dict_t, dict_t)
DICT_DEF2(string_pool, string_t, string_t)
#define M_OPL_string_pool_t() DICT_OPLIST(string_pool, STRING_OPLIST, STRING_OPLIST)
SHARED_PTR_DEF(string_pool_ts, string_pool_t, M_OPL_string_pool_t() )
DICT_SET_DEF(dict2, int)
SHARED_PTR_DEF(shared_dict2, dict2_t, DICT_SET_OPLIST(dict2))
/* OA dict needs more operators than the default ones to work */
static inline bool int_oor_equal_p(int s, unsigned char n)
{
return s == -n;
}
static inline void int_oor_set(int *s, unsigned char n)
{
*s = -n;
}
#define INT_OA_OPLIST \
M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(int_oor_equal_p), OOR_SET(API_2(int_oor_set)))
DICT_OA_DEF2(dict3, int, INT_OA_OPLIST, int, M_BASIC_OPLIST)
SHARED_PTR_DEF(shared_dict3, dict3_t, DICT_OPLIST(dict3))
// TEST WITH TUPLE
TUPLE_DEF2(point, (x, int), (y, int))
#define M_OPL_point_t() TUPLE_OPLIST(point, M_BASIC_OPLIST, M_BASIC_OPLIST)
SHARED_PTR_DEF(shared_point, point_t)
//SHARED_PTR_DECL(shared_point, ARRAY_OPLIST(array_str, M_STRING_OPLIST))
//M_OPL_point_t())
// TEST WITH VARIANT
VARIANT_DEF2(dimension, (x, int), (y, float))
#define M_OPL_dimension_t() VARIANT_OPLIST(dimension, M_BASIC_OPLIST, M_BASIC_OPLIST)
SHARED_PTR_DEF(shared_dimension, dimension_t)
// TEST WITH DEQUE
DEQUE_DEF(deque1, int)
// Provide a FULL_P operator
static inline bool deque1_full_p(const deque1_t a) { return deque1_size(a) >= 10; }
#define M_OPL_deque1_t() M_OPEXTEND( DEQUE_OPLIST(deque1, M_BASIC_OPLIST), SUBTYPE(int), FULL_P(deque1_full_p) )
SHARED_PTR_DEF(shared_deque1, deque1_t)
PRIOQUEUE_DEF(prio1, int)
SHARED_PTR_DEF(shared_prio1, prio1_t, PRIOQUEUE_OPLIST(prio1))
// TEST WITH B+TREE
BPTREE_DEF2(bptree1, 10, int, int)
SHARED_PTR_DEF(shared_bptree1, bptree1_t, BPTREE_OPLIST2(bptree1, M_BASIC_OPLIST, M_BASIC_OPLIST))
BPTREE_DEF(bptree2, 10, int)
SHARED_PTR_DEF(shared_bptree2, bptree2_t, BPTREE_OPLIST(bptree2))
// TEST WITH R/B TREE
RBTREE_DEF(rbtree1, int)
SHARED_PTR_DEF(shared_rbtree1, rbtree1_t, RBTREE_OPLIST(rbtree1))
/********************************/
static void conso(void *p)
{
shared_deque1_t *ptr = (shared_deque1_t*) p;
for(int i = 0; i < 1000; i++) {
int j;
shared_deque1_pop(&j, ptr);
assert (j == i);
}
shared_deque1_release(ptr);
}
static void test_thread(void)
{
m_thread_t idx;
shared_deque1_t *ptr = shared_deque1_new();
m_thread_create (idx, conso, shared_deque1_acquire(ptr));
for(int i = 0; i < 1000; i++) {
// shared_deque1 is full if number of stored element is >= 10
shared_deque1_push (ptr, i);
}
shared_deque1_release(ptr);
m_thread_join(idx);
}
/************************************************************************/
// Define effective code when only _DECL was used.
START_COVERAGE
SHARED_PTR_DEF_EXTERN(shared_string, string_t, STR_OPL)
END_COVERAGE
// Non measured code
#include "test-mshared-ptr.h"
static void test_string2(void)
{
M_LET( (x, ("Hello")), string_t) {
shared_string_t *p = shared_string_new_from(x);
assert(!shared_string_empty_p(p));
assert(string_equal_str_p(*shared_string_cref(p), "Hello"));
assert(string_equal_str_p(*shared_string_ref(p), "Hello"));
shared_string_release(p);
}
shared_string_t *p = shared_string_new();
shared_string_t *q = shared_string_make("2");
shared_string_t *r = shared_string_make("1");
shared_string_t *rr = shared_string_make("3");
shared_string_add(p, q, r);
assert(shared_string_equal_p(p, rr));
shared_string_add(p, r, q);
assert(shared_string_equal_p(p, rr));
shared_string_add(p, r, r);
assert(shared_string_equal_p(p, q));
shared_string_add(p, p, r);
assert(shared_string_equal_p(p, rr));
shared_string_add(p, r, r);
assert(shared_string_equal_p(p, q));
shared_string_add(p, r, p);
assert(shared_string_equal_p(p, rr));
shared_string_sub(p, q, r);
assert(shared_string_equal_p(p, r));
shared_string_add(p, p, p);
assert(shared_string_equal_p(p, q));
shared_string_mul(p, q, r);
assert(shared_string_equal_p(p, q));
shared_string_div(p, q, r);
assert(shared_string_equal_p(p, q));
shared_string_reset(p);
shared_string_release(p);
shared_string_release(q);
shared_string_release(r);
shared_string_release(rr);
}
/************************************************************************/
#define MAX_NUM 200
static void conso_thread1_string(void *p)
{
shared_string_t *ptr = (shared_string_t*) p;
bool tmp[MAX_NUM] = {0};
for(int i = 1; i < MAX_NUM; i++) {
m_string_unicode_t j;
shared_string_pop(&j, ptr);
assert (j > 0 && j < MAX_NUM);
assert(tmp[j] == false);
tmp[j] = true;
}
shared_string_release(ptr);
}
static void test_thread1_string(void)
{
m_thread_t idx;
shared_string_t *ptr = shared_string_new();
m_thread_create (idx, conso_thread1_string, shared_string_acquire(ptr));
for(int i = 1; i < MAX_NUM; i++) {
shared_string_push (ptr, (m_string_unicode_t) i);
}
shared_string_release(ptr);
m_thread_join(idx);
}
// This will test the proper handling of the read lock,
// by pushing it to its limit.
#define MAX_READER 8
static void conso_thread2_string(void *p)
{
shared_string_t *ptr = (shared_string_t*) p;
size_t size = shared_string_size(ptr);
for(int i = 0; i < 1000; i++) {
size_t n = shared_string_size(ptr);
assert( n >= size);
size = n;
}
shared_string_release(ptr);
}
static void test_thread2_string(void)
{
m_thread_t idx[MAX_READER];
shared_string_t *ptr = shared_string_new();
for(int i = 0; i < MAX_READER; i++) {
m_thread_create (idx[i], conso_thread2_string, shared_string_acquire(ptr));
}
for(int i = 1; i < 100000; i++) {
shared_string_push (ptr, (m_string_unicode_t) i);
}
shared_string_release(ptr);
for(int i = 0; i < MAX_READER; i++) {
m_thread_join(idx[i]);
}
}
/************************************************************************/
int main(void)
{
test_string();
test_string2();
test_thread1_string();
test_thread2_string();
test_string_io();
test_double();
test_array();
test_array_string();
test_thread();
exit(0);
}
+7
View File
@@ -0,0 +1,7 @@
// Header for coverage purpose
// Expand here all code that shall not be measured in coverage.
// Since the functions are not declared as static inline, their coverage is measured
SHARED_PTR_DEF_EXTERN_AS(shared_int, SharedInt, int, M_BASIC_OPLIST)
SHARED_PTR_DEF_EXTERN_AS(shared_double, SharedDouble, double, M_OPEXTEND(M_BASIC_OPLIST, TYPE(double)))
SHARED_PTR_DEF_EXTERN(shared_array, array_t)
SHARED_PTR_DEF_EXTERN(shared_array_str, array_str_t, ARRAY_OPLIST(array_str, M_STRING_OPLIST))
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff. Show more