Author SHA1 Message Date
Patrick Pelissier 4a96845fac Improve error messages by the compiler
if the number of arguments doesn't match what is expected.
2023-11-01 11:14:27 +01:00
Patrick Pelissier b742881ebc WIP interface 2023-11-01 11:14:27 +01:00
Patrick Pelissier f7f5db61d3 Fix formatting 2023-11-01 11:13:54 +01:00
Patrick Pelissier 32b00eb433 Add new operator GENTYPE 2023-10-21 19:05:08 +02:00
Patrick Pelissier 796f2619e5 Change M_BY_NARGS semantics:
simplify suffix by __ # NARGS
instead of __NARGS_ # NARGS

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

Define new user API:
* M_USE_EXTERN_DECL : define it before including any MLIB header to requests to call the M*LIB functions and don't inline then.
* M_USE_EXTERN_DEF  : define it once in a translation unit to define the M*LIB functions. This translation unit shall contain all templated macro too.
If you use one option, you need to use the other one too for another translation unit.
2023-06-11 23:32:02 +02:00
Patrick Pelissier 2628536d20 Add +m_string_it_set_ref 2023-06-10 01:50:14 +02:00
Patrick Pelissier ecfa4a9152 Add new function string_pop_u
to pop the last unicode code point
of a string.
2023-06-03 00:59:34 +02:00
Patrick Pelissier dafe4bcec5 Add basic test for M_DEFAULT_ARGS
Print hint to developper in case of incorrect usage
2023-06-03 00:56:13 +02:00
Patrick Pelissier 8544a7f289 Update example 2023-05-30 14:01:30 +02:00
Patrick Pelissier aa18915d61 Fix overflow computation 2023-05-21 00:15:58 +02:00
Patrick Pelissier df4bc0a56c Remove alloc warning for analyzer:
it is not able to track allocations correctly and only give false warnings.
valgraind or memory sanitizer are much more reliable to track memory errors.
2023-05-13 00:52:06 +02:00
Patrick Pelissier b6046e5e23 Fix new warnings with GCC13 2023-05-13 00:52:06 +02:00
Patrick Pelissier b487bfe5ad Adding missing inline 2023-05-08 23:54:16 +02:00
PpHd 408272c810 Merge pull request
Add m_string_cat_vprintf
2023-05-08 23:35:54 +02:00
kitterion 6a9ee17eca Add tests 2023-05-08 23:17:19 +03:00
kitterion 05c5a94634 Add m_string_cat_vprintf 2023-05-08 20:52:47 +03:00
Patrick Pelissier b1b0927e9f Update examples 2023-05-06 21:54:08 +02:00
Patrick Pelissier 37770365a1 Fix several bugs:
* in case of some invalid format starting with ']' directly it wrongly assume that the parsing was valid.
* incase of exception handling, and an exception was raised during the internal parsing, some destructors wern't called.
2023-05-05 22:16:39 +02:00
Patrick Pelissier 82bdcc685a Add internal macro MQLET for internal M_LET use 2023-05-05 22:15:13 +02:00
Patrick Pelissier 26fa733f70 Fix format 2023-05-05 22:14:41 +02:00
Patrick Pelissier cd2fc05573 Add FIXED_SIZE property 2023-05-05 21:41:05 +02:00
Patrick Pelissier 33430d4359 Update 2023-04-29 18:46:09 +02:00
Patrick Pelissier dc95b49f05 Update 2023-04-29 12:07:10 +02:00
Patrick Pelissier 3ec24cfa40 Update 2023-04-29 11:41:08 +02:00
Patrick Pelissier 12b099dcf8 Update 2023-04-29 11:25:56 +02:00
Patrick Pelissier b885d6136c Update 2023-04-29 11:20:00 +02:00
Patrick Pelissier b51c4bea2b Update 2023-04-29 11:19:54 +02:00
Patrick Pelissier 302c5eb300 Remove export passthrough of NEW/DEL/REALLOC/FREE interface
from the container oplist.
It has no real practical purpose.
NEW/DEL/REALLOC/FREE shall be a property of the container
to be defined, and not a property for an already defined container.
2023-04-29 10:50:04 +02:00
Patrick Pelissier 6d2900a2bd Improve coverage of test suite. 2023-04-27 00:04:44 +02:00
Patrick Pelissier 573e09c56e Remove variable number of elements support which was not working.
Request an explicit number of elements.
2023-04-27 00:04:03 +02:00
Patrick Pelissier 0a39a17d36 The undefined number of element value of serial _array_start
is now (size_t)-1 and no longer 0.
Moreoever, a serialization interface may refuse outputting data
if it doesn't know the number of elements.
In which case, the container shall explictly compute the number
of elements and recall the serial object _array_start method.
2023-04-27 00:01:50 +02:00
Patrick Pelissier a913d0947c Rewrite code to fix coverage 2023-04-26 23:34:13 +02:00
Patrick Pelissier abbd8ad6ce Update 2023-04-26 22:56:23 +02:00
Patrick Pelissier a09c9fe1c3 Update 2023-04-26 22:56:23 +02:00
Patrick Pelissier 219c62ff5d Provide a way to alter the standard naming convention for the functions
Close #72
2023-04-22 09:33:08 +02:00
Patrick Pelissier 272a326375 Fix warning issue for coverage 2023-04-22 09:23:18 +02:00
Patrick Pelissier eca79f2c80 Improve coverage of test suite 2023-04-21 22:44:34 +02:00
Patrick Pelissier 2feda4059f Fix issue in case of invalid IO 2023-04-21 22:44:23 +02:00
Patrick Pelissier de0ef67cf2 Fix build issue with VC++ 2023-04-19 23:06:23 +02:00
Patrick Pelissier 95762a058c Fix const attribute of _get_emplace 2023-04-19 22:42:21 +02:00
Patrick Pelissier acd8671017 Fix test case according to contract. 2023-04-19 22:37:54 +02:00
Patrick Pelissier a6c1688f23 Fix issue in remove root node for tree (part 2) 2023-04-19 22:37:40 +02:00
Patrick Pelissier f6ed549ce5 Fix issue in tree_remove for root node index (Part 2)
Enable support of removing root node
In this case, it considers the first child of the root as the new root (breaking symetry)
2023-04-18 23:24:48 +02:00
Patrick Pelissier 8639fdbd98 Fix issue in tree_remove when it is root node (part 1) 2023-04-18 22:56:56 +02:00
Patrick Pelissier 693b09387f Improve coverage of test suite. 2023-04-18 22:19:41 +02:00
Patrick Pelissier 4052d3794b Tree: Transform runtime tests into assertion
as it is proven that it cannot happen.
2023-04-18 22:19:28 +02:00
Patrick Pelissier 044e9c3bf7 Fix benchmark 2023-04-17 16:56:57 +02:00
Patrick Pelissier 45a2a1afb2 Add bench for CC 2023-04-16 21:16:04 +02:00
Patrick Pelissier b44e4e09dd Don't use builtin for clang as they are not properly supported. 2023-04-16 20:11:56 +02:00
Patrick Pelissier 5158059ec4 Make m_throw a classic "cold" function rather than a static inline function.
Since the function shall be rarely called, there is no point in inlining/duplicating it
in several units.
2023-04-16 19:26:15 +02:00
Patrick Pelissier 7279011f0a Use of the builtin versions of setjmp /longjmp for GCC / CLANG
They are more than twice faster on extreme bench of M\_LET / M\_TRY scenario
(and still twice slower than C++).
They also use less stack usage.
2023-04-16 19:18:04 +02:00
Patrick Pelissier 79369e9b05 Update README 2023-04-16 19:16:08 +02:00
Patrick Pelissier 58a7a7c214 Add gene TRY 2023-04-16 19:15:54 +02:00
Patrick Pelissier e3e94ca238 Add M_ATTR_COLD_FUNCTION attribute 2023-04-16 19:15:22 +02:00
Patrick Pelissier ae81271b2a Fix name of internal variable for M_DEFER 2023-04-16 08:33:45 +02:00
Patrick Pelissier d9c3724cff Compatiblity with C++ 2023-04-15 23:18:45 +02:00
Patrick Pelissier 1ff9848384 Update test so that it tests also the case of throwing without any try section (override M_RAISE_FATAL) 2023-04-15 22:51:57 +02:00
Patrick Pelissier 83e7f2a641 Use of new M_ATTR_NO_RETURN attribute 2023-04-15 22:50:47 +02:00
Patrick Pelissier 6580601969 Add M_ATTR_NO_RETURN attribute support (not C23 compatible yet) 2023-04-15 22:50:27 +02:00
Patrick Pelissier ad023bb09d Improve coverage of the test suite 2023-04-13 23:27:01 +02:00
Patrick Pelissier b5df21f9c5 Simplify function: for LCA both iterators shall be in the same tree 2023-04-13 23:26:12 +02:00
Patrick Pelissier d2dccbf514 Add table of contents 2023-04-12 20:35:22 +02:00
Patrick Pelissier a8075cbb05 Transform runtime test into assertion
as it is proven that it cannot happen.
2023-04-11 21:57:56 +02:00
Patrick Pelissier 1322afc4c9 Improve coverage of test suite 2023-04-11 21:53:14 +02:00
Patrick Pelissier 0f885fb158 Fix coverage source for m-core 2023-04-11 21:32:57 +02:00
Patrick Pelissier 25a2eb0875 Correct syntax 2023-04-11 21:32:38 +02:00
Patrick Pelissier 6bcd51f10f Complete coverage synthesis
with branch coverage information
2023-04-11 21:09:40 +02:00
Patrick Pelissier 9c1cb9c835 Update doc 2023-04-11 00:07:33 +02:00
Patrick Pelissier 19cb329ae6 Simplfy example 2023-04-11 00:05:41 +02:00
Patrick Pelissier 8099e6a94f Update doc 2023-04-11 00:04:46 +02:00
Patrick Pelissier ce6cc080d6 Improve coverage of test suite 2023-04-10 23:09:18 +02:00
Patrick Pelissier a27b0b4014 Fix wrong parsing of malformed dict. 2023-04-10 23:08:51 +02:00
Patrick Pelissier e74f80cc59 Update doc 2023-04-08 11:32:15 +02:00
Patrick Pelissier bafd307247 Update performance chapter 2023-04-08 11:27:37 +02:00
Patrick Pelissier 6bed3ac697 Print time with 2 digits after the decimal 2023-04-08 11:25:59 +02:00
Patrick Pelissier b8a9073fc9 Fix issue to generate graph without any repetition 2023-04-08 11:25:05 +02:00
Patrick Pelissier da3e71c676 Fix introduced warning in release mode 2023-04-08 11:23:32 +02:00
Patrick Pelissier 4ea83e129e Improve coverage of test suite 2023-04-08 00:17:33 +02:00
Patrick Pelissier fc34fa80b5 Properly mark condition for entering memory failure. 2023-04-07 23:50:03 +02:00
Patrick Pelissier 4f608ec266 Fix target of run 2023-04-06 22:02:49 +02:00
Patrick Pelissier ccaa54d2a5 Improve coverage of the tet suite 2023-04-06 21:55:21 +02:00
Patrick Pelissier 095bf7070d Rewrite loop to avoid final assert(0) 2023-04-06 21:55:06 +02:00
Patrick Pelissier 676b12d5d2 Fix warnings 2023-04-05 23:15:39 +02:00
Patrick Pelissier f858aa2cb5 Improve coverage of the test suite. 2023-04-05 23:07:52 +02:00
Patrick Pelissier 9d77448a25 Reduce optimization level to 0
Otherwise the coverage results are not reliable:
* some uncovered lines are not reported as uncovered
* some covered lines are reported as uncovered (!)
2023-04-05 23:04:22 +02:00
Patrick Pelissier 8babc33b91 Rewrite code differently to be more robust
and improve coverage of the test suite
2023-04-05 22:41:33 +02:00
Patrick Pelissier 352d7cf725 Improve coverage of the test suite 2023-04-05 22:22:49 +02:00
Patrick Pelissier a6993ab758 Overload assert in coverage mode
to avoid polluting the report with untaken branch by the assert macro.
2023-04-05 22:22:01 +02:00
Patrick Pelissier 48260ac2a6 Ignore temporary coverage files 2023-04-05 22:21:23 +02:00
Patrick Pelissier e4afb024b9 Improve coverage of test suite 2023-04-05 00:38:44 +02:00
Patrick Pelissier 63008af30f Add assertions 2023-04-05 00:38:32 +02:00
Patrick Pelissier 1828aca65e Fix preprocessing 2023-04-05 00:38:16 +02:00
Patrick Pelissier 9354e9717d Remove unused test function 2023-04-04 01:06:32 +02:00
Patrick Pelissier 100c0f22fe Improve test coverage 2023-04-04 00:48:03 +02:00
Patrick Pelissier ff778528f8 Improve coverage of the test suite 2023-04-04 00:22:49 +02:00
Patrick Pelissier 8f1a1df617 Improve coverage of test suite 2023-04-03 16:38:54 +02:00
Patrick Pelissier 28faf815d7 Improve robustness against broken I/O file 2023-04-03 14:09:34 +02:00
Patrick Pelissier fe3b703d0a Don't expand isspace for coverage tests 2023-04-03 14:09:07 +02:00
Patrick Pelissier 0aa6a9f0cf Update comments 2023-04-03 13:19:50 +02:00
Patrick Pelissier 67832ac918 Add more tests case for IO parsing 2023-04-03 13:18:28 +02:00
Patrick Pelissier 669a3297c7 Fix issue in ispace usage and allow more space in formating (to be more uniform. The allowed spaces before were in strange place only) 2023-04-03 13:18:10 +02:00
Patrick Pelissier 25d4829d7b Add utilities function for parsing FILE or char* while skipping space character 2023-04-03 13:17:25 +02:00
Patrick Pelissier ebab05a789 Improve coverage of test suite 2023-04-03 12:36:35 +02:00
Patrick Pelissier 727df5b7bf Fix coverage 2023-04-03 00:36:18 +02:00
Patrick Pelissier aebe1308fc Improve coverage computation 2023-04-03 00:23:05 +02:00
Patrick Pelissier 1d163a396e Replace assert by M_ASSERT 2023-04-03 00:12:00 +02:00
Patrick Pelissier 7534aa0d61 Add internal M_UNLIKELY_NOMEM to help coverage computation 2023-04-03 00:05:29 +02:00
Patrick Pelissier 6976d4e23d Remove (broken) auto check of coverage 2023-04-02 23:02:48 +02:00
Patrick Pelissier 6b7954e141 Fix coverage for new files 2023-04-02 22:52:40 +02:00
Patrick Pelissier c27d76a83b Remove assert & contract from coverage to avoid polluting reports 2023-04-02 22:45:56 +02:00
Patrick Pelissier 0e3081bfc6 Include branch probabilities in report 2023-04-02 22:45:35 +02:00
Patrick Pelissier d754e9a15f Update doc for _get_emplace functions for associative array 2023-03-29 22:59:54 +02:00
Patrick Pelissier 4e4e21793c Add _get_emplace functions for associative array 2023-03-29 22:59:43 +02:00
Patrick Pelissier c515da9f37 Update documentation 2023-03-29 22:50:51 +02:00
Patrick Pelissier 961b93bd4c Add test for M_LET + associative array 2023-03-28 23:43:49 +02:00
Patrick Pelissier 39a719c3f0 Fix warnings 2023-03-28 23:43:21 +02:00
Patrick Pelissier 8b643b4878 Add support for emplace detection in associative array + M_LET 2023-03-28 23:43:08 +02:00
Patrick Pelissier 876c394fba Fix format 2023-03-28 23:42:25 +02:00
Patrick Pelissier 81439c9437 Fix grammar 2023-03-27 01:05:53 +02:00
Patrick Pelissier 191ef7d09e Fix grammar 2023-03-27 00:55:08 +02:00
Patrick Pelissier bd4131dd8f Fix typo 2023-03-27 00:53:31 +02:00
Patrick Pelissier cfe8293c64 Cleanup documentation 2023-03-25 10:25:52 +01:00
Patrick Pelissier ea2715aa75 Remove static assert. Some old compilers failed to simplify into a constant expression, resulting in build failure 2023-03-24 23:54:49 +01:00
Patrick Pelissier f69cec6820 Fix warning with clang+const char * type 2023-03-24 21:53:23 +01:00
Patrick Pelissier 085f371693 Update doc 2023-03-24 21:12:28 +01:00
Patrick Pelissier 16a0a5c1a3 Add new examples 2023-03-24 21:11:15 +01:00
Patrick Pelissier 3953f81884 Update 2023-03-24 21:10:59 +01:00
Patrick Pelissier 8365888e6e Add Oplist inheritanc chapter 2023-03-24 20:38:51 +01:00
Patrick Pelissier c3f6e3c5f0 Add trivial script to separate preproc of compilation for debug 2023-03-19 00:29:51 +01:00
Patrick Pelissier 4cafe4dce1 Fix steps to debug 2023-03-19 00:29:06 +01:00
Patrick Pelissier 9f8b97ae46 Rename fields of m_exception_t:
data->context
n->num
2023-03-18 18:11:23 +01:00
Patrick Pelissier 030bea9c11 Add test for M_DEFER & M_LET_IF in exceptions 2023-03-18 17:57:46 +01:00
Patrick Pelissier e33894f1b3 Finish support for M_DEFER / M_LET_IF & exceptions 2023-03-18 17:57:15 +01:00
Patrick Pelissier 66f5e6c67d Support for more than one expression in M_DEFER 2023-03-18 17:56:44 +01:00
Patrick Pelissier 78c6dce068 Update doc 2023-03-18 17:56:11 +01:00
Patrick Pelissier 0798480ad5 Refactoring & implement support for RAII with M_DEFER 2023-03-18 17:39:56 +01:00
Patrick Pelissier e8322215fd Update doc about clang 2023-03-18 17:39:20 +01:00
Patrick Pelissier 42d3042c15 Add NOCLEAR property to M_BASIC_OPLIST and prepare injection for M_DEFER 2023-03-18 17:25:08 +01:00
Patrick Pelissier b7214ad11d Adding test of NOCLEAR property 2023-03-18 17:20:30 +01:00
Patrick Pelissier 08b4bc9052 M_LET_IF reuses clear mechanism provided by M_DEFER 2023-03-18 15:14:31 +01:00
Patrick Pelissier d813171e8a Add new test generation 2023-03-18 09:15:39 +01:00
Patrick Pelissier d1094999f5 Update M_ASSERT to separate requirement for predicate 2023-03-18 09:15:11 +01:00
Patrick Pelissier c8354aa6ed Update doc 2023-03-18 08:08:33 +01:00
Patrick Pelissier ab3aa33fac Add M_SET_THROUGH_INIT_SET 2023-03-18 08:08:23 +01:00
Patrick Pelissier 01f2f376e2 Update doc 2023-03-18 00:21:46 +01:00
Patrick Pelissier 535e00366a Correct reported errors 2023-03-18 00:21:37 +01:00
Patrick Pelissier 42111ec22a Rename field of m_exception_t 2023-03-17 22:13:18 +01:00
Patrick Pelissier ef0e849616 Fix test 2023-03-17 22:04:50 +01:00
Patrick Pelissier 9c516c2055 Update doc 2023-03-17 21:56:27 +01:00
Patrick Pelissier db48b985c3 Update doc 2023-03-17 21:48:56 +01:00
Patrick Pelissier 888796ca06 Update doc 2023-03-17 21:48:07 +01:00
Patrick Pelissier 144ae9c5ed Add ID support for GAIA 2023-03-17 21:47:04 +01:00
Patrick Pelissier 6223c93b53 Update doc 2023-03-17 20:21:19 +01:00
Patrick Pelissier 30f7656822 Update documentation 2023-03-17 20:15:42 +01:00
PpHd 8209a0c32a Merge pull request #107 from jonadem/patch-1
Fix typos
2023-03-17 20:14:38 +01:00
Jona 92e6957320 Fix typos 2023-03-17 14:00:49 +01:00
Patrick Pelissier b3c9f6ee47 Add support of exception for Visual C++ 2023-03-14 20:00:11 +01:00
Patrick Pelissier ab8bbad3f4 Fix syntax 2023-03-13 23:54:52 +01:00
Patrick Pelissier 9047376b7f Add m-try.h in HEADER 2023-03-13 23:53:36 +01:00
Patrick Pelissier dfed8dbc50 Fix format 2023-03-13 23:53:26 +01:00
Patrick Pelissier 2d3c25cd85 TCC doesn't support the __thread attribute,
which is a GCC extension.
Use this extension only for GCC.
2023-03-13 23:45:28 +01:00
Patrick Pelissier bfa948dc7a Add WIP implementation for the exception memory model:
add header m-try that defines the M_TRY / M_CATCH macros
that macros similarly to the C++ try / catch keywords.
Add implementation for C++, GCC, CLANG and pure C compiler.
2023-03-13 23:31:50 +01:00
Patrick Pelissier 94b6d1b9fd Fix computation of HASH_FINAL 2023-03-13 22:17:10 +01:00
Patrick Pelissier 0ddd5ddc6d Add AARCH64 target for CI 2023-03-13 21:04:05 +01:00
Patrick Pelissier cba93d5bb4 Add SC library 2023-03-12 21:12:21 +01:00
Patrick Pelissier d02de75487 Add control that all initialized testobj are properly cleared at the end of the test suite 2023-03-12 20:29:58 +01:00
Patrick Pelissier 2f57766249 Rename NOEXCEPT to NOCLEAR property as NOEXCEPT
doesn't provide enough guarantee to the exception handler.
2023-03-12 20:23:31 +01:00
Patrick Pelissier 3ff01e37eb Update documentation for properties 2023-03-05 19:44:36 +01:00
Patrick Pelissier 5683df0f22 Adding test for M_TEST_NOEXCEPT_P 2023-03-05 19:44:21 +01:00
Patrick Pelissier 11dc7afc06 Add NOEXCEPT property and M_TEST_NOEXCEPT_P macro to test it 2023-03-05 19:44:04 +01:00
Patrick Pelissier 9e2ae3f101 Update example 2023-03-05 18:30:47 +01:00
Patrick Pelissier c35aa9c891 Replace the M_ASSERT(0) by M_ASSUME(0) since they are not reachable (in order to inform the compiler of this fact) 2023-03-05 00:50:40 +01:00
Patrick Pelissier 2fc31d8c12 Factorization find of item in prioqueue 2023-03-03 23:23:06 +01:00
Patrick Pelissier 4f8d0a7156 Split macro 2023-03-03 23:12:40 +01:00
Patrick Pelissier aaf5bbd6a0 Separate IO from core macro 2023-03-03 23:03:24 +01:00
Patrick Pelissier 2f76652f2b Maintenance 2023-03-03 22:50:14 +01:00
Patrick Pelissier 942cf32519 Maintenance: separate macros for type and core functions. 2023-03-03 22:02:07 +01:00
Patrick Pelissier c1c9cb9cd3 Update example 2023-03-03 20:12:50 +01:00
Patrick Pelissier 76bfb48685 Update freebsd CI to be more robust. 2023-03-02 23:41:59 +01:00
Patrick Pelissier afff412f7a Maintenance: continue splitting huge macros into smaller ones. 2023-03-02 00:39:08 +01:00
Patrick Pelissier e48dcdeecc Factorization & assertions 2023-02-27 14:05:59 +01:00
Patrick Pelissier 12fae5c36c Update example 2023-02-26 23:43:57 +01:00
Patrick Pelissier 3f9129c7ed Update comments 2023-02-26 23:43:57 +01:00
Patrick Pelissier b918f61f4f Update example 2023-02-26 23:43:57 +01:00
Patrick Pelissier 93b69d0f8b Fix call of memcpy with size=0 and src=NULL 2023-02-25 12:17:18 +01:00
Patrick Pelissier b3440a2edd Fix position of assertions 2023-02-24 20:35:32 +01:00
Patrick Pelissier 4140df5bfc Improve test suite 2023-02-24 19:56:56 +01:00
Patrick Pelissier fbab093d52 Avoid warnings about variables never read. 2023-02-24 19:56:26 +01:00
Patrick Pelissier f57a0e0bf8 Update README 2023-02-24 18:21:39 +01:00
Patrick Pelissier 925c06beb8 Fix usage of va_copy 2023-02-24 15:07:29 +01:00
Patrick Pelissier 8e2f67b794 Fix building on exe files: some targets don't link with LDFLAGS if it is not in the last argument 2023-02-23 12:42:11 +01:00
Patrick Pelissier f176d01e49 Fix README 2023-02-23 10:53:32 +01:00
Patrick Pelissier 2b99bbe956 Remove bool oplist as it is globally registered 2023-02-22 22:47:31 +01:00
Patrick Pelissier 700bfc5341 Add documentation for RET in API transformation 2023-02-22 22:47:17 +01:00
Patrick Pelissier 4062525821 Add test for RET in API transformation 2023-02-22 22:47:06 +01:00
Patrick Pelissier 800a1e5c36 Add support for RET in API transformation and register global bool oplist 2023-02-22 22:46:46 +01:00
Patrick Pelissier 1838f675a7 Add more comments, another alternative.
Rename some variables to be clearer.
2023-02-18 20:15:17 +01:00
Patrick Pelissier bf6c1341b3 Add comments to explain what we do
Always use the prefixed version of M*LIB
2023-02-18 09:58:30 +01:00
Patrick Pelissier 55b745b916 Use of M_WORKER_SPAWN_DEF2 in example:
it shows how to add paralelization to a code base.
2023-02-18 00:25:30 +01:00
Patrick Pelissier 5cb0d9214b Adding doc for M_WORKER_SPAWN_DEF2. 2023-02-18 00:25:17 +01:00
Patrick Pelissier 8f99b8e617 Adding test for M_WORKER_SPAWN_DEF2 2023-02-18 00:25:06 +01:00
Patrick Pelissier d2ce71a90a Adding M_WORKER_SPAWN_DEF2 for providing specialization
and type-safe variant of m_worker_spawn.
2023-02-18 00:24:20 +01:00
Patrick Pelissier 8562986eea Avoid dereferencing the table in asserts if the lock is done on the tree:
it usually means that the tree is partially accessed by other
threads (at least the derefencing methods which should be
safe).
2023-02-18 00:15:31 +01:00
Patrick Pelissier ee89858bf0 Add renaming of header in compatibility chapter 2023-02-16 00:20:07 +01:00
Patrick Pelissier 085d7157c0 Add compatibility layer with old header m-mutex
as a transition for current programs.
2023-02-16 00:10:55 +01:00
Patrick Pelissier 91f3174a36 Rename m-mutex header into m-thread
as it is not just about mutex but about thread functions
just like the header pthread or thread.h
2023-02-16 00:09:06 +01:00
Patrick Pelissier 56f51c59af Add comments about locale dependent functions. 2023-02-11 23:47:11 +01:00
Patrick Pelissier df0c95ca46 Adding note on locale for JSON outputs 2023-02-11 14:35:36 +01:00
Patrick Pelissier 482ec38e62 Fix wrong use of isprint function. 2023-02-11 14:34:13 +01:00
Patrick Pelissier 82743ca72c Update pipeline for freebsd 2023-02-11 09:28:12 +01:00
Patrick Pelissier 312821454c Avoid running tests in example for freebsd 2023-02-11 00:39:06 +01:00
Patrick Pelissier c953e87660 Link with stdthreads for FreeBSD 2023-02-11 00:11:48 +01:00
Patrick Pelissier 8846707c8b Fix issue on FreeBSD when building in C11 mode:
thrd_sucess is not equal to 0 on this system,
so replace 0 by thrd_success
2023-02-11 00:08:51 +01:00
Patrick Pelissier bf7d4eee55 Avoid using .log suffix for failure checks
some Make program (like FreeBSD one) doesn't handle
priority of a specific rule over a generic rule.
So the generic rule is used for failure checks and it fails the check.
Use another suffix to avoid collision between rules
2023-02-10 23:57:28 +01:00
Patrick Pelissier d4dc85f89a Add FreeBSD target for CI 2023-02-10 21:36:04 +01:00
Patrick Pelissier 130c32e7d4 Include only sysinfoapi.h on Windows machine to reduce dependency
Add fallback to _SC_NPROCESSORS_CONF if _SC_NPROCESSORS_ONLN is not supported (Android)
2023-02-07 23:17:03 +01:00
Patrick Pelissier 2bcaba3bc0 Factorize into smaller macros 2023-02-06 22:17:12 +01:00
Patrick Pelissier 443c805c22 Fix format 2023-02-06 21:28:19 +01:00
Patrick Pelissier 414c87bb69 Rename internal macros 2023-02-05 23:38:29 +01:00
Patrick Pelissier e9ca506d93 Factorization of macros 2023-02-03 20:53:27 +01:00
Patrick Pelissier 8e58f7d637 Factorize into smaller generation macros. Add emplace support for MPMC/SPSC queues 2023-02-01 20:58:28 +01:00
Patrick Pelissier 254beaf3c7 Factorize into smaller generation macros 2023-02-01 20:43:01 +01:00
Patrick Pelissier 27735eb224 Migrate version of action scripts. 2023-01-29 00:19:22 +01:00
Patrick Pelissier 0afc81bd54 Simplify example by using emplace function. 2023-01-28 14:49:17 +01:00
Patrick Pelissier 39e67eebdc EMPLACE: Fix expansion of methods as macro in the oplist
by performing another evaluation step.
2023-01-28 14:49:07 +01:00
Patrick Pelissier 840226c7fd TREE: Add documentation of emplace functions 2023-01-28 14:48:58 +01:00
Patrick Pelissier e421851525 TREE: Add tests for emplace functions 2023-01-28 14:48:50 +01:00
Patrick Pelissier f70b52c9e4 TREE: Add support for emplace functions 2023-01-28 14:48:12 +01:00
Patrick Pelissier 40249ce5bc Separate macros into smaller more functionnal ones 2023-01-28 14:48:01 +01:00
Patrick Pelissier 97ff413f01 Maintenance:
Separate big macros into several smaller ones
2023-01-22 23:33:13 +01:00
Patrick Pelissier 8e4ac99a35 Separate macro into smaller macros (more maintenable) 2023-01-20 23:45:40 +01:00
Patrick Pelissier 4366e8eeab Separate macro into smaller macros
to ease reading and enable easier customization of generation
2023-01-20 23:45:40 +01:00
Patrick Pelissier 4e5e498bb2 Add new library 2023-01-09 23:35:33 +01:00
Patrick Pelissier 870a6f4b57 Update to 0.7.1 2023-01-02 13:06:29 +01:00
Patrick Pelissier a2ae209992 Happy new year 2023-01-02 12:57:41 +01:00
132 changed files with 11392 additions and 5953 deletions

No files matched your search

+40 -8
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: CPU Information
run: lscpu || sysctl -a|grep cpu || echo "CPU information not available"
- name: make check C99
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: update package list
run: sudo apt-get update
- name: install multilib
@@ -50,7 +50,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: CPU Information
run: lscpu || echo "CPU information not available"
- name: Clone TCC
@@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: make sanitize
working-directory: ./tests
run: make sanitize -j 2
@@ -79,7 +79,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: make check
shell: bash
run: mingw32-make -j 2 CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
@@ -90,7 +90,7 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
@@ -103,7 +103,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: make check
shell: cmd
working-directory: ./tests
@@ -113,9 +113,41 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: make check
shell: cmd
working-directory: ./tests
run: Make-check-cl.bat clang-cl
build_freebsd:
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0
with:
usesh: true
run: |
freebsd-version
cd tests
make check -j 2 CC="cc -std=c99 -Wno-unknown-warning-option -Werror" && make clean && make check -j 2 CC="cc -std=c11 -Wno-unknown-warning-option -Werror" LDFLAGS="-lstdthreads" && make clean
# The host should always be linux
build_aarch64:
runs-on: ubuntu-latest
name: Build on aarch64
steps:
- uses: actions/checkout@v3
- uses: uraimo/run-on-arch-action@v2.5.0
name: Run commands on aarch64
id: make
with:
arch: aarch64
distro: bullseye
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
+3 -3
View File
@@ -28,7 +28,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -36,7 +36,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -50,4 +50,4 @@ jobs:
run: make exe
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
+5
View File
@@ -38,8 +38,13 @@ depend
other/
a*.dat
config
config.link
*.log
.vscode
.vs
tmp-serial.*
*~
*.gcda
*.gcno
*.gcov
*.synt
+1 -1
View File
@@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2017-2022, Patrick Pelissier
Copyright (c) 2017-2023, Patrick Pelissier
All rights reserved.
Redistribution and use in source and binary forms, with or without
+3 -3
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2022, Patrick Pelissier
# Copyright (c) 2017-2023, Patrick Pelissier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -34,10 +34,10 @@ DESTDIR=
# Package name & version.
PACKAGE=m_lib-$(VERSION)
VERSION=0.7.0
VERSION=0.7.1
# Define the contain of the distribution tarball
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-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-tuple.h m-variant.h m-worker.h
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-thread.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h
DOC1=LICENSE README.md
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c
+940 -282
View File
File diff suppressed because it is too large. Load diff
+13 -2
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2022, Patrick Pelissier
# Copyright (c) 2017-2023, Patrick Pelissier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ pgo:
#########################################################################
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-cmc bench-sparsepp bench-densehashmap bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-emhash bench-hopscotchmap bench-uthash bench-qlibc bench-libsrt bench-nedtries bench-rigtorp-hashmap bench-ctl bench-stc bench-pottery
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-cmc bench-sparsepp bench-densehashmap bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-emhash bench-hopscotchmap bench-uthash bench-qlibc bench-libsrt bench-nedtries bench-rigtorp-hashmap bench-ctl bench-stc bench-pottery bench-CC
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
queue: bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-rigtorp-mpmc-queue
@@ -388,6 +388,17 @@ bench-pottery0:
@./bench-pottery.exe 41
@./bench-pottery.exe 50
# CCLIB shall point to the source directory of CC (where the headers are)
bench-CC:
@if test -n "$${CCLIB}" ; then $(MAKE) bench-CC0 ; else echo "Nothing to be done for CC." ; fi
bench-CC0:
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CCLIB}/ bench-CC.c common.c -o bench-CC.exe
@./bench-CC.exe 10
@./bench-CC.exe 20
@./bench-CC.exe 40
@./bench-CC.exe 41
############################################################################
bench-mlib-json: bench-mlib
+140
View File
@@ -0,0 +1,140 @@
#define NDEBUG
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "common.h"
#include "cc.h"
/********************************************************************************************/
static void test_array(size_t n)
{
vec( unsigned int ) a1, a2;
init(&a1);
init(&a2);
for(size_t i = 0; i < n; i++) {
push(&a1, rand_get() );
push(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned long i = 0; i < n; i++) {
s += *get(&a1, i ) * *get(&a2, i );
}
g_result = s;
cleanup(&a1);
cleanup(&a2);
}
/********************************************************************************************/
static void test_list(size_t n)
{
list( unsigned int ) a1, a2;
init(&a1);
init(&a2);
for(size_t i = 0; i < n; i++) {
push(&a1, rand_get() );
push(&a2, rand_get() );
}
unsigned int s = 0;
for(unsigned *it1 = first( &a1 ), *it2 = first( &a2 ); it1 != end( &a1 ); it1 = next( &a1, it1 ), it2 = next(&a2, it2) ) {
s += *it1 * *it2;
}
g_result = s;
cleanup(&a1);
cleanup(&a2);
}
/********************************************************************************************/
static void
test_dict(size_t n)
{
map( unsigned long, unsigned long ) dict;
init(&dict);
for (size_t i = 0; i < n; i++) {
unsigned long value = rand_get();
unsigned long key = rand_get();
insert(&dict, key, value);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
unsigned long key = rand_get();
const unsigned long *val = get(&dict, key);
if (val != NULL)
s += *val;
}
g_result = s;
cleanup(&dict);
}
/********************************************************************************************/
typedef struct {
char a[256];
} char_array_t;
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
static inline bool char_equal_p (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a)==0; }
static inline bool char_cmp (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a); }
static inline void char_clear (char_array_t *a) { (void)a; }
static inline size_t char_hash(const char_array_t *a) {
size_t hash = 0;
const char *s = a->a;
while (*s) hash = hash * 31421 + (*s++) + 6927;
return hash;
}
#define CC_DTOR char_array_t, { char_clear(&val); }
#define CC_CMPR char_array_t, { return char_cmp(&val_1, &val_2); }
#define CC_HASH char_array_t, { return char_hash(&val); }
#include "cc.h"
static void
test_dict_big(size_t n)
{
map( char_array_t, char_array_t) dict;
init(&dict);
for (size_t i = 0; i < n; i++) {
char_array_t s1, s2;
sprintf(s1.a, "%u", rand_get());
sprintf(s2.a, "%u", rand_get());
insert(&dict, s1, s2);
}
rand_init();
unsigned int s = 0;
for (size_t i = 0; i < n; i++) {
char_array_t s1;
sprintf(s1.a, "%u", rand_get());
const char_array_t *val = get(&dict, s1);
if (val != NULL)
s ++;
}
g_result = s;
cleanup(&dict);
}
/********************************************************************************************/
const config_func_t table[] = {
{ 10, "List", 10000000, 0, test_list, 0},
{ 20, "Array", 100000000, 0, test_array, 0},
{ 40, "dict", 1000000, 0, test_dict, 0},
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
};
int main(int argc, const char *argv[])
{
test("CC", numberof(table), table, argc, argv);
exit(0);
}
+1 -1
View File
@@ -14,7 +14,7 @@
#include "m-mempool.h"
#include "m-string.h"
#include "m-buffer.h"
#include "m-mutex.h"
#include "m-thread.h"
#include "m-deque.h"
#include "m-concurrent.h"
#include "m-tuple.h"
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - String Bench
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -36,7 +36,7 @@
*/
/* Update of integrating M*LIB, LIBSRT, SDS, GLIB & POTTERY
Copyright (c) 2018-2022 Patrick Pelissier
Copyright (c) 2018-2023 Patrick Pelissier
*/
/*
+4 -2
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -235,7 +235,7 @@ test(const char library[], size_t n, const config_func_t functions[], int argc,
sprintf(filename, "plot-%s-%d.dat", library, arg.test_function);
graph_file = fopen(filename, "wt");
if (!graph_file) {
fprintf(stderr, "ERROR: Cannot create plot.dat\n");
fprintf(stderr, "ERROR: Cannot create the file '%s'\n", filename);
exit(-2);
}
fprintf(graph_file, "# plotting %s-%d : %s\n# N T", library, arg.test_function, functions[i].funcname);
@@ -271,6 +271,8 @@ test(const char library[], size_t n, const config_func_t functions[], int argc,
avg /= arg.repeat;
variance = (variance - arg.repeat * avg * avg) / (arg.repeat - 1);
ba = get_best_within(arg.repeat, measure, 1. + arg.within);
} else {
ba = avg;
}
// The average value shall be around best_within (and greater)
+4 -5
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - common functions for bench module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -31,7 +31,7 @@
// Needed for M_THREAD_ATTR
#if defined(MULTI_THREAD_MEASURE)
#include "m-mutex.h"
#include "m-thread.h"
#endif
#if defined(_WIN32)
@@ -55,7 +55,7 @@
// Number of elements of a table
#define numberof(x) (sizeof(x)/sizeof(x[0]))
/* Get the current CPU time. */
/* Get the current CPU time in microsecond. */
#if defined(_WIN32)
/* WINDOWS variant */
static inline unsigned long long
@@ -119,9 +119,8 @@ test_function(const char *str, size_t n, void (*func)(size_t))
(*func)(n);
end = cputime();
result = (double)(end-start) / 1000.0;
end = (end - start) / 1000U;
if (str != NULL) {
printf ("%20.20s time %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
printf ("%20.20s time %.2f ms for n = %lu [r=%lu]\n", str, result, (unsigned long) n, g_result);
}
return result;
}
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* MLIB - TEST module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1
View File
@@ -81,3 +81,4 @@ V0.6.*: * Rename M_DEFAULT_OPLIST in M_BASIC_OPLIST
so except if you are using the prefixed version of the strings
or the bounded strings API, you won't need to upgrade your code.
V0.7.*: * Rename m-mutex.h header in m-thread.h
+302 -49
View File
@@ -1,5 +1,252 @@
*Long term issues*
------------------
==========================================================
* TODO #33 : Handling of partially constructed object :ANOMALY:
** State
Support of partially constructed object needs special code to handle it
correctly (register a correct cleanup function in case of exception).
Current containers don't support such features.
** Actions
Add such support in M*LIB containers.
It can be difficult. For example, for array _init_set
function due to the copy of the object one per one. Each copied sub object
may throw an exception, so we always need to have the container in a correct
state so that it can be cleanup.
* TODO #32 : User specialization fields in the container :ENHANCEMENT:
** State
Some methods of a contained object may need to have some options that
are neither constant nor global, but are specialized in function of the container.
For example, a memory handler may need a parameter which is needed to identify
which pool of memory has to be used.
** Plan:
Add a new operator "CONTEXT" which is a type to be added in the
defined container. Then it can be accessed by the operator through an extension
of the GAIA by adding support for identification of specialization like this:
( CONTEXT( memory_pool_ref ),
NEW( API(NONE, memory_pool_alloc, CONTEXT, ARG1)),
DEL( API(NONE, memory_pool_free, CONTEXT, ARG1)) )
** Open points:
- How to get access to the specialization field of the container? ==> Define the "self" name as the name of the container for all methods? What to do when there are several containers for a method?
- How to initialize the specialization field of the container? provide custom initialization functions? It seems to be the hard point.
* TODO #31 : Uniformize parametrisation options of containers :ENHANCEMENT:
** State
Currently in order to pass some parametrization of the containers,
the parameters are passed to the generation macros through:
- extra parameters of the macros,
- special operators of the contained object oplist.
The extra parameters of some generation macros break the uniformity
of the interface.
It is conceptually wrong to put some special operators of the contained object
that have only meanings for the container object.
In case of adding new options, it break the existing interface or put another
operator in the contained object oplist.
The following parameters are used:
- Fixed size of a queue (buffer / B+Tree),
- Policy of a queue (buffer),
- Type of the container, IT type of the container, Ref type of the iterator
The following operators are used:
- NEW / DEL / REALLOC / FREE
- MEMPOOL / MEMPOOL_LINKAGE
The following new options is planned:
- Interface (See #28)
- Extra parameter for memory allocation function
** Plan
The M\_*\_DEF\_AS macros shall be changed like this:
M_*_DEF_AS(name, cont_oplist, type[, type_oplist])
with cont\_oplist being the container oplist that gives parametrization to the container definition.
The following operators are planned:
- TYPE: to define the type of the container,
- IT\_TYPE: to define the IT type of the container,
- SUBTYPE: to define the type pointed by the iterator,
- FIXED\_SIZE: to define the fixed size of a container,
- POLICY: to define the policy of a container,
- NEW / DEL / REALLOC / FREE: to define the memory interface,
- MEMPOOL / MEMPOOL_LINKAGE: to request a definition of a specialized memory interface,
- INTERFACE: to request a pure header definition, an implementation or both (see #28),
- CONTEXT: to request an extra context parameter to add in the container.
All operators should be optional.
The NEW / DEL / REALLOC / FREE / MEMPOOL / MEMPOOL_LINKAGE won't have any effects anymore if defined in the contained object oplist.
** Pro:
- Uniformize interface
** Con:
- API breakage. It can be mitigated by providing an adaptating layer by prividing the new interface for M\_*\_DEF\_AS only
and *\_DEF\_AS will provide a translation layer.
* TODO #30 : Serialization rework :ENHANCEMENT:
** State
Currently, each container supports 3 serialization methods:
- old format to FILE
- old format to string
- generic serialization
Generic serialiation connect the container format to the serialization object constraints.
It is done through a vtable. As such there is a performance penality and it avoids proper inlining.
** Evolution
Old format should be deprecated and shall use the generic serialization interface.
Serialization object shall provide an OPLIST of serialization.
For example CORE will provide OLD Format oplist and serial-json will provide JSON format oplist.
Each oplist will provide the suffix needed for the serialization, and the interface
(see already existing interface).
Then a container will generate specialized serialization methods for each provided oplist.
Pros:
- Faster
- In the M*LIB philosphy: much like other oplist usage.
Cons:
- compatibility breakage.
Open point:
- how can a user add a new serialization object?
- Can we make a generic serialization object to support migration path?
** Example
serial-json provides
#define M_SERIAL_1() (NAME(_json), READ_INT(...), WRITE_INT(...) )
containers provides
For i in 1 100 ; do
if do exist M_SERIAL_ ## i ()
expand(SERIAL_METHOD with M_SERIAL_1())
SERIAL_METHOD(name, ....)
static inline m_serial_return_code_t \
M_C(M_F(name, _out_serial),M_GET_NAME(serial_op) )(M_GET_TYPE(serial_op) f, const list_t list) \
* DONE #29 : Support of partial initialized object :ANOMALY:
** State
In case of exception during the construction of an object,
if the object is partially constructed, there is no destructor to call.
Therefore if there is some partially constructed object
(with some fields already fully constructed), there will be some links.
This is the same problem with C++
** Proposition
Provide new macros to perform the partial initialization before doing the remaining taf.
Something like:
M_CHAIN_INIT( init_code, clear_code)
or
x is { string_t s; string_t d; int num; }
void struct_init_set(struct_t x, const struct_t y)
{
// First thing is Chained Initialization
M_CHAIN_INIT( string_init_set(x->s, y->s), string_clear(x->s) );
M_CHAIN_INIT( string_init_set(x->d, y->d), string_clear(x->d) );
// End of chain init
x->num = y->num;
}
M_CHAIN_INIT will push the clean code pretty much like M_LET.
Everything else is done in M_LET: when the constructor fully returns,
the index of the clean code to call is reset to its value before calling
the constructor. It means that the partially initialized code will be removed
from this stack before the clean method of the structure itself is pushed.
If an exception is raised after a M_CHAIN_INIT, only the clean methods
of the M_CHAIN_INIT are called.
* TODO #28 : Separate generation of interface to implementation :ENHANCEMENT:
Enable support for generating an interface only for the headers
and an implementation only for the source code.
Try to keep API compatibility
==> Only modify renamed macro with M_ prefix by giving a new mandatory
argument for such generation.
This argument is a generation parametric argument.
oplist is a parameter that define the property of the contained type.
It could be used to pass this information but it will be an abuse of interface.
Another argument is needed.
Used arguments to parameter generation not related to the contained type:
- memory interface (NEW / DEL / ...)
- queue policy
- fixed size for queue
- fixed node size for B+Tree
- generate static inline, public (extern) or private (define) functions.
- name of the type.
- name of the iterator
- name of the iterated element.
- ...
Use another oplist as argument for M_.._DEF_AS (See #31)
M_BUFFER_DEF_AS(name, (TYPE(toto), FIXED_SIZE(10), POLICY(STACK), INTERFACE(header)), int, M_BASIC_OPLIST)
* TODO #27 : Uniformization of m-buffer interface :ENHANCEMENT:
** State
The m-buffer interface requires special argument to work:
- size & policy for BUFFER,
- policy only for the others.
This is not uniform with the others generation macros and not uniform with each other.
Moreover the others are the only one which can be used in a shared memory, so
a fixed size will be good to have too for such interface.
** Plan
- Uniformization of the interface (See #31)
- Provide static size for all QUEUE.
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
@@ -34,12 +281,12 @@ unsigned get_small_hash(int64_t x) {
}
#+END_SRC
* TODO #25 : Support of error code model for error handling. :ENHANCEMENT:
* TODO #25 : Support of error return model for error handling. :ENHANCEMENT:
Find a way to support error return code for the API in case of allocation
failure.
Any service that returns void shall return a "int".
Any service that returns void shall return a "int" (or another type).
In case of allocation failure, it shall return an error.
M_CALL macro shall stop its execution if the service returns an error code
and the error code represents an error (avoid rewritting everything)
@@ -98,19 +345,20 @@ RETCODE/RETCODE
If really needed, the macro can be avoided and code can be hand written.
Open points:
- How to handle warnings on unused labels?
- What about M_LET/ M_EACH?
- What about M_LET / M_EACH? Maybe only supports those.
* TODO #24 : New: Add MIN-MAX-HEAP container :ENHANCEMENT:
* TODO #24 : New MIN-MAX-HEAP container :ENHANCEMENT:
See https://en.wikipedia.org/wiki/Min-max_heap
as DPRIORITY_QUEUE_DEF ?
NOTE: Needs for such container?
* TODO #23 : Strict MOVE semantic to clarify :ENHANCEMENT:
* TODO #23: Strict MOVE semantic to clarify :ENHANCEMENT:
** State
Some type may need to have a force MOVE semantic (for example, they can store
pointer to themselves). Currently the INIT_MOVE & MOVE operators are more
@@ -125,7 +373,7 @@ moving the data before they can be moved using MOVE. Two solutions:
- New operator MOVE_SELF to fix a type after it has been moved.
- If MOVE defined, force another table and then copy by hand the type. This will be slower and consumme more memory.
** MOVE in OPLIST :ENHANCEMENT:
** MOVE in OPLIST
Proposal: do not export INIT_MOVE / MOVE operator in OPLIST if the MOVE operator is
compatible with a pure COPY semantic. An exported MOVE operator will tell
@@ -144,8 +392,22 @@ defined with [1] tricks but without an explicit INIT_MOVE / MOVE
operators as it uses MOVE_DEFAULT which is not (fully compatible).
==> Analyse limitation and possible constraint usages.
Being able to define a correct default for INIT_MOVE will be really good
(compatible with trivial move copy).
In such case, all INIT_MOVE & MOVE operators can be removed from oplist
to only use the default, and theses operators can be disabled or defined
only when really needed in the oplist.
However creating a default INIT_MOVE macro seems problematic.
If the type is typedef T t[1] then passing such an argument to a function,
will transform the argument to T*, and the type of the argument doesn't match
what is expecting resulting in a move of the pointers, not a move of the design data.
* TODO #20: New: Bucket priority queue :ENHANCEMENT:
Defining this type seems possible with C11 _Generic and a TYPE in the oplist,
but without C11 _Generic I don't see any way to define such macro.
Without a way to write such a macro, the ticket seems pretty much a dead end.
* TODO #20 : New: Bucket priority queue :ENHANCEMENT:
Add a new kind of priority queue.
See https://en.wikipedia.org/wiki/Bucket_queue
@@ -161,21 +423,18 @@ except that we can scan 64 entries at a time).
Check if we can use BITSET, or introduce fixed size BITSET or use ad-hock
implementation.
* TODO #19: New: Intrusive Red Black Tree :ENHANCEMENT:
* TODO #19 : New: Intrusive Red Black Tree :ENHANCEMENT:
Add intrusive red black tree.
Look also for AVL tree (NOTE: Is there a performance difference between the two?)
* TODO #18: Missing methods :ENHANCEMENT:
* TODO #18 : Missing methods :ENHANCEMENT:
Some containers don't have all the methods they should.
See the cells in yellow here:
http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Container.html
* TODO #17: New: Ressource handler :ENHANCEMENT:
* TODO #17 : New: Ressource handler :ENHANCEMENT:
A global 'ressouce handler' which shall associated a unique handle to a ressource.
The handle shall be unique for the ressource and shall not be reused.
@@ -202,11 +461,10 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
How to handle multiple resource ?
* variant: works fine. Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
* embedded the type in the ressource handler: more work, API more complex. Memory usage seems better.
* variant: Pro : easy. Con: Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
* embedded the type in the ressource handler: Con: more work, API more complex. Pro: Memory usage seems better.
* TODO #16: New: Lock Free List :ENHANCEMENT:
* TODO #16 : New: Lock Free List :ENHANCEMENT:
Implement a lock free list. Most of the difficulty is the memory reclamation part.
Typically this lock free list shall be compatible with RCU method.
@@ -229,31 +487,7 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
- needs to be logically deleted : needs a previous field
(NULL if not logically deleted) ? TBC
* TODO #15: Prologue / Epilogue for Constructor / Destructor for error handling :ENHANCEMENT:
Constructor (and destructor) need to use user-defined prologue / epilogue.
This is in order to register the constructed object into a proper Exception
Handling Stack so that throwing exceptions may work reliably.
Proposal:
- M_CONSTRUCTOR_PROLOGUE(object, oplist);
- M_CONSTRUCTOR_EPILOGUE(object, oplist);
- M_DESTRUCTOR_PROLOGUE(object, oplist);
- M_DESTRUCTOR_EPILOGUE(object, oplist);
Object creation will need to add all sub-objects into the stack,
then unstack all to push instead the root object (which recursively remove them).
See also http://freetype.sourceforge.net/david/reliable-c.html#cseh
- How to handle like allocation of the object?
- How to avoid calling the destructor multiple times? (It is needed?)
- How the code can be factorized with RETCODE needs?
* TODO #14: Memory allocation enhancement :ENHANCEMENT:
* DONE #14 : Memory allocation enhancement :ENHANCEMENT:
Enhancement of the memory allocation scheme to find way to deal properly with advanced allocators:
@@ -287,8 +521,9 @@ Needs some formal operator in the oplist to support it fully and an example.
It is a kind of object inheritance where the container inherits some extra data from its base.
Duplicate with #32 which is more generic ==> Closed
* TODO #12: New: Atomic shared pointer :ENHANCEMENT:
* TODO #12 : New: Atomic shared pointer :ENHANCEMENT:
Add an extension to the SHARED_PTR API:
@@ -362,9 +597,7 @@ Other alternative solution is to use the bit 0 to mark the pointer as being upda
Other implementation seems to have it hard to be lock-free: cf. https://github.com/llvm-mirror/libcxx/commit/5fec82dc0db3623546038e4a86baa44f749e554f
* TODO #5: New: Concurrent dictionary Container :ENHANCEMENT:
* TODO #5 : New: Concurrent dictionary Container :ENHANCEMENT:
Implement a more efficient dictionary than lock + std dictionary for all operations when dealing with threads.
See https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
@@ -410,7 +643,6 @@ It needs before lock-free list: http://www.cse.yorku.ca/~ruppert/papers/lfll.pdf
Atomic update is done by allocating a new node, update it, and atomicaly store it in the dict, putting the old one as logically deleted and to reclaim later.
(Other threads may still read its data so we cannot free immediatly).
* DONE #22: Enhanced services for SPSC Queue :ENHANCEMENT:
Add services:
@@ -448,6 +680,27 @@ push always succeed and the queue always keep the youngest element.
To simplify it, this interface could only support one kind of import/export
per compilation unit.
* DONE #15: Prologue / Epilogue for Constructor / Destructor for error handling :ENHANCEMENT:
Constructor (and destructor) need to use user-defined prologue / epilogue.
This is in order to register the constructed object into a proper Exception
Handling Stack so that throwing exceptions may work reliably.
Proposal:
- M_CONSTRUCTOR_PROLOGUE(object, oplist);
- M_CONSTRUCTOR_EPILOGUE(object, oplist);
- M_DESTRUCTOR_PROLOGUE(object, oplist);
- M_DESTRUCTOR_EPILOGUE(object, oplist);
Object creation will need to add all sub-objects into the stack,
then unstack all to push instead the root object (which recursively remove them).
See also http://freetype.sourceforge.net/david/reliable-c.html#cseh
- How to handle like allocation of the object?
- How to avoid calling the destructor multiple times? (It is needed?)
- How the code can be factorized with RETCODE needs?
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Executable
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
mkdir -p /tmp/prepro-$USER
name=/tmp/prepro-$USER/$$.c
param1=""
param2=""
while test $# -gt 0 ; do
case $1 in
-c) param2="$param2 -c" ;;
*\.c) param1="$param1 $1" ;;
*) param1="$param1 $1" ; param2="$param2 $1" ;;
esac
shift
done
cc $param1 -E |grep -v '^#' > ${name} \
&& perl -pi -e 's/\;/\;\n/g' ${name} \
&& cc $param2 ${name}
+19 -6
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2022, Patrick Pelissier
# Copyright (c) 2017-2023, Patrick Pelissier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -21,7 +21,7 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
CC=cc -std=c99
CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -g
CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -g
GMP_DIR=
MPFR_DIR=$(GMP_DIR)
CPPFLAGS=-I..
@@ -31,20 +31,33 @@ RM=rm -f
.SUFFIXES: .c .exe
.c.exe:
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -o $@ `cat config`
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ `cat config` $(LDFLAGS)
.c.o:
@$(MAKE) config
$(CC) $(CFLAGS) $(CPPFLAGS) -O2 `cat config.link` -DNDEBUG -g $< -c -o $@
all: config
$(MAKE) `ls ex-*.c|sed -e 's/\.c/\.exe/g'`
if test "$(CC)" = "cc -std=c99" ; then $(MAKE) `ls ex11-*.c|sed -e 's/\.c/\.exe/g'` CC="cc -std=c11" ; fi
$(MAKE) exm.exe exn.exe
exm.exe: exm-lib.o exm-main.o
$(CC) `cat config.link` -g $^ -o $@
exn.exe: exn-lib.o exn-main.o
$(CC) `cat config.link` -g $^ -o $@
config:
printf "#include <gmp.h>\nint main(void){mpz_t z; mpz_init(z); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I$(GMP_DIR)/include -L$(GMP_DIR)/lib config-test.c -lgmp -o config-test.exe 2> /dev/null && echo "-DHAVE_GMP=1 -DHAVE_MPFR=0 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp" > config || echo "-DHAVE_GMP=0 -DHAVE_MPFR=0 " > config
$(CC) $(CFLAGS) $(CPPFLAGS) -I$(GMP_DIR)/include -L$(GMP_DIR)/lib config-test.c -lgmp -o config-test.exe $(LDFLAGS) 2> /dev/null && echo "-DHAVE_GMP=1 -DHAVE_MPFR=0 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp" > config || echo "-DHAVE_GMP=0 -DHAVE_MPFR=0 " > config
printf "#include <mpfr.h>\nint main(void){mpfr_t z; mpfr_init(z); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I$(GMP_DIR)/include -I$(MPFR_DIR)/include -L$(GMP_DIR)/lib -L$(MPFR_DIR)/lib config-test.c -lmpfr -lgmp -o config-test.exe 2> /dev/null && echo "-DHAVE_GMP=1 -DHAVE_MPFR=1 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -I$(MPFR_DIR)/include -L$(MPFR_DIR)/lib -lmpfr -lgmp" > config || echo "MPFR not available"
$(CC) $(CFLAGS) $(CPPFLAGS) -I$(GMP_DIR)/include -I$(MPFR_DIR)/include -L$(GMP_DIR)/lib -L$(MPFR_DIR)/lib config-test.c -lmpfr -lgmp -o config-test.exe $(LDFLAGS) 2> /dev/null && echo "-DHAVE_GMP=1 -DHAVE_MPFR=1 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -I$(MPFR_DIR)/include -L$(MPFR_DIR)/lib -lmpfr -lgmp" > config || echo "MPFR not available"
printf "#include <stdio.h>\nint main(void){printf(\"Ok\"); return 0;}\n" > config-test.c
$(CC) $(CFLAGS) $(CPPFLAGS) config-test.c -ffunction-sections -fdata-sections -Wl,--gc-sections 2> /dev/null && echo "-ffunction-sections -fdata-sections -Wl,--gc-sections" > config.link || echo "" > config.link
$(RM) config-test.c config-test.exe
clean:
$(RM) *.exe *.s *~ *.o ./a.dat config
$(RM) *.exe *.s *~ *.o ./a.dat config config.link
+8 -8
View File
@@ -4,16 +4,16 @@
/* Define a dynamic array of int */
ARRAY_DEF(vector_int, int)
/* Register the oplist of this dynamic array globally */
/* Register the oplist of this dynamic array globally (using implicitely M_BASIC_OPLIST) */
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
/* Define some standards algorithms on this dynamic array */
/* Define some standards algorithms on this dynamic array. We can reuse the prefix of the dynamic array */
ALGO_DEF(vector_int, vector_int_t)
/* Define a dynamic array of bool */
ARRAY_DEF(vector_bool, bool, M_BOOL_OPLIST)
ARRAY_DEF(vector_bool, bool)
/* Register the oplist of this dynamic array globally */
#define M_OPL_vector_bool_t() ARRAY_OPLIST(vector_bool, M_BOOL_OPLIST)
/* Define some standards algorithms on this dynamic array */
/* Define some standards algorithms on this dynamic array. We can reuse the prefix of the dynamic array */
ALGO_DEF(vector_bool, vector_bool_t)
/* ID function */
@@ -41,10 +41,10 @@ int main(void)
#else
for M_EACH(it, myvector, vector_int_t) {
printf(" %d", *it);
}
}
#endif
printf("\n");
}
} /* End of M_LET: myvector & tmp are automaticly cleared */
// Same thing but with booleans
M_LET(myvector, tmp, vector_bool_t) {
@@ -56,13 +56,13 @@ int main(void)
printf("myvector contains:");
for M_EACH(it, myvector, vector_bool_t) {
printf(" %d", (int)*it);
}
}
printf("\n");
printf ("ALL: %d ANY: %d NONE: %d\n",
vector_bool_all_of_p(myvector, id),
vector_bool_any_of_p(myvector, id),
vector_bool_none_of_p(myvector, id) );
}
} /* End of M_LET: myvector & tmp are automaticly cleared */
return 0;
}
+5 -6
View File
@@ -14,7 +14,7 @@ ARRAY_DEF(vector_int, int)
/* Register the oplist of this array of integer globally */
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
/* Micro macro to detect if a string starts with the given pattern */
/* Micro macro to detect if the key string starts with the given pattern */
#define start_with(pattern, item) \
string_start_with_str_p((item).key, (pattern))
@@ -26,11 +26,10 @@ int main(void)
int s = 0;
// Init keys as a vector_int_t, initialize it (and prepare its destruction)
M_LET(keys, vector_int_t)
// Initialize m as a dictionnary and initialize it with the given database
// Initialize tmp as a dictionanry
M_LET( (m, (STRING_CTE("foo"), 1), (STRING_CTE("bar"), 42), (STRING_CTE("bluez"), 7), (STRING_CTE("stop"), 789) ), tmp, dict_t) {
/* Extract all elements of 'm' that starts with 'b' */
// Initialize m as a dictionnary and initialize it with the given database
// Initialize tmp as a dictionnary
M_LET( (m, (STRING_CTE("foo"), 1), (STRING_CTE("bar"), 42), (STRING_CTE("bluez"), 7), (STRING_CTE("stop"), 789) ), tmp, dict_t) {
/* Extract all elements of 'm' that starts with 'b' and fills tmp with it */
ALGO_EXTRACT(tmp, dict_t, m, dict_t, start_with, "b");
/* Extract the values of theses elements */
ALGO_TRANSFORM(keys, vector_int_t, tmp, dict_t, get_value);
+11 -8
View File
@@ -7,28 +7,31 @@ ARRAY_DEF(vector_int, int)
/* Register the oplist of this array of int globally */
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
/* Set s (string_t) to the integer i */
#define str_ui(s, i) string_printf(s, "%d", i)
/* Concat two strings by insertion a '-' within them */
#define str_cat(s1, s2) (string_cat_str(s1, "-"), string_cat(s1, s2))
int main(void)
{
// Let s be a string_t and initilize it.
// Let 's' be a string_t and initialize it.
M_LET(s, string_t)
// Let v be a vector of integer and initialize it.
M_LET( (v, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), vector_int_t) {
// Let 'v' be a vector of integer and initialize it.
M_LET( (v, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), vector_int_t) {
int sum = 0, product = 0;
// Compute the sum of theses integers.
// 'add' is a registered keyword, recognized by M*LIB to perform a sum.
ALGO_REDUCE(sum, v, vector_int_t, add);
// Compute the product of theses integers.
// 'mul' is a registered keyword, recognized by M*LIB to perform a product.
ALGO_REDUCE(product, v, vector_int_t, mul);
/* Example of reduction with a transformation of the type from int to string_t */
ALGO_REDUCE( (s, string_t), v, vector_int_t, str_cat, str_ui);
/* Example of reduction with a transformation of the type from int to string_t
By default, the result ("s") should be of the same type as the base type
of the container. This is not the case here, we want to transform the
type. As such, we add the precision of the type 'string_t' to 's'.
The transformation from string to integer is done by string_set_si */
ALGO_REDUCE( (s, string_t), v, vector_int_t, str_cat, string_set_si);
printf ("sum: %d\n"
"product: %d\n"
+19 -9
View File
@@ -25,6 +25,7 @@
for basic types.
For integer and floats, there is no need to specify
an oplist. M_BASIC_OPLIST will be used.
For bool, M_BOOL_OPLIST will be used.
*/
ARRAY_DEF(r_bool, bool)
@@ -214,9 +215,9 @@ static char *my_strdup(const char *p)
return d;
}
/* Same with type const char *, representing a contant string.
/* Same with type const char *, representing a contant C string.
We *** need *** an explicit oplist to tell M*LIB
to consider the type as a constant string.
to consider the type as a constant C string.
The oplist to use is M_CSTR_OPLIST.
We could have used M_PTR_OPLIST too,
but in this case, we won't get proper string order or
@@ -245,13 +246,13 @@ test_cstring(int n)
for(int i = 0; i < n; i++) {
// Get back the string
const char *p = *r_cstring_get(array, i);
// Convert it to integer
// Convert it to an integer
int j = atoi(p);
s += j;
}
// We also need a special unalloc pass
// to free all constant strings we have allocated.
// to free all constant strings we have allocated using my_strdup
for(int i = 0; i < n; i++) {
const char *p = *r_cstring_get(array, i);
free((void*)(uintptr_t)p);
@@ -264,7 +265,7 @@ test_cstring(int n)
/* Same with type string_t of M*LIB, representing a variable string.
/* Same with type string_t of M*LIB, representing a dynamic string.
We don't need explicit oplist to tell M*LIB!
The oplist to use is STRING_OPLIST,
but it has been registered globaly, so we don't need to explicit
@@ -282,7 +283,7 @@ test_string(int n)
for(int i = 0; i < n; i++) {
// create a string from an integer
string_printf(str, "%d", i);
string_set_si(str, i);
// No need to make a copy in the container!
r_string_push_back(array, str);
}
@@ -294,7 +295,7 @@ test_string(int n)
s += j;
}
// No need to explicit dealloc !
// No need to explicit free each term of the array
string_clear(str);
r_string_clear(array);
@@ -304,7 +305,7 @@ test_string(int n)
/* Same with type string_t of M*LIB, representing a variable string
and its explicit oplist. And said above, it generates exactly the
and its explicit oplist. As said above, it generates exactly the
same code. */
ARRAY_DEF(r_string2, string_t, STRING_OPLIST)
@@ -340,6 +341,9 @@ test_vintptr(int n)
int s = 0;
for(int i = 0; i < n; i++) {
// Get the pointer to the integer.
// As the basic type of the container is a pointer,
// and r_vintptr_get return a pointer to the basic type
// we need to dereference its return value to get the pointer value.
volatile unsigned *p = *r_vintptr_get(array, i);
s += *p;
}
@@ -374,6 +378,7 @@ test_rockme(int n)
for(int i = 0; i < n; i++) {
struct rock_me_out rock;
rock.n = i*i - i;
rock.other = 0.0f;
// Push the struct in the array
r_rockme_push_back(array, rock);
}
@@ -414,6 +419,7 @@ test_rockme2(int n)
for(int i = 0; i < n; i++) {
rock_me_in rock;
rock->n = i*i - i;
rock->other = 0.0f;
r_rockme2_push_back(array, rock);
}
@@ -458,6 +464,7 @@ test_rockme2b(int n)
for(int i = 0; i < n; i++) {
rock_me_in rock;
rock->n = i*i - i;
rock->other = 0.0f;
r_rockme2b_push_back(array, rock);
}
@@ -478,11 +485,14 @@ test_rockme2b(int n)
/* Same with a structure type defined as a tuple.
We first define a tuple. No need to specify the oplist as it uses basic C types
so M_BASIC_OPLIST is used.
so M_BASIC_OPLIST is used by default.
Then we define the array, giving it the oplist definition of the tuple:
the macro TUPLE_OPLIST is used to build it based on the tuple name,
and the oplists of the tuple.
The main advantage of a tuple over a classic structure is that you get
free classic methods of your structure.
*/
TUPLE_DEF2(rock_you, (n, int), (other, float))
+4 -1
View File
@@ -25,7 +25,9 @@ int main(void)
map_employee_id_set_at(Employees, 11632, STRING_CTE("John Do"));
map_employee_id_set_at(Employees, 852, STRING_CTE("Peter"));
map_employee_id_set_at(Employees, 530, STRING_CTE("Amanda"));
map_employee_id_set_at(Employees, 4258, STRING_CTE("Elisabeth"));
// You can also emplace the structure
map_employee_id_emplace_val(Employees, 4258, "Elisabeth");
printf("Number of employees: %zu\n", map_employee_id_size(Employees));
@@ -60,6 +62,7 @@ int main(void)
printf("\nElisabeth has id = %d\n", *map_employee_name_get(Employees_r, STRING_CTE("Elisabeth")));
} /* Employees_r is cleared beyond this point */
// But employee is not
map_employee_id_clear(Employees);
return 0;
}
+1 -1
View File
@@ -2,7 +2,7 @@
#include "m-buffer.h"
#include "m-shared.h"
#include "m-mutex.h"
#include "m-thread.h"
/* Global variable to stop scheduling */
atomic_bool continue_threading_g;
+56
View File
@@ -0,0 +1,56 @@
#include <stdio.h>
#include "m-dict.h"
/*
* Provide OOR methods for OA Hashmap.
* See documentation for definition of OOR
* OOR values are represented as:
* empty (0) is INT_MIN
* deleted (1) is INT_MIN + 1
* So valid range for integers are [INT_MIN+2, INT_MAX]
*/
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;
}
/*
* Provide a custom hash function (optional for OA)
*/
static inline size_t hash(int n)
{
return (size_t) n; // Identity hash!
}
/* Define an OA dictionnay which performs a hashmap between an int to an int */
DICT_OA_DEF2(dict_oa,
int, M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_4(oor_set)), HASH(hash)),
int, M_BASIC_OPLIST)
int main(void)
{
dict_oa_t d;
dict_oa_init(d);
for(int i = 0; i < 10; i++) {
dict_oa_set_at(d, i*i, 4*i);
}
dict_oa_set_at(d, 3, -17);
printf("DICT[3] = %d\n", *dict_oa_get(d, 3));
dict_oa_it_t it;
printf("DICT=");
for(dict_oa_it(it, d); !dict_oa_end_p(it); dict_oa_next(it)) {
printf("%d:%d, ", dict_oa_cref(it)->key, dict_oa_cref(it)->value);
}
printf("\n");
dict_oa_clear(d);
return 0;
}
+64
View File
@@ -0,0 +1,64 @@
#include <stdio.h>
#include "m-dict.h"
/*
* Provide OOR methods for OA Hashmap.
* See documentation for definition of OOR
* OOR values are represented as:
* empty (0) is "EMPTY"
* deleted (1) is "DELETED"
*/
static const char empty_str[] = "EMPTY";
static const char deleted_str[] = "DELETED";
static const char *oor_table[] = { empty_str, deleted_str };
static inline bool oor_equal_p(const char *k, int n)
{
return k == oor_table[n];
}
static inline const char *oor_set(int n)
{
return oor_table[n];
}
/*
* Provide a custom hash function (optional for OA)
*/
static inline size_t hash(const char *k)
{
size_t r = 17;
while (*k) {
r = *k * 33 + r;
k++;
}
return r;
}
/* Define an OA dictionnay which performs a hashmap between a CSTR to an int */
DICT_OA_DEF2(dict_oa,
const char *, M_OPEXTEND(M_CSTR_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_4(oor_set)), HASH(hash)),
int, M_BASIC_OPLIST)
int main(void)
{
dict_oa_t d;
dict_oa_init(d);
dict_oa_set_at(d, "THIS", -17);
dict_oa_set_at(d, "IS", -170);
dict_oa_set_at(d, "AN", 7);
dict_oa_set_at(d, "EXAMPLE", 77);
printf("DICT[\"AN\"] = %d\n", *dict_oa_get(d, "AN"));
dict_oa_it_t it;
printf("DICT=");
for(dict_oa_it(it, d); !dict_oa_end_p(it); dict_oa_next(it)) {
printf("%s:%d, ", dict_oa_cref(it)->key, dict_oa_cref(it)->value);
}
printf("\n");
dict_oa_clear(d);
return 0;
}
+56 -21
View File
@@ -8,43 +8,54 @@
#include "m-string.h"
#include "m-algo.h"
// Let's create a list of string.
// Let's create a list of string by using the M*LIB dynamic string 'string_t'
// The prefix used for generating functions will be 'list_string'
LIST_DEF(list_string, string_t)
// Let's register the oplist globally so that other m*lib macros
// Let's register the oplist globally so that other M*LIB macros
// can get the oplist without us giving it explictly to them.
// An oplist is the association of operators to the provided methods
// so that generic code can use correctly an object of such type.
// LIST_OPLIST is a macro used to generate the oplist associated to the
// list itself. It needs the prefix used for generating the list (aka list_string here)
// and the oplist of the item in the list (aka the string oplist)
#define M_OPL_list_string_t() LIST_OPLIST(list_string, STRING_OPLIST)
// Let's define some basic algorithms over this list of string.
ALGO_DEF(astring, M_OPL_list_string_t())
// We reuse the prefix of the list to simplify usage, but algorithms can use separate namespace.
ALGO_DEF(list_string, M_OPL_list_string_t())
int main(void)
{
// Let's create a variable named 'grow' which is a list of string_t
// Let's create a variable named 'list' which is a list of string_t
// This variable is only defined within the associated '{' '}' block.
// The constructor/destructor of the variable will be called within this block
// You can also define the variable (list_string_t) and use explicitly
// its constructor (list_string_init) / destructor (list_string_clear).
M_LET (grow, list_string_t)
M_LET (list, list_string_t)
M_LET (tmpstr, string_t) { // And let's create also a string_t
// add string elements to the list in different ways
list_string_push_back (grow, STRING_CTE("AB"));
// First is pushing a const string.
// The macro STRING_CTE creates a const string_t based on the given const char * string.
list_string_push_back (list, STRING_CTE("AB"));
// Second is creating a separate string_t and then pushing it in the list
string_printf(tmpstr, "%d", 12);
list_string_push_back (grow, tmpstr);
list_string_emplace_back (grow, "CD");
list_string_push_back (list, tmpstr);
// Third is to emplace directly in the list by constructing a new string_t based on the given const char *
list_string_emplace_back (list, "CD");
// Serialize the list of string into a big string.
list_string_get_str (tmpstr, grow, false);
list_string_get_str (tmpstr, list, false);
// print out
printf("Number of elements = %zu\n", list_string_size(grow));
// print it
printf("Number of elements = %zu\n", list_string_size(list));
printf("Final string = %s\n", string_get_cstr(tmpstr));
// Let's iterate over each element of the list
// using the macro for iteration over a container.
printf ("Using macro EACH :\n");
for M_EACH (item, grow, list_string_t) {
printf ("Using macro EACH:\n");
for M_EACH (item, list, list_string_t) {
printf ("Item = ");
string_fputs (stdout, *item);
printf ("\n");
@@ -52,35 +63,59 @@ int main(void)
// You can also use classic iterator:
list_string_it_t it;
printf ("Using iterators :\n");
for(list_string_it(it, grow) ; !list_string_end_p(it) ; list_string_next(it)) {
printf ("Using iterators:\n");
for(list_string_it(it, list) ; !list_string_end_p(it) ; list_string_next(it)) {
const string_t *item = list_string_cref (it);
printf ("Item = ");
string_fputs (stdout, *item);
printf ("\n");
}
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
// Another way: we map each element to the M_PRINT function.
printf ("Using macro ALGO_FOR_EACH:");
ALGO_FOR_EACH(list, list_string_t, M_PRINT, "\nItem = ");
printf ("\n");
#endif
// Get the min and the max of the list
string_t *p = astring_min(grow);
string_t *p = list_string_min(list);
printf ("Min string is %s\n", string_get_cstr(*p));
p = astring_max(grow);
p = list_string_max(list);
printf ("Max string is %s\n", string_get_cstr(*p));
// We can even sort the list.
astring_sort(grow);
for M_EACH (item, grow, list_string_t) {
list_string_sort(list);
for M_EACH (item, list, list_string_t) {
printf ("Sort Item = %s\n", string_get_cstr(*item) );
}
// Split a string into a list.
string_set_str(tmpstr, "HELLO;JOHN;HOW;ARE;YOU");
astring_split(grow, tmpstr, ';');
list_string_split(list, tmpstr, ';');
// Iterate over the splitted list
for M_EACH (item, grow, list_string_t) {
for M_EACH (item, list, list_string_t) {
printf ("Split Item = %s\n", string_get_cstr(*item) );
}
} // Everything is cleared after this point.
// Let's create another list of string and init it.
list_string_t list;
list_string_init(list);
// Let's put some elements in it:
list_string_emplace_back (list, "A");
list_string_emplace_back (list, "B");
list_string_emplace_back (list, "C");
list_string_emplace_back (list, "D");
// Reverse it
list_string_reverse(list);
// Let's display the string by serializing the elements of the list:
printf ("List of string = ");
list_string_out_str(stdout, list);
printf ("\n");
// Don't forget to clear the list before leaving
list_string_clear(list);
return 0;
}
+81 -50
View File
@@ -10,51 +10,70 @@
#include "m-string.h"
#include "m-atomic.h"
/* Sort the atomic unsigned int with the biggest value first. */
/* Define the comparison function for atomic_uint type
to sort the atomic unsigned int with the biggest value first. */
static inline int atomic_uint_cmp(const atomic_uint *a, const atomic_uint *b)
{
unsigned ai = atomic_load(a), bi = atomic_load(b);
return ai > bi ? -1 : ai < bi;
}
/* Disable HASH for atomic as the default hash method won't work */
#define M_OPL_atomic_uint() M_OPEXTEND(M_BASIC_OPLIST, HASH(0), CMP(API_6(atomic_uint_cmp)))
/* Define a directory as the number of lines of the files in this directory
and the name of the directory
(Only sort with the number value by disable the sort compare for the string).
/* Define the oplist for atomic_uint and register it globally.
This oplist extends the basic oplist by:
* disabling HASH for atomic as the default hash method won't work.
* providing the methods for CMP, INIT_SET and SET operators (with their API adaptator) */
#define M_OPL_atomic_uint() M_OPEXTEND(M_BASIC_OPLIST, HASH(0), CMP(API_6(atomic_uint_cmp)), INIT_SET(API_2(atomic_store)), SET(API_2(atomic_store)))
/* Define a directory as a tree where each node contains:
* the number of lines of the files in this directory,
* and the name of the directory/
Since we want to sort with the number of lines only, we extend the oplist of a string by disabling the compare method.
So first, let's define the node as a tuple of such elements:
*/
TUPLE_DEF2( dir, (nlines, atomic_uint), (name, string_t, M_OPEXTEND(STRING_OPLIST, CMP(0))))
#define M_OPL_dir_t() TUPLE_OPLIST(dir, M_BASIC_OPLIST, M_STRING_OPLIST)
M_TUPLE_DEF2( dir, (nlines, atomic_uint), (name, m_string_t, M_OPEXTEND(M_STRING_OPLIST, CMP(0))))
/* Register globally the oplist of this tuple */
#define M_OPL_dir_t() M_TUPLE_OPLIST(dir, M_OPL_atomic_uint(), M_STRING_OPLIST)
/* A hierarchical tree of directory */
/* Define a hierarchical tree of directory.
Note that it will use the globally registered oplist associated to dir_t to use it */
M_TREE_DEF(tree, dir_t)
/* Maximum number of characters read by file */
#define MAX_READ_BUFFER 8192
/* Maximum number of directory to scan */
#define MAX_DIRECTORY 100000
/* Define the pool of worker threads */
m_worker_t workers;
/* Global synchronous block for all threads */
m_worker_sync_t workers_block;
/* Add a specialization of the spawn function for the scan_file function.
This functions takes as input an iterator of a tree (treated as a POD data),
and a file name */
M_WORKER_SPAWN_DEF2(scan_file, (parent, tree_it_t, M_POD_OPLIST), (filename, m_string_t) )
/* Add either the root node or a child to parent in function of the existence of parent */
static tree_it_t
add_directory(tree_it_t parent, const string_t dirname)
add_directory(tree_it_t parent, const m_string_t dirname)
{
// TODO: return dir_emplace_child(parent, 0, dirname); to simplify this function.
// which is too complex for what she does.
dir_t data;
tree_it_t it;
dir_init_emplace(data, 0, dirname);
if (tree_end_p(parent)) {
it = tree_set_root( tree_tree(parent), data);
return tree_emplace_root( tree_tree(parent), 0, dirname);
} else {
it = tree_insert_child(parent, data);
return tree_emplace_child(parent, 0, dirname);
}
dir_clear(data);
return it;
}
/* count the number of end of line character in the buffer */
static unsigned
count_eol(const char buffer[], size_t num)
count_eol(size_t num, const char buffer[num])
{
unsigned count = 0;
for(size_t i = 0; i < num; i++) {
@@ -63,23 +82,23 @@ count_eol(const char buffer[], size_t num)
return count;
}
/* Test if a filename suffix ends with a C++ or C suffix */
static bool
is_a_c_file(const string_t filename)
is_a_c_file(const m_string_t filename)
{
return (string_end_with_str_p(filename, ".c")
|| string_end_with_str_p(filename, ".h")
|| string_end_with_str_p(filename, ".cpp")
|| string_end_with_str_p(filename, ".hpp"));
return (m_string_end_with_str_p(filename, ".c")
|| m_string_end_with_str_p(filename, ".h")
|| m_string_end_with_str_p(filename, ".cpp")
|| m_string_end_with_str_p(filename, ".hpp"));
}
/* Scan the file and count its number of lines */
static void
scan_file(tree_it_t parent, const string_t filename)
scan_file(tree_it_t parent, m_string_t filename)
{
if (!is_a_c_file(filename)) return;
FILE *f = fopen(string_get_cstr(filename), "rt");
FILE *f = fopen(m_string_get_cstr(filename), "rt");
if (f == NULL) {
fprintf(stderr, "ERROR: Cannot open %s as a text file.\n", string_get_cstr(filename) );
fprintf(stderr, "ERROR: Cannot open %s as a text file.\n", m_string_get_cstr(filename) );
exit(1);
}
@@ -89,25 +108,27 @@ scan_file(tree_it_t parent, const string_t filename)
size_t num;
do {
num = fread (buffer, 1, MAX_READ_BUFFER, f);
count += count_eol(buffer, num);
count += count_eol(num, buffer);
} while (num == MAX_READ_BUFFER);
fclose(f);
/* Adding the number of lines to the parent directory */
dir_t *d = tree_ref(parent);
/* atomic add because the scan of files is done in parallel */
atomic_fetch_add( &(*d)->nlines, count );
}
/* Scan the directories recursively for all files */
static void
scan_directories(tree_it_t parent, const string_t dirname)
scan_directories(tree_it_t parent, const m_string_t dirname)
{
tree_it_t it = add_directory(parent, dirname);
string_t filename;
string_init(filename);
DIR *dir = opendir(string_get_cstr(dirname));
m_string_t filename;
m_string_init(filename);
DIR *dir = opendir(m_string_get_cstr(dirname));
if (dir == NULL) {
fprintf(stderr, "ERROR: Cannot open %s as a directory.\n", string_get_cstr(dirname) );
fprintf(stderr, "ERROR: Cannot open %s as a directory.\n", m_string_get_cstr(dirname) );
exit(1);
}
@@ -116,20 +137,21 @@ scan_directories(tree_it_t parent, const string_t dirname)
while (entry) {
// Ignore hidden entries (including '.' and '..')
if (entry->d_name[0] != '.') {
// Construct the path to the filename
string_sets(filename, dirname, "/", entry->d_name);
// Construct the path to the filename by concatenating dirname, "/" and d_name
m_string_sets(filename, dirname, "/", entry->d_name);
// Get the property of this filename
struct stat buf;
int err = stat(string_get_cstr(filename), &buf);
int err = stat(m_string_get_cstr(filename), &buf);
if (err != 0) {
fprintf(stderr, "ERROR: Cannot stat %s\n", string_get_cstr(filename));
fprintf(stderr, "ERROR: Cannot stat %s\n", m_string_get_cstr(filename));
exit(2);
}
// Further scanning in function of the kind of entry
if (S_ISDIR(buf.st_mode)) {
scan_directories(it, filename);
} else {
scan_file(it, filename);
} else if (is_a_c_file(filename)) {
// Spawn a scan of file to available workers or run it ourselves
m_worker_spawn_scan_file(workers_block, scan_file, it, filename);
}
}
// Next entry in the directory
@@ -137,13 +159,14 @@ scan_directories(tree_it_t parent, const string_t dirname)
}
closedir(dir);
string_clear(filename);
m_string_clear(filename);
}
/* Consolidate all lines number to the parent directory */
static void
consolidate_directories(tree_t directories)
{
// Scan all entries, first the childreen, then the parent
// Scan all entries, first the children, then the parent
for(tree_it_t it = tree_it_post(directories) ; !tree_end_p(it) ; tree_next_post(&it) ) {
dir_t *parent = tree_up_ref(it);
dir_t *myself = tree_ref(it);
@@ -151,28 +174,29 @@ consolidate_directories(tree_t directories)
// Add the child number of lines to the parent
atomic_fetch_add_explicit(&(*parent)->nlines, (*myself)->nlines, memory_order_relaxed);
}
// Sort the data of the childreen by the number of lines
// Sort the data of the children by the number of lines
tree_sort_child(it);
}
}
/* Print the consolidated results on stdout */
static void
print_result(tree_t directories)
{
// Scan all entries, first the parent, then the childreen
// Scan all entries, first the parent, then the children
for(tree_it_t it = tree_it(directories) ; !tree_end_p(it) ; tree_next(&it) ) {
int i, depth = tree_depth(it);
for(i = 0; i < depth; i++) printf("+");
for( ; i < 8; i++) printf(" ");
const dir_t *d = tree_cref(it);
printf("%6u %s\n", (unsigned) (*d)->nlines, string_get_cstr( (*d)->name ) );
printf("%6u %s\n", (unsigned) (*d)->nlines, m_string_get_cstr( (*d)->name ) );
}
}
int main(int argc, const char *argv[])
{
tree_t directories;
string_t str;
m_string_t str;
printf("Count the number of C/C++ lines of code\n");
if (argc < 2) {
@@ -180,22 +204,29 @@ int main(int argc, const char *argv[])
exit(1);
}
/* Initialize the worker threads */
m_worker_init(workers);
/* Init the tree, reserve for at least the number of max directory
and prevent any further increase of allocation:
this is to ensure that other threads can deref a dir safely */
tree_init(directories);
tree_reserve(directories, MAX_DIRECTORY);
tree_lock(directories, true);
string_init_set_str(str, argv[1]);
m_string_init_set_cstr(str, argv[1]);
tree_it_t it = tree_it_end(directories);
/* Main processing */
m_worker_start(workers_block, workers);
scan_directories(it, str);
worker_sync(workers_block);
consolidate_directories(directories);
print_result(directories);
/* Clear everything & quit */
string_clear(str);
m_string_clear(str);
tree_clear(directories);
m_worker_clear(workers);
exit(0);
}
+4 -1
View File
@@ -14,13 +14,15 @@ int main(void)
string_t buffer;
m_serial_return_code_t status;
/* Let's define a string 'buffer' that will receive the JSON representation of our message*/
string_init(buffer);
/* Encode our message */
{
SimpleMessage_t message;
SimpleMessage_init(message);
/* Create a stream that will write to our buffer. */
/* Create a stream that will write to our string buffer. */
m_serial_str_json_write_t stream;
m_serial_str_json_write_init(stream, buffer);
@@ -43,6 +45,7 @@ int main(void)
}
/* Now we could transmit the message over network, store it in a file
* It is represented in a string 'buffer'
* but we are doing it right now.
*/
+21 -19
View File
@@ -98,35 +98,36 @@ mpfr_fast_hash (const mpfr_t f)
TYPE(mpfr_t) )
/* Define a variant over three kind of numbers named my_number_t
/* Define a variant (aka a typed union) over three kind of numbers named my_number_t
Notice than mpz_t/mpfr_t has special init & clear functions.
This is handle by the library.
This is handled by the library.
*/
VARIANT_DEF2(my_number,
(n, long long),
(z, mpz_t),
(fr, mpfr_t) )
/* Define the oplist associated to this variant and register it */
/* Register the oplist associated to this variant named my_number_t */
#define M_OPL_my_number_t() VARIANT_OPLIST(my_number, M_BASIC_OPLIST, M_OPL_mpz_t(), M_OPL_mpfr_t() )
/* Define a dictionary of string_t --> my_number_t named my_dict_t */
DICT_DEF2(my_dict, string_t, my_number_t)
/* Define the oplist associated to this dictionary and register it */
/* Register the oplist associated to this dictionary named my_dict_t */
#define M_OPL_my_dict_t() DICT_OPLIST(my_dict, STRING_OPLIST, M_OPL_my_number_t() )
/* Define an array of such dictionary named my_array_t */
ARRAY_DEF(my_array, my_dict_t)
/* Define the oplist associated to this array of dictionary of variant and register it */
/* Register the oplist associated to this array (of dictionary of variant) named my_array_t*/
#define M_OPL_my_array_t() ARRAY_OPLIST(my_array, M_OPL_my_dict_t())
// Show how to define an array of mpz_t & mpfr_t and some algorithms on then
// (Not needed for the sequel of this example)
// Show how to define an array of mpz_t and some algorithms on it
ARRAY_DEF(array_mpz, mpz_t)
#define M_OPL_array_mpz_t() ARRAY_OPLIST(array_mpz, M_OPL_mpz_t())
ALGO_DEF(array_mpz, array_mpz_t)
// Show how to define an array of mpfr_t and some algorithms on it
ARRAY_DEF(array_mpfr, mpfr_t)
#define M_OPL_array_mpfr_t() ARRAY_OPLIST(array_mpfr, M_OPL_mpfr_t())
ALGO_DEF(array_mpfr, array_mpfr_t)
@@ -136,14 +137,13 @@ compute_roots(array_mpfr_t roots, const array_mpz_t pol)
{
assert( array_mpz_size(pol) == 3);
// Initialize mpfr_t variables from the mpz_t values in the array 'pol'
// & clear then once the brackets end.
M_LET( (a, (*array_mpz_get(pol, 0))),
(b, (*array_mpz_get(pol, 1))),
(c, (*array_mpz_get(pol, 2))),
delta, tmp, mpfr_t) {
// Reset the output array
array_mpfr_reset(roots);
// Compute the results
// Compute the first root
mpfr_mul(delta, b, b, MPFR_RNDN);
mpfr_mul_ui(tmp, a, 4, MPFR_RNDN);
mpfr_mul(tmp, tmp, c, MPFR_RNDN);
@@ -155,13 +155,14 @@ compute_roots(array_mpfr_t roots, const array_mpz_t pol)
mpfr_div_2ui(tmp, tmp, 1, MPFR_RNDN);
// Push first root
array_mpfr_push_back(roots, tmp);
// Compute second root
mpfr_neg(tmp, b, MPFR_RNDN);
mpfr_sub(tmp, tmp, delta, MPFR_RNDN);
mpfr_div(tmp, tmp, a, MPFR_RNDN);
mpfr_div_2ui(tmp, tmp, 1, MPFR_RNDN);
// Push second root
array_mpfr_push_back(roots, tmp);
}
} // Clear the mpfr_t variables after this point.
}
int main(void)
@@ -202,18 +203,19 @@ int main(void)
printf("Key is '%s'. ", string_get_cstr(p->key));
// In function of the type of value
if (my_number_n_p(p->value)) {
printf ("It is a long long, value = %lld\n",
*my_number_cget_n(p->value));
// Print it.
M_PRINT("It is a long long, value = ", *my_number_cget_n(p->value), "\n");
}
if (my_number_z_p(p->value)) {
printf ("It is a mpz_t, value = ");
mpz_out_str(stdout, 10, *my_number_cget_z(p->value));
printf("\n");
// Print it. Since mpz_t isn't a native type, M_PRINT macro doesn't know it by default
// So we need to add the type to the value.
// M_PRINT will then look at its globally registered oplist
// and known how to print it.
M_PRINT("It is a mpz_t, value = ", (*my_number_cget_z(p->value), mpz_t), "\n");
}
if (my_number_fr_p(p->value)) {
printf ("It is a mpfr_t, value = ");
mpfr_out_str(stdout, 10, 0, *my_number_cget_fr(p->value), MPFR_RNDN);
printf("\n");
// Same but with a MPFR number.
M_PRINT("It is a mpfr_t, value = ", (*my_number_cget_fr(p->value), mpfr_t), "\n");
}
}
printf("Next element of array\n");
@@ -223,7 +225,7 @@ 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)
// 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) {
+28 -2
View File
@@ -14,32 +14,50 @@ RBTREE_DEF(tree_cstr, const char *, M_CSTR_OPLIST)
// It is probably the best for beginner in M*LIB to start like this.
static void classic1(void)
{
// Let's declare a tree on the stack.
// Even if the tree itself is allocated on the stack,
// the nodes will be allocated on the heap.
tree_cstr_t tree;
// Initialize the tree using the provided constructor
tree_cstr_init(tree);
// Let's fill in the tree
// Note that the object of the tree are pointer
// to array of char. Therefore the strings themselves
// are not stored in the tree but only a reference to then
// As such, the pointed objects shall not moved nor being
// destroyed, which is the case for const C string.
tree_cstr_push (tree, "Hello");
tree_cstr_push (tree, "World");
tree_cstr_push (tree, "How");
tree_cstr_push (tree, "Are");
tree_cstr_push (tree, "You");
// Let's print the tree.
// We'll use the OUT_STR method to display the tree
// without iterating ourself in the tree.
printf("\nUsing C string, the generated (sorted) tree is ");
tree_cstr_out_str(stdout, tree);
printf ("\n");
// Let's search for the key "How" in the tree.
// If it is not found a NULL pointer is returned.
// Otherwise a pointer to the key within the tree is returned.
// This pointer is valid until a modification operation on the tree is done.
const char *present = tree_cstr_get(tree, "How") == NULL ? "not " : "";
printf("The word 'How' is %spresent\n", present);
present = tree_cstr_get(tree, "Not") == NULL ? "not " : "";
printf("The word 'Not' is %spresent\n", present);
// Destroy the tree by calling the destructor.
tree_cstr_clear(tree);
}
// Let's define another Red Black Tree over the M*LIB strings
// Notice that we don't need to specify the oplist for this case.
// since the oplist of a string_t is registered globally.
RBTREE_DEF(tree_str, string_t)
// The classic C way
@@ -52,16 +70,24 @@ static void classic2(void)
// STRING_CTE enables to construct a temporary const string_t from a const char *
// usable for function call.
// Note that this time the full string is stored within the tree,
// and not only a pointer to it.
tree_str_push (tree, STRING_CTE("Hello"));
tree_str_push (tree, STRING_CTE("World"));
tree_str_push (tree, STRING_CTE("How"));
tree_str_push (tree, STRING_CTE("Are"));
tree_str_push (tree, STRING_CTE("You"));
// We may also push a const C string into a string_t by calling the emplace function
tree_str_emplace (tree, "You");
// Let's print the tree
printf("\nUsing M*LIB string_t, the generated (sorted) tree is ");
tree_str_out_str(stdout, tree);
printf ("\n");
// Let's search for the string "How"
// We convert it to a temporary string_t, and let's test
// if it is found or not by testing if the returned pointer to string_t
// is null or not.
const char *present = tree_str_get(tree, STRING_CTE("How")) == NULL ? "not " : "";
printf("The word 'How' is %spresent\n", present);
+12
View File
@@ -0,0 +1,12 @@
#ifndef MY_HEADER
#define MY_HEADER
// Request M*LIB to not inline functions if not the library source
#define M_USE_DECL
#include "m-string.h"
#include "m-array.h"
ARRAY_DEF(array_str, string_t)
#endif
+5
View File
@@ -0,0 +1,5 @@
// Request M*LIB to define functions
// This include both m-string functions and array_str templated functions
// Defining M_USE_EXTERN_DEF override M_USE_EXTERN_DECL definition
#define M_USE_DEF
#include "exm-header.h"
+14
View File
@@ -0,0 +1,14 @@
#include "exm-header.h"
int main(void)
{
array_str_t a;
array_str_init(a);
array_str_emplace_back(a, "Hello");
array_str_emplace_back(a, "World");
printf("a=");
array_str_out_str(stdout, a);
printf("\n");
array_str_clear(a);
return 0;
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef MY_HEADER
#define MY_HEADER
// Request M*LIB to not always inline functions
#define M_USE_FINE_GRAINED_LINKAGE
#include "m-string.h"
#include "m-array.h"
// The following functions will not be inlined:
#define M_USE_DECL
ARRAY_DEF(array_str, string_t)
#undef M_USE_DECL
#endif
+4
View File
@@ -0,0 +1,4 @@
// Request M*LIB to define functions for functions defined within M_USE_DECL.
#define M_USE_DEF
#include "exn-header.h"
+19
View File
@@ -0,0 +1,19 @@
#include "exn-header.h"
int main(void)
{
string_t s;
string_init_set_str(s, "a=");
string_fputs(stdout, s);
string_clear(s);
array_str_t a;
array_str_init(a);
array_str_emplace_back(a, "Hello");
array_str_emplace_back(a, "World");
//printf("a=");
array_str_out_str(stdout, a);
printf("\n");
array_str_clear(a);
//main2();
return 0;
}
+140 -122
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - ALGO module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,7 +28,7 @@
#include "m-core.h"
/* Define different kind of basic algorithms named 'name' over the container
which oplist is 'cont_oplist', as static inline functions.
which oplist is 'cont_oplist'.
USAGE:
ALGO_DEF(algogName, containerOplist|type if oplist has been registered) */
#define M_ALGO_DEF(name, cont_oplist) \
@@ -92,7 +92,9 @@
M_ALG0_INSERT_AT(contDst, M_GLOBAL_OPLIST(contDstOp), position, contSrc, M_GLOBAL_OPLIST(contSrcOp) )
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
/*****************************************************************************/
/* Try to expand the algorithms */
#define M_ALG0_DEF_P1(name, cont_oplist) \
@@ -119,36 +121,41 @@
M_CHECK_COMPATIBLE_OPLIST(name, 10, container_t, cont_oplist) \
M_CHECK_COMPATIBLE_OPLIST(name, 11, type_t, type_oplist) \
\
M_ALG0_CALLBACK_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_IF_FUNCOBJ(M_ALG0_FUNCOBJ_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t)) \
M_ALG0_DEF_CALLBACK(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_IF_FUNCOBJ(M_ALG0_DEF_FUNCOBJ(name, container_t, cont_oplist, type_t, type_oplist, it_t)) \
\
M_IF_METHOD(EQUAL, type_oplist)( \
M_ALG0_FIND_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_DEF_FIND(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
, /* NO EQUAL */) \
\
M_ALG0_FIND_IF_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, if, M_C(name, _test_cb_ct), M_C(name, _eq_cb_ct), M_APPLY, M_APPLY) \
M_IF_FUNCOBJ(M_ALG0_FIND_IF_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, fo, M_C(name, _test_obj_t), M_C(name, _eq_obj_t), M_C(name, _test_obj_call), M_C(name, _eq_obj_call))) \
M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \
if, M_F(name,_test_cb_ct), M_F(name,_eq_cb_ct), M_APPLY, M_APPLY) \
M_IF_FUNCOBJ(M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \
fo, M_F(name,_test_obj_t), M_F(name,_eq_obj_t), M_F(name, _test_obj_call), M_F(name, _eq_obj_call))) \
\
M_ALG0_MAP_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_DEF_MAP(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
M_ALG0_ALL_OF_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, _, M_C(name, _test_cb_ct), M_APPLY) \
M_IF_FUNCOBJ(M_ALG0_ALL_OF_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, _fo_, M_C(name, _test_obj_t), M_C(name, _test_obj_call)) ) \
M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \
_, M_F(name,_test_cb_ct), M_APPLY) \
M_IF_FUNCOBJ(M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, \
_fo_, M_F(name,_test_obj_t), M_F(name,_test_obj_call)) ) \
\
/* If there is a IT_REF method, we consider the container as modifiable through iterator */ \
M_IF_METHOD(IT_REF, cont_oplist)( \
M_ALG0_FILL_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_VECTOR_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_DEF_VECTOR(name, container_t, cont_oplist, type_t, type_oplist, it_t)\
\
M_IF_METHOD(CMP, type_oplist)( \
M_ALG0_MINMAX_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_SORT_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, +, _sort) \
M_ALG0_SORT_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, -, _sort_dsc) \
M_ALG0_DEF_MINMAX(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, +, _sort) \
M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, -, _sort_dsc) \
M_IF_METHOD(IT_REMOVE, cont_oplist)( \
M_ALG0_REMOVE_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t)\
, /* No IT_REMOVE method */) \
, /* No CMP method */) \
\
M_IF_FUNCOBJ(M_ALG0_SORT_DEF_P6(name, container_t, cont_oplist, type_t, type_oplist, it_t, _sort_fo, M_ALG0_SORT_CALL_OBJ_P4, M_ALG0_SORT_PARAM_OBJ_P4, M_ALG0_SORT_ARG_OBJ_P4) ) \
M_IF_FUNCOBJ(M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, \
_sort_fo, M_ALG0_SORT_CALL_OBJ_P4, M_ALG0_SORT_PARAM_OBJ_P4, M_ALG0_SORT_ARG_OBJ_P4) ) \
, /* No IT_REF method */) \
\
M_IF_METHOD(EXT_ALGO, type_oplist)( \
@@ -158,36 +165,36 @@
/* Define the types of the callbacks associated to the algorithms.
* Types remain internal */
#define M_ALG0_CALLBACK_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
#define M_ALG0_DEF_CALLBACK(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
typedef bool (*M_C(name, _test_cb_ct))(type_t const); \
typedef bool (*M_C(name, _eq_cb_ct))(type_t const, type_t const); \
typedef int (*M_C(name, _cmp_cb_ct))(type_t const, type_t const); \
typedef void (*M_C(name, _transform_cb_ct))(type_t *, type_t const); \
typedef void (*M_C(name, _apply_cb_ct))(type_t); \
typedef bool (*M_F(name, _test_cb_ct))(type_t const); \
typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \
typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \
typedef void (*M_F(name, _transform_cb_ct))(type_t *, type_t const); \
typedef void (*M_F(name, _apply_cb_ct))(type_t); \
/* Define the function objects associated to the algorithms.
* Created Function objects are part of the public interface */
#define M_ALG0_FUNCOBJ_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
#define M_ALG0_DEF_FUNCOBJ(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
FUNC_OBJ_ITF_DEF(M_C(name, _test_obj), bool, type_t const) \
FUNC_OBJ_ITF_DEF(M_C(name, _eq_obj), bool, type_t const, type_t const ) \
FUNC_OBJ_ITF_DEF(M_C(name, _cmp_obj), int, type_t const, type_t const ) \
FUNC_OBJ_ITF_DEF(M_C(name, _transform_obj), void, type_t *, type_t const ) \
FUNC_OBJ_ITF_DEF(M_C(name, _apply_obj), void, type_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 * ) \
/* Define the sort functions with the CMP operator using the order selected */
#define M_ALG0_SORT_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, order, sort_name) \
#define M_ALG0_DEF_SORT(name, container_t, cont_oplist, type_t, type_oplist, it_t, order, sort_name) \
\
/* Define the encapsulation function that perform the selected order */ \
static inline int M_C3(name,sort_name,_cmp)(type_t const*a,type_t const*b) \
M_INLINE int M_C3(name,sort_name,_cmp)(type_t const*a,type_t const*b) \
{ \
return order M_CALL_CMP(type_oplist, *a, *b); \
} \
\
M_ALG0_SORT_DEF_P6(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, M_ALG0_SORT_CALL_CMP_P4, M_EAT, /*empty*/ )
M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, M_ALG0_SORT_CALL_CMP_P4, M_EAT, /*empty*/ )
// Call the comparaison function of the type oplist (Using CMP operator)
#define M_ALG0_SORT_CALL_CMP_P4(name, sort_name, ref1, ref2) \
@@ -196,9 +203,9 @@
// Call the created function object
#define M_ALG0_SORT_CALL_OBJ_P4(name, sort_name, ref1, ref2) \
M_C(name, _cmp_obj_call)(funcobj, *ref1, *ref2)
M_F(name, _cmp_obj_call)(funcobj, *ref1, *ref2)
// Adding a parameter named 'funcobj' to the algorithm functions
#define M_ALG0_SORT_PARAM_OBJ_P4(name) M_DEFERRED_COMMA M_C(name, _cmp_obj_t) funcobj
#define M_ALG0_SORT_PARAM_OBJ_P4(name) M_DEFERRED_COMMA M_F(name, _cmp_obj_t) funcobj
#define M_ALG0_SORT_ARG_OBJ_P4 M_DEFERRED_COMMA funcobj
@@ -215,10 +222,10 @@
It is needed to add another argument to the function.
- cmp_arg: Name of such argument.
*/
#define M_ALG0_SORT_DEF_P6(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, cmp_func, cmp_param, cmp_arg) \
#define M_ALG0_DEF_SORT_AUX(name, container_t, cont_oplist, type_t, type_oplist, it_t, sort_name, cmp_func, cmp_param, cmp_arg) \
\
/* Test if the container is sorted */ \
static inline bool \
M_INLINE bool \
M_C3(name,sort_name,_p)(const container_t l cmp_param(name)) \
{ \
it_t it1; \
@@ -246,7 +253,7 @@
/* - a selection sort */ \
M_IF(M_AND(M_TEST_METHOD_P(SORT, cont_oplist), M_EMPTY_P(cmp_arg)))( \
/******** OPTIMIZED SORT FOR CONTAINER *********/ \
static inline void M_C(name,sort_name)(container_t l) \
M_INLINE void M_F(name,sort_name)(container_t l) \
{ \
M_CALL_SORT(cont_oplist, l, M_C3(name, sort_name,_cmp)); \
} \
@@ -258,7 +265,7 @@
\
/* Split the container 'l' into near even size l1 and l2 * \
using odd/even splitting */ \
static inline void \
M_INLINE void \
M_C3(name,sort_name,_split)(container_t l1, container_t l2, container_t l) \
{ \
it_t it; \
@@ -273,7 +280,7 @@
} \
\
/* Merge in empty 'l' the sorted container 'l1' and 'l2' */ \
static inline void \
M_INLINE void \
M_C3(name,sort_name,_merge)(container_t l, container_t l1, container_t l2 cmp_param(name)) \
{ \
/* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \
@@ -313,8 +320,8 @@
} \
\
/* Sort the container 'l' */ \
static inline void \
M_C(name,sort_name)(container_t l cmp_param(name)) \
M_INLINE void \
M_F(name,sort_name)(container_t l cmp_param(name)) \
{ \
container_t l1; \
container_t l2; \
@@ -346,8 +353,8 @@
M_CALL_INIT(cont_oplist, l1); \
M_CALL_INIT(cont_oplist, l2); \
M_C3(name,sort_name,_split)(l1, l2, l); \
M_C(name,sort_name)(l1 cmp_arg); \
M_C(name,sort_name)(l2 cmp_arg); \
M_F(name,sort_name)(l1 cmp_arg); \
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_CALL_CLEAR(cont_oplist, l2); \
@@ -356,7 +363,7 @@
, \
M_IF_METHOD(IT_PREVIOUS, cont_oplist)( \
/******** GENERIC INSERTION SORT *********/ \
static inline void M_C(name,sort_name)(container_t l cmp_param(name)) \
M_INLINE void M_F(name,sort_name)(container_t l cmp_param(name)) \
{ \
it_t it1; \
it_t it2; \
@@ -393,7 +400,7 @@
\
, \
/********** GENERIC SELECTION SORT ************/ \
static inline void M_C(name,sort_name)(container_t l cmp_param(name)) \
M_INLINE void M_F(name,sort_name)(container_t l cmp_param(name)) \
{ \
it_t it1; \
it_t it2; \
@@ -426,7 +433,7 @@
) /* IF SORT METHOD */ \
/* Compute the union of two ***sorted*** containers */ \
M_IF_METHOD(IT_INSERT, cont_oplist)( \
static inline void \
M_INLINE void \
M_C3(name,sort_name,_union)(container_t dst, const container_t src cmp_param(name)) \
{ \
it_t itSrc; \
@@ -471,7 +478,7 @@
\
/* Compute the intersection of two ***sorted*** containers */ \
M_IF_METHOD(IT_REMOVE, cont_oplist)( \
static inline void \
M_INLINE void \
M_C3(name,sort_name,_intersect)(container_t dst, const container_t src cmp_param(name)) \
{ \
it_t itSrc; \
@@ -507,11 +514,11 @@
TODO: Define _find_sorted that find in a sorted random access container
(binary search)
*/
#define M_ALG0_FIND_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
#define M_ALG0_DEF_FIND(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
/* It supposes that the container is not sorted */ \
/* Find the next occurrence from it (included) of data */ \
static inline void \
M_C(name, _find_again) (it_t it, type_t const data) \
M_INLINE void \
M_F(name, _find_again) (it_t it, type_t const data) \
{ \
for ( /*nothing*/ ; !M_CALL_IT_END_P(cont_oplist, it) ; \
M_CALL_IT_NEXT(cont_oplist, it)) { \
@@ -521,19 +528,19 @@
} \
\
/* Find the first occurrence of data */ \
static inline void \
M_C(name, _find) (it_t it, container_t const l, type_t const data) \
M_INLINE void \
M_F(name, _find) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_FIRST(cont_oplist, it, l); \
M_C(name, _find_again)(it, data); \
M_F(name, _find_again)(it, data); \
} \
\
/* Test if data is within the container */ \
static inline bool \
M_C(name, _contain_p) (container_t const l, type_t const data) \
M_INLINE bool \
M_F(name, _contain_p) (container_t const l, type_t const data) \
{ \
it_t it; \
M_C(name,_find)(it, l, data); \
M_F(name,_find)(it, l, data); \
return !M_CALL_IT_END_P(cont_oplist, it); \
} \
\
@@ -542,8 +549,8 @@
PREVIOUS & IT_LAST are defined, then search backwards */ \
M_IF_METHOD2(PREVIOUS, IT_LAST, cont_oplist) \
( \
static inline void \
M_C(name, _find_last) (it_t it, container_t const l, type_t const data) \
M_INLINE void \
M_F(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
for (M_CALL_IT_LAST(cont_oplist, it, l); \
!M_CALL_IT_END_P(cont_oplist, it) ; \
@@ -555,8 +562,8 @@
} \
, \
/* Otherwise search forward, but don't stop on the first occurrence */ \
static inline void \
M_C(name, _find_last) (it_t it, container_t const l, type_t const data) \
M_INLINE void \
M_F(name, _find_last) (it_t it, container_t const l, type_t const data) \
{ \
M_CALL_IT_END(cont_oplist, it, l); \
it_t it2; \
@@ -571,8 +578,8 @@
) /* End of alternative of _find_last */ \
\
/* Count the number of occurrence of data in the container */ \
static inline size_t \
M_C(name, _count) (container_t const l, type_t const data) \
M_INLINE size_t \
M_F(name, _count) (container_t const l, type_t const data) \
{ \
it_t it; \
size_t count = 0; \
@@ -587,8 +594,8 @@
\
\
/* Find the next mismatch between the containers */ \
static inline void \
M_C(name, _mismatch_again) (it_t it1, it_t it2) \
M_INLINE void \
M_F(name, _mismatch_again) (it_t it1, it_t it2) \
{ \
for (/* nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
!M_CALL_IT_END_P(cont_oplist, it2); \
@@ -601,12 +608,12 @@
} \
\
/* Find the first mismatch between the containers */ \
static inline void \
M_C(name, _mismatch) (it_t it1, it_t it2, container_t const l1, container_t const l2 ) \
M_INLINE void \
M_F(name, _mismatch) (it_t it1, it_t it2, container_t const l1, container_t const l2 ) \
{ \
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
M_CALL_IT_FIRST(cont_oplist, it2, l2); \
M_C(name, _mismatch_again)(it1, it2); \
M_F(name, _mismatch_again)(it1, it2); \
} \
@@ -615,10 +622,10 @@
TODO: Define _find_sorted that find in a sorted random access container
(binary search)
*/
#define M_ALG0_FIND_IF_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, test_t, eq_t, call_test, call_eq) \
#define M_ALG0_DEF_FIND_IF(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, test_t, eq_t, call_test, call_eq) \
\
/* Find the next occurrence that matches the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _find_again_, suffix) (it_t it, test_t func) \
{ \
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it) ; \
@@ -629,7 +636,7 @@
} \
\
/* Find the first occurrence that matches the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _find_, suffix) (it_t it, container_t l, test_t func) \
{ \
M_CALL_IT_FIRST(cont_oplist, it, l); \
@@ -637,7 +644,7 @@
} \
\
/* Count the number of occurrence that matches the condition */ \
static inline size_t \
M_INLINE size_t \
M_C3(name, _count_, suffix) (container_t const l, test_t func) \
{ \
it_t it; \
@@ -653,7 +660,7 @@
} \
\
/* Find the next mismatch between the containers according to the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _mismatch_again_, suffix) (it_t it1, it_t it2, eq_t func) \
{ \
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
@@ -667,7 +674,7 @@
} \
\
/* Find the first mismatch between the containers according to the condition */ \
static inline void \
M_INLINE void \
M_C3(name, _mismatch_, suffix) (it_t it1, it_t it2, container_t const l1, \
container_t l2, eq_t func) \
{ \
@@ -678,11 +685,11 @@
/* Define the FILL algorithms */
#define M_ALG0_FILL_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
#define M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
/* Fill all the container with value (overwritten) */ \
static inline void \
M_C(name, _fill) (container_t l, type_t const value) \
M_INLINE void \
M_F(name, _fill) (container_t l, type_t const value) \
{ \
for M_EACH(item, l, cont_oplist) { \
M_CALL_SET(type_oplist, *item, value); \
@@ -691,8 +698,8 @@
\
/* Fill the container with exactly 'n' occurrence of 'value' */ \
M_IF_METHOD(PUSH, cont_oplist)( \
static inline void \
M_C(name, _fill_n) (container_t l, size_t n, type_t const value) \
M_INLINE void \
M_F(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++) { \
@@ -703,8 +710,8 @@
\
/* Fill the container with FOR('value'; 'value'+'inc') */ \
M_IF_METHOD(ADD, type_oplist)( \
static inline void \
M_C(name, _fill_a) (container_t l, type_t const value, type_t const inc) \
M_INLINE void \
M_F(name, _fill_a) (container_t l, type_t const value, type_t const inc) \
{ \
type_t tmp; \
M_CALL_INIT_SET(type_oplist, tmp, value); \
@@ -717,8 +724,8 @@
\
/* Fill the container with n occurences of FOR('value'; 'value'+'inc') */ \
M_IF_METHOD(PUSH, cont_oplist)( \
static inline void \
M_C(name, _fill_an) (container_t l, size_t n, type_t const value, type_t const inc) \
M_INLINE void \
M_F(name, _fill_an) (container_t l, size_t n, type_t const value, type_t const inc) \
{ \
type_t tmp; \
M_CALL_INIT_SET(type_oplist, tmp, value); \
@@ -734,11 +741,11 @@
/* Define MAP algorithms */
#define M_ALG0_MAP_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
#define M_ALG0_DEF_MAP(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
/* Apply func for all elements of the container */ \
static inline void \
M_C(name, _for_each) (container_t l, M_C(name, _apply_cb_ct) func) \
M_INLINE void \
M_F(name, _for_each) (container_t l, M_F(name, _apply_cb_ct) func) \
{ \
for M_EACH(item, l, cont_oplist) { \
func(*item); \
@@ -749,10 +756,10 @@
M_IF_METHOD(PUSH_MOVE, cont_oplist)( \
\
/* Apply func for all elements of the container src and push the result in dst */ \
static inline void \
M_C(name, _transform) (container_t dst, \
M_INLINE void \
M_F(name, _transform) (container_t dst, \
container_t src, \
M_C(name, _transform_cb_ct) func) \
M_F(name, _transform_cb_ct) func) \
{ \
M_ASSERT(dst != src); \
M_CALL_RESET(cont_oplist, dst); \
@@ -768,9 +775,9 @@
\
/* Reduce all elements of the container in dst in function of func */ \
M_IF_METHOD(SET, type_oplist)( \
static inline void \
M_C(name, _reduce) (type_t *dest, container_t const l, \
M_C(name, _transform_cb_ct) func) \
M_INLINE void \
M_F(name, _reduce) (type_t *dest, container_t const l, \
M_F(name, _transform_cb_ct) func) \
{ \
bool initDone = false; \
for M_EACH(item, l, cont_oplist) { \
@@ -786,11 +793,11 @@
\
/* Reduce all transformed elements of the container in dst in function of func */ \
M_IF_METHOD(INIT, type_oplist)( \
static inline \
void M_C(name, _map_reduce) (type_t *dest, \
M_INLINE \
void M_F(name, _map_reduce) (type_t *dest, \
const container_t l, \
M_C(name, _transform_cb_ct) redFunc, \
M_C(name, _transform_cb_ct) mapFunc) \
M_F(name, _transform_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) mapFunc) \
{ \
bool initDone = false; \
type_t tmp; \
@@ -810,9 +817,9 @@
/* Define ALL_OF algorithms */
#define M_ALG0_ALL_OF_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, func_t, call) \
#define M_ALG0_DEF_ALL_OF(name, container_t, cont_oplist, type_t, type_oplist, it_t, suffix, func_t, call) \
\
static inline bool \
M_INLINE bool \
M_C4(name, _any_of, suffix, p) (container_t const l, \
func_t func ) \
{ \
@@ -823,7 +830,7 @@
return false; \
} \
\
static inline bool \
M_INLINE bool \
M_C4(name, _all_of, suffix, p) (container_t const l, \
func_t func ) \
{ \
@@ -834,7 +841,7 @@
return true; \
} \
\
static inline bool \
M_INLINE bool \
M_C4(name, _none_of, suffix, p) (container_t l, \
func_t func ) \
{ \
@@ -847,10 +854,10 @@
/* Define MIN / MAX algorithms */
#define M_ALG0_MINMAX_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
#define M_ALG0_DEF_MINMAX(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
static inline type_t * \
M_C(name, _min) (const container_t l) \
M_INLINE type_t * \
M_F(name, _min) (const container_t l) \
{ \
type_t *min = NULL; \
for M_EACH(cref, l, cont_oplist) { \
@@ -862,8 +869,8 @@
return min; \
} \
\
static inline type_t * \
M_C(name, _max) (const container_t l) \
M_INLINE type_t * \
M_F(name, _max) (const container_t l) \
{ \
type_t *max = NULL; \
for M_EACH(cref, l, cont_oplist) { \
@@ -875,8 +882,8 @@
return max; \
} \
\
static inline void \
M_C(name, _minmax) (type_t **min_p, type_t **max_p, \
M_INLINE void \
M_F(name, _minmax) (type_t **min_p, type_t **max_p, \
const container_t l) \
{ \
type_t *min = NULL; \
@@ -895,15 +902,15 @@
*max_p = max; \
} \
#define M_ALG0_REMOVE_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
/* Define functions based on remove method */
#define M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
static inline void \
M_C(name, _uniq)(container_t l) \
M_INLINE void \
M_F(name, _uniq)(container_t l) \
{ \
it_t it1; \
it_t it2; \
M_ASSERT(M_C(name, _sort_p)(l)); \
M_ASSERT(M_F(name, _sort_p)(l)); \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
M_CALL_IT_SET(cont_oplist, it2, it1); \
M_CALL_IT_NEXT(cont_oplist, it2); \
@@ -920,8 +927,8 @@
} \
} \
\
static inline void \
M_C(name, _remove_val)(container_t l, type_t const val) \
M_INLINE void \
M_F(name, _remove_val)(container_t l, type_t const val) \
{ \
it_t it1; \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
@@ -935,8 +942,8 @@
} \
} \
\
static inline void \
M_C(name, _remove_if)(container_t l, M_C(name, _test_cb_ct) func) \
M_INLINE void \
M_F(name, _remove_if)(container_t l, M_F(name, _test_cb_ct) func) \
{ \
it_t it1; \
M_CALL_IT_FIRST(cont_oplist, it1, l); \
@@ -950,11 +957,11 @@
} \
} \
#define M_ALG0_VECTOR_DEF_P5(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
/* Define vector algorithms */
#define M_ALG0_DEF_VECTOR(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
\
M_IF_METHOD(ADD, type_oplist)( \
static inline void M_C(name, _add) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _add) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -972,7 +979,7 @@
, /* NO_ADD METHOD */ ) \
\
M_IF_METHOD(SUB, type_oplist)( \
static inline void M_C(name, _sub) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _sub) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -990,7 +997,7 @@
, /* NO_SUB METHOD */ ) \
\
M_IF_METHOD(MUL, type_oplist)( \
static inline void M_C(name, _mul) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _mul) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -1008,7 +1015,7 @@
, /* NO_MUL METHOD */ ) \
\
M_IF_METHOD(DIV, type_oplist)( \
static inline void M_C(name, _div) (container_t dst, const container_t src) \
M_INLINE void M_F(name, _div) (container_t dst, const container_t src) \
{ \
it_t itSrc; \
it_t itDst; \
@@ -1034,6 +1041,7 @@
// fast _uniq for array
// _flatten (takes a set of containers and returns a new container containing all flatten objects)
/******************************** INTERNAL ***********************************/
#define M_ALG0_FOR_EACH(container, cont_oplist, func) do { \
for M_EACH(_item, container, cont_oplist) { \
@@ -1048,6 +1056,8 @@
} while (0)
/******************************** INTERNAL ***********************************/
#define M_ALG0_TRANSFORM(contD, contDop, contS, contSop, func) do { \
M_CALL_RESET(contDop, contD); \
for M_EACH(_item, contS, contSop) { \
@@ -1071,6 +1081,8 @@
} while (0)
/******************************** INTERNAL ***********************************/
#define M_ALG0_EXTRACT(contDst, contDstOplist, contSrc, contSrcOplist) \
M_ALG0_EXTRACT_P1(contDst, M_GLOBAL_OPLIST(contDstOplist), contSrc, M_GLOBAL_OPLIST(contSrcOplist))
#define M_ALG0_EXTRACT_P1(contDst, contDstOplist, contSrc, contSrcOplist) do{ \
@@ -1122,6 +1134,8 @@
} while (0)
/******************************** INTERNAL ***********************************/
#define M_ALG0_REDUCE_DISPATCH(dest, destOp, dest_t, cont, contOp, ...) \
M_IF_NARGS_EQ1(__VA_ARGS__) \
(M_ALG0_REDUCE_BASIC(dest, dest_t, destOp, cont, contOp, __VA_ARGS__), \
@@ -1197,6 +1211,8 @@
} while (0)
/******************************** INTERNAL ***********************************/
#define M_ALG0_INSERT_AT(contDst, contDstOp, position, contSrc, contSrcOp) do { \
M_GET_IT_TYPE contSrcOp _itSrc; \
M_GET_IT_TYPE contDstOp _itDst; \
@@ -1210,6 +1226,8 @@
} while (0)
/******************************** INTERNAL ***********************************/
#if M_USE_SMALL_NAME
#define ALGO_DEF M_ALGO_DEF
#define ALGO_FOR_EACH M_ALGO_FOR_EACH
+357 -347
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin stdatomic wrapper for C++ compatibility
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -154,7 +154,7 @@ using std::memory_order_seq_cst;
*/
#else
#include "m-mutex.h"
#include "m-thread.h"
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
@@ -226,7 +226,7 @@ typedef _Atomic(uintmax_t) atomic_uintmax_t;
#endif
/* (INTERNAL) Unlock the mutex and return the given value */
static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
M_INLINE long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
{
m_mutex_unlock (*lock);
return val;
+70 -65
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - BITSET module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -81,7 +81,7 @@ typedef struct m_bitset_it_s {
} m_bitset_it_t[1];
/* Initialize a bitset (CONSTRUCTOR) */
static inline void
M_INLINE void
m_bitset_init(m_bitset_t t)
{
M_ASSERT (t != NULL);
@@ -93,7 +93,7 @@ m_bitset_init(m_bitset_t t)
}
/* Clean a bitset */
static inline void
M_INLINE void
m_bitset_reset(m_bitset_t t)
{
M_B1TSET_CONTRACT(t);
@@ -101,7 +101,7 @@ m_bitset_reset(m_bitset_t t)
}
/* Clear a bitset (DESTRUCTOR) */
static inline void
M_INLINE void
m_bitset_clear(m_bitset_t t)
{
m_bitset_reset(t);
@@ -114,29 +114,34 @@ m_bitset_clear(m_bitset_t t)
}
/* Set a bitset to another one */
static inline void
M_INLINE void
m_bitset_set(m_bitset_t d, const m_bitset_t s)
{
M_B1TSET_CONTRACT(d);
M_B1TSET_CONTRACT(s);
if (M_UNLIKELY (d == s)) return;
const size_t needAlloc = M_B1TSET_TO_ALLOC (s->size);
if (s->size > M_B1TSET_FROM_ALLOC (d->alloc)) {
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, d->ptr, needAlloc);
if (M_UNLIKELY (ptr == NULL)) {
M_MEMORY_FULL(needAlloc);
return ;
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);
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(needAlloc);
return ;
}
d->ptr = ptr;
d->alloc = needAlloc;
}
d->ptr = ptr;
d->alloc = needAlloc;
M_ASSERT(d->ptr != NULL);
M_ASSERT(s->ptr != NULL);
memcpy (d->ptr, s->ptr, needAlloc * sizeof(m_b1tset_limb_ct) );
}
memcpy (d->ptr, s->ptr, needAlloc * sizeof(m_b1tset_limb_ct) );
d->size = s->size;
M_B1TSET_CONTRACT(d);
}
/* Initialize & set a bitset to another one (CONSTRUCTOR) */
static inline void
M_INLINE void
m_bitset_init_set(m_bitset_t d, const m_bitset_t s)
{
M_ASSERT (d != s);
@@ -145,7 +150,7 @@ m_bitset_init_set(m_bitset_t d, const m_bitset_t s)
}
/* Initialize & move a bitset (CONSTRUCTOR) from another one (DESTRUCTOR) */
static inline void
M_INLINE void
m_bitset_init_move(m_bitset_t d, m_bitset_t s)
{
M_B1TSET_CONTRACT(s);
@@ -159,7 +164,7 @@ m_bitset_init_move(m_bitset_t d, m_bitset_t s)
}
/* Move a bitset from another one (DESTRUCTOR) */
static inline void
M_INLINE void
m_bitset_move(m_bitset_t d, m_bitset_t s)
{
m_bitset_clear(d);
@@ -167,7 +172,7 @@ m_bitset_move(m_bitset_t d, m_bitset_t s)
}
/* Set the bit 'i' in the bitset to the value 'x' */
static inline void
M_INLINE void
m_bitset_set_at(m_bitset_t v, size_t i, bool x)
{
M_B1TSET_CONTRACT(v);
@@ -182,7 +187,7 @@ m_bitset_set_at(m_bitset_t v, size_t i, bool x)
}
/* Flip the bit 'i' in the bitset */
static inline void
M_INLINE void
m_bitset_flip_at(m_bitset_t v, size_t i)
{
M_B1TSET_CONTRACT(v);
@@ -195,7 +200,7 @@ m_bitset_flip_at(m_bitset_t v, size_t i)
}
/* Push back the boolean 'x' in the bitset (increasing the bitset) */
static inline void
M_INLINE void
m_bitset_push_back (m_bitset_t v, bool x)
{
M_B1TSET_CONTRACT (v);
@@ -203,14 +208,14 @@ m_bitset_push_back (m_bitset_t v, bool x)
// Compute the needed allocation.
const size_t needAlloc = M_B1TSET_INC_ALLOC_SIZE(v->alloc);
// Check for integer overflow
if (M_UNLIKELY (needAlloc <= v->alloc)) {
if (M_UNLIKELY_NOMEM (needAlloc <= v->alloc)) {
M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct));
return;
}
// Alloc memory
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, needAlloc);
// Check if success
if (M_UNLIKELY (ptr == NULL) ) {
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct));
return;
}
@@ -234,12 +239,12 @@ m_bitset_push_back (m_bitset_t v, bool x)
}
/* Resize the bitset to have exactly 'size' bits */
static inline void
M_INLINE void
m_bitset_resize (m_bitset_t v, size_t size)
{
M_B1TSET_CONTRACT (v);
// Check for overflow
if (M_UNLIKELY (size >= ((size_t)-1) - M_B1TSET_LIMB_BIT)) {
if (M_UNLIKELY_NOMEM (size >= ((size_t)-1) - M_B1TSET_LIMB_BIT)) {
M_MEMORY_FULL((size_t) -1);
return;
}
@@ -248,7 +253,7 @@ m_bitset_resize (m_bitset_t v, size_t 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);
if (M_UNLIKELY (ptr == NULL) ) {
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct));
return;
}
@@ -282,7 +287,7 @@ m_bitset_resize (m_bitset_t v, size_t size)
}
/* Reserve allocation in the bitset to accomodate at least 'size' bits without reallocation */
static inline void
M_INLINE void
m_bitset_reserve (m_bitset_t v, size_t alloc)
{
M_B1TSET_CONTRACT (v);
@@ -300,7 +305,7 @@ m_bitset_reserve (m_bitset_t v, size_t alloc)
} else {
// Allocate more memory or reduce memory usage
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc);
if (M_UNLIKELY (ptr == NULL) ) {
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct));
return;
}
@@ -314,7 +319,7 @@ m_bitset_reserve (m_bitset_t v, size_t alloc)
* NOTE: Interface is a little bit different:
* It doesn't return a pointer to the data, but the data itself.
*/
static inline bool
M_INLINE bool
m_bitset_get(const m_bitset_t v, size_t i)
{
M_B1TSET_CONTRACT(v);
@@ -329,7 +334,7 @@ m_bitset_get(const m_bitset_t v, size_t i)
#define m_bitset_cget m_bitset_get
/* Pop back the last bit in the bitset */
static inline void
M_INLINE void
m_bitset_pop_back(bool *dest, m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -349,7 +354,7 @@ m_bitset_pop_back(bool *dest, m_bitset_t v)
}
/* Return the front bit value in the bitset */
static inline bool
M_INLINE bool
m_bitset_front(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -358,7 +363,7 @@ m_bitset_front(m_bitset_t v)
}
/* Return the back bit value in the bitset */
static inline bool
M_INLINE bool
m_bitset_back(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -367,7 +372,7 @@ m_bitset_back(m_bitset_t v)
}
/* Test if the bitset is empty (no bits stored)*/
static inline bool
M_INLINE bool
m_bitset_empty_p(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -375,7 +380,7 @@ m_bitset_empty_p(m_bitset_t v)
}
/* Return the number of bits of the bitset */
static inline size_t
M_INLINE size_t
m_bitset_size(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -383,7 +388,7 @@ m_bitset_size(m_bitset_t v)
}
/* Return the capacity in limbs of the bitset */
static inline size_t
M_INLINE size_t
m_bitset_capacity(m_bitset_t v)
{
M_B1TSET_CONTRACT (v);
@@ -391,7 +396,7 @@ m_bitset_capacity(m_bitset_t v)
}
/* Swap the bit at index i and j of the bitset */
static inline void
M_INLINE void
m_bitset_swap_at (m_bitset_t v, size_t i, size_t j)
{
M_ASSERT_INDEX(i, v->size);
@@ -404,7 +409,7 @@ m_bitset_swap_at (m_bitset_t v, size_t i, size_t j)
}
/* Swap the bitsets */
static inline void
M_INLINE void
m_bitset_swap (m_bitset_t v1, m_bitset_t v2)
{
M_B1TSET_CONTRACT (v1);
@@ -420,7 +425,7 @@ m_bitset_swap (m_bitset_t v1, m_bitset_t v2)
* integrating the carry in the lowest position.
* Return the new carry.
*/
static inline m_b1tset_limb_ct
M_INLINE m_b1tset_limb_ct
m_b1tset_lshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
{
for(size_t i = 0; i < n; i++) {
@@ -435,7 +440,7 @@ m_b1tset_lshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
* integrating the carry in the lowest position.
* Return the new carry.
*/
static inline m_b1tset_limb_ct
M_INLINE m_b1tset_limb_ct
m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
{
for(size_t i = n - 1; i < n; i--) {
@@ -448,7 +453,7 @@ m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
/* Insert a new bit at position 'key' of value 'value' in the bitset 'set'
shifting the set accordingly */
static inline void
M_INLINE void
m_bitset_push_at(m_bitset_t set, size_t key, bool value)
{
M_B1TSET_CONTRACT (set);
@@ -476,7 +481,7 @@ m_bitset_push_at(m_bitset_t set, size_t key, bool value)
/* Pop a new bit at position 'key' in the bitset
* and return in *dest its value if *dest exists */
static inline void
M_INLINE void
m_bitset_pop_at(bool *dest, m_bitset_t set, size_t key)
{
M_B1TSET_CONTRACT (set);
@@ -502,7 +507,7 @@ m_bitset_pop_at(bool *dest, m_bitset_t set, size_t key)
}
/* Test if two bitsets are equal */
static inline bool
M_INLINE bool
m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
{
M_B1TSET_CONTRACT (set1);
@@ -519,7 +524,7 @@ m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
}
/* Initialize an iterator to the first bit of the biset */
static inline void
M_INLINE void
m_bitset_it(m_bitset_it_t it, m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -528,7 +533,7 @@ m_bitset_it(m_bitset_it_t it, m_bitset_t set)
}
/* Initialize an iterator to reference the same bit as the given one*/
static inline void
M_INLINE void
m_bitset_it_set(m_bitset_it_t it, const m_bitset_it_t itorg)
{
M_ASSERT (it != NULL && itorg != NULL);
@@ -537,7 +542,7 @@ m_bitset_it_set(m_bitset_it_t it, const m_bitset_it_t itorg)
}
/* Initialize an iterator to reference the last bit of the bitset*/
static inline void
M_INLINE void
m_bitset_it_last(m_bitset_it_t it, m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -546,7 +551,7 @@ m_bitset_it_last(m_bitset_it_t it, m_bitset_t set)
}
/* Initialize an iterator to reference no valid bit of the bitset*/
static inline void
M_INLINE void
m_bitset_it_end(m_bitset_it_t it, m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -555,7 +560,7 @@ m_bitset_it_end(m_bitset_it_t it, m_bitset_t set)
}
/* Test if an iterator references no valid bit of the bitset anymore */
static inline bool
M_INLINE bool
m_bitset_end_p(const m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -563,7 +568,7 @@ m_bitset_end_p(const m_bitset_it_t it)
}
/* Test if an iterator references the last (or end) bit of the bitset anymore */
static inline bool
M_INLINE bool
m_bitset_last_p(const m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -573,7 +578,7 @@ m_bitset_last_p(const m_bitset_it_t it)
}
/* Test if both iterators reference the same bit */
static inline bool
M_INLINE bool
m_bitset_it_equal_p(const m_bitset_it_t it1, const m_bitset_it_t it2)
{
M_ASSERT (it1 != NULL && it2 != NULL);
@@ -581,7 +586,7 @@ m_bitset_it_equal_p(const m_bitset_it_t it1, const m_bitset_it_t it2)
}
/* Move the iterator to the next bit */
static inline void
M_INLINE void
m_bitset_next(m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -589,7 +594,7 @@ m_bitset_next(m_bitset_it_t it)
}
/* Move the iterator to the previous bit */
static inline void
M_INLINE void
m_bitset_previous(m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -600,7 +605,7 @@ m_bitset_previous(m_bitset_it_t it)
/* Return a pointer to the bit referenced by the iterator
* Only one reference is possible at a time per iterator */
static inline const bool *
M_INLINE const bool *
m_bitset_cref(m_bitset_it_t it)
{
M_ASSERT (it != NULL && it->set != NULL);
@@ -609,7 +614,7 @@ m_bitset_cref(m_bitset_it_t it)
}
/* Output the bitset as a formatted text in a FILE */
static inline void
M_INLINE void
m_bitset_out_str(FILE *file, const m_bitset_t set)
{
M_B1TSET_CONTRACT (set);
@@ -624,7 +629,7 @@ m_bitset_out_str(FILE *file, const m_bitset_t set)
}
/* Input the bitset from a formatted text in a FILE */
static inline bool
M_INLINE bool
m_bitset_in_str(m_bitset_t set, FILE *file)
{
M_B1TSET_CONTRACT (set);
@@ -643,7 +648,7 @@ m_bitset_in_str(m_bitset_t set, FILE *file)
}
/* Parse the bitset from a formatted text in a C string */
static inline bool
M_INLINE bool
m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
{
M_B1TSET_CONTRACT (set);
@@ -667,7 +672,7 @@ m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
}
/* Set the bitset from a formatted text in a C string */
static inline bool
M_INLINE bool
m_bitset_set_str(m_bitset_t dest, const char str[])
{
return m_bitset_parse_str(dest, str, NULL);
@@ -675,7 +680,7 @@ m_bitset_set_str(m_bitset_t dest, const char str[])
/* Perform an AND operation between the bitsets,
* up to the minimum size of both bitsets */
static inline void
M_INLINE void
m_bitset_and(m_bitset_t dest, const m_bitset_t src)
{
M_B1TSET_CONTRACT(dest);
@@ -691,7 +696,7 @@ m_bitset_and(m_bitset_t dest, const m_bitset_t src)
/* Perform an OR operation between the bitsets,
* up to the minimum size of both bitsets */
static inline void
M_INLINE void
m_bitset_or(m_bitset_t dest, const m_bitset_t src)
{
M_B1TSET_CONTRACT(dest);
@@ -707,7 +712,7 @@ m_bitset_or(m_bitset_t dest, const m_bitset_t src)
/* Perform an XOR operation between the bitsets,
* up to the minimum size of both bitsets */
static inline void
M_INLINE void
m_bitset_xor(m_bitset_t dest, const m_bitset_t src)
{
M_B1TSET_CONTRACT(dest);
@@ -728,7 +733,7 @@ m_bitset_xor(m_bitset_t dest, const m_bitset_t src)
}
/* Perform a NOT operation of the bitset */
static inline void
M_INLINE void
m_bitset_not(m_bitset_t dest)
{
M_B1TSET_CONTRACT(dest);
@@ -746,7 +751,7 @@ m_bitset_not(m_bitset_t dest)
}
/* Copute a hash of the bitset */
static inline size_t
M_INLINE size_t
m_bitset_hash(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -759,7 +764,7 @@ m_bitset_hash(const m_bitset_t set)
}
/* Count the number of leading zero */
static inline size_t
M_INLINE size_t
m_bitset_clz(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -787,7 +792,7 @@ m_bitset_clz(const m_bitset_t set)
}
/* Count the number of trailing zero */
static inline size_t
M_INLINE size_t
m_bitset_ctz(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -814,14 +819,14 @@ m_bitset_ctz(const m_bitset_t set)
// For GCC or CLANG or ICC
#if defined(__GNUC__)
static inline size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
{
return (size_t) __builtin_popcountll(limb);
}
#else
// MSVC __popcnt64 may not exist on the target architecture (no emulation layer)
// Use emulation layer: https://en.wikipedia.org/wiki/Hamming_weight
static inline size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
{
limb = limb - ((limb >> 1) & 0x5555555555555555ULL);
limb = (limb & 0x3333333333333333ULL) + ((limb >> 2) & 0x3333333333333333ULL);
@@ -831,7 +836,7 @@ static inline size_t m_b1tset_popcount64(m_b1tset_limb_ct limb)
#endif
/* Count the number of 1 */
static inline size_t
M_INLINE size_t
m_bitset_popcount(const m_bitset_t set)
{
M_B1TSET_CONTRACT(set);
@@ -957,7 +962,7 @@ M_BEGIN_PROTECTED_CODE
/* Output to a m_string_t 'str' the formatted text representation of the bitset 'set'
or append it to the strinf (append=true) */
static inline void
M_INLINE void
m_bitset_get_str(m_string_t str, const m_bitset_t set, bool append)
{
M_B1TSET_CONTRACT (set);
+380 -368
View File
File diff suppressed because it is too large. Load diff
+230 -165
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -26,7 +26,7 @@
#define MSTARLIB_BUFFER_H
#include "m-core.h"
#include "m-mutex.h"
#include "m-thread.h"
#include "m-atomic.h"
/* Define the different kind of policy a lock-based buffer can have:
@@ -51,23 +51,53 @@ typedef enum {
} m_buffer_policy_e;
// TOMOVE to m-core
#define M_DEF_AS(macro, ...) \
M_BEGIN_PROTECTED_CODE \
M_BY_NARGS(macro, __VA_ARGS__)(__VA_ARGS__) \
M_END_PROTECTED_CODE
#define M_SIZE_AS(oplist, default) M_GET_METHOD(SIZE, default, M_OPFLAT oplist)
#define M_POLICY_AS(oplist, default) M_GET_METHOD(POLICY, default, M_OPFLAT oplist)
#define M_TYPE_AS(oplist, default) M_GET_METHOD(TYPE, default, M_OPFLAT oplist)
#define M_TYPE2OPL(type) M_GLOBAL_OPLIST_OR_DEF(type)()
/* Define a lock based buffer.
If size is 0, then the size will only be defined at run-time when initializing the buffer,
otherwise the size will be a compile time constant.
USAGE: BUFFER_DEF(name, type, size_of_buffer_or_0, policy[, oplist]) */
#define M_BUFFER_DEF(name, type, m_size, ... ) \
M_BUFFER_DEF_AS(name, M_C(name, _t), type, m_size, __VA_ARGS__)
#define M_BUFFER_DEF(a, b, c, ... ) M_DEF_AS(M_BUFF3R_DEF, a, b, c, __VA_ARGS__)
#define M_BUFF3R_DEF__4(n, t, s, p) M_BUFF3R_DEF_P1((n, t, s, p, M_TYPE2OPL(t), M_F(n, _t)))
#define M_BUFF3R_DEF__5(n, t, s, p, o) M_BUFF3R_DEF_P1((n, t, s, p, o, M_F(n, _t)))
#define M_BUFF3R_DEF__6(n, t, s, p, o)
#define M_BUFF3R_DEF__7(n, t, s, p, o)
#define M_BUFF3R_DEF__8(n, t, s, p, o)
#define M_BUFF3R_DEF__9(n, t, s, p, o)
/* Define a lock based buffer
as the provided type name_t.
USAGE: BUFFER_DEF_AS(name, name_t, type, size_of_buffer_or_0, policy[, oplist of type]) */
#define M_BUFFER_DEF_AS(name, name_t, type, m_size, ... ) \
M_BEGIN_PROTECTED_CODE \
M_BUFF3R_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, type, m_size,__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t ), \
(name, type, m_size,__VA_ARGS__, name_t ))) \
M_END_PROTECTED_CODE
USAGE: BUFFER_DEF_AS(name, config_opl, type[, oplist of type]) */
#define M_BUFFER_DEF_AS(a, b, ...) M_DEF_AS(M_BUFF3R_DEF_AS, a, b, __VA_ARGS__)
#define M_BUFF3R_DEF_AS__3(n, c, t) M_BUFF3R_DEF_P1(M_BUFF3R_CONFIG(n, c, t, M_TYPE2OPL(t)))
#define M_BUFF3R_DEF_AS__4(n, c, t, o) M_BUFF3R_DEF_P1(M_BUFF3R_CONFIG(n, c, t, o))
#define M_BUFF3R_DEF_AS__5(n, c, t, o)
#define M_BUFF3R_DEF_AS__6(n, c, t, o)
#define M_BUFF3R_DEF_AS__7(n, c, t, o)
#define M_BUFF3R_DEF_AS__8(n, c, t, o)
#define M_BUFF3R_DEF_AS__9(n, c, t, o)
// Extract the optional customization attributes from name_opl
#define M_BUFF3R_CONFIG(n, c, t, o) \
(n, t, M_SIZE_AS(c, 0), M_POLICY_AS(c, M_BUFFER_QUEUE), o, M_TYPE_AS(c, M_C(n, _t)))
// Old call: (name, name_t, type, syze, policy [, oplist] )
#define BUFFER_DEF_AS(a, b, c, d, ...) M_DEF_AS(M_BUFF3R_D3F_AS, a, b, c ,d, __VA_ARGS__)
#define M_BUFF3R_D3F_AS__5(n, n_t, t, s, p) M_BUFF3R_DEF_P1((n, t, s, p, M_TYPE2OPL(type), n_t))
#define M_BUFF3R_D3F_AS__6(n, n_t, t, s, p, o) M_BUFF3R_DEF_P1((n, t, s, p, o, n_t))
#define M_BUFF3R_D3F_AS__7(n, n_t, t, s, p, o)
#define M_BUFF3R_D3F_AS__8(n, n_t, t, s, p, o)
#define M_BUFF3R_D3F_AS__9(n, n_t, t, s, p, o)
/* Define the oplist of a lock based buffer given its name and its oplist.
@@ -85,7 +115,7 @@ typedef enum {
USAGE: QUEUE_MPMC_DEF(name, type, policy, [oplist of type])
*/
#define M_QUEUE_MPMC_DEF(name, type, ...) \
M_QUEUE_MPMC_DEF_AS(name, M_C(name,_t), type, __VA_ARGS__)
M_QUEUE_MPMC_DEF_AS(name, M_F(name,_t), type, __VA_ARGS__)
/* Define a nearly lock-free queue for Many Producer Many Consummer
@@ -110,7 +140,7 @@ typedef enum {
USAGE: QUEUE_SPSC_DEF(name, type, policy, [oplist of type])
*/
#define M_QUEUE_SPSC_DEF(name, type, ...) \
M_QUEUE_SPSC_DEF_AS(name, M_C(name, _t), type, __VA_ARGS__)
M_QUEUE_SPSC_DEF_AS(name, M_F(name, _t), type, __VA_ARGS__)
/* Define a wait-free queue for Single Producer Single Consummer
@@ -152,7 +182,7 @@ typedef union m_buff3r_number_s {
#endif
} m_buff3r_number_ct[1];
static inline void
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))
@@ -161,7 +191,7 @@ m_buff3r_number_init(m_buff3r_number_ct n, unsigned int policy)
n->u = 0UL;
}
static inline unsigned int
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))
@@ -172,7 +202,7 @@ m_buff3r_number_load(m_buff3r_number_ct n, unsigned int policy)
return n->u;
}
static inline void
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))
@@ -182,7 +212,7 @@ m_buff3r_number_store(m_buff3r_number_ct n, unsigned int v, unsigned int policy)
n->u = v;
}
static inline void
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))
@@ -192,7 +222,7 @@ m_buff3r_number_set(m_buff3r_number_ct n, m_buff3r_number_ct v, unsigned int pol
n->u = v->u;
}
static inline unsigned int
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))
@@ -201,7 +231,7 @@ m_buff3r_number_inc(m_buff3r_number_ct n, unsigned int policy)
return n->u ++;
}
static inline unsigned int
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))
@@ -210,7 +240,7 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
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))
@@ -218,7 +248,7 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
/* Return the size (run time or build time).
NOTE: It assumed that the buffer variable name is 'v' */
#define M_BUFF3R_SIZE(m_size) \
M_BUFF3R_IF_CTE_SIZE(m_size) (m_size, v->size)
M_BUFF3R_IF_CTE_SIZE(m_size) (m_size, v->capacity)
/* Contract of a buffer.
Nothing particular since we cannot test much without locking it.
@@ -233,8 +263,11 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
M_ASSERT (m_buff3r_number_load(buffer->number[0], policy) <= M_BUFF3R_SIZE(size)); \
} while (0)
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
so that all arguments are evaluated before further expansion
USAGE: M_BUFF3R_DEF_P1 ( (name, type, m_size, policy, oplist, buffer_t) )
*/
#define M_BUFF3R_DEF_P1(arg) M_ID( M_BUFF3R_DEF_P2 arg )
/* Validate the value oplist before going further */
@@ -254,22 +287,29 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
- buffer_t: name of the buffer
*/
#define M_BUFF3R_DEF_P3(name, type, m_size, policy, oplist, buffer_t) \
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)
/* Define the type of a buffer */
#define M_BUFF3R_DEF_TYPE(name, type, m_size, policy, oplist, buffer_t) \
\
/* Put each data in a separate cache line to avoid false sharing \
by multiple writing threads. No need to align if there is no thread */ \
typedef union M_C(name, _el_s) { \
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]; \
} M_C(name, _el_ct); \
} M_F(name, _el_ct); \
\
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
/* Data for a producer */ \
m_mutex_t mutexPush; /* MUTEX used for pushing elements */ \
size_t idx_prod; /* Index of the production threads */ \
size_t overwrite; /* Number of overwritten values */ \
m_cond_t there_is_data; /* condition raised when there is data */ \
/* Read only Data */ \
M_BUFF3R_IF_CTE_SIZE(m_size)( ,size_t size;) /* Size of the buffer */ \
M_BUFF3R_IF_CTE_SIZE(m_size)( ,size_t capacity;) /* Capacity of the buffer */ \
/* Data for a consummer */ \
m_cond_t there_is_room_for_data; /* Cond. raised when there is room */ \
m_mutex_t mutexPop; /* MUTEX used for popping elements */ \
@@ -278,24 +318,25 @@ m_buff3r_number_dec(m_buff3r_number_ct n, unsigned int policy)
/* 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)]; \
/* If fixed size, array of elements, otherwise pointer to element */ \
M_C(name, _el_ct) M_BUFF3R_IF_CTE_SIZE(m_size)(data[m_size], *data); \
M_F(name, _el_ct) M_BUFF3R_IF_CTE_SIZE(m_size)(data[m_size], *data); \
} buffer_t[1]; \
\
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
/* Internal type used to unconst the buffer */ \
typedef union { M_C(name, _srcptr) cptr; M_C(name, _ptr) ptr; } M_C(name, _uptr_ct); \
typedef union { M_F(name, _srcptr) cptr; M_F(name, _ptr) ptr; } M_F(name, _uptr_ct); \
/* Internal types used by the oplist */ \
typedef type M_C(name, _subtype_ct); \
typedef buffer_t M_C(name, _ct); \
typedef type M_F(name, _subtype_ct); \
typedef buffer_t M_F(name, _ct); \
/* Define the core functionnalities of a buffer */
#define M_BUFF3R_DEF_CORE(name, type, m_size, policy, oplist, buffer_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(buffer_t v, size_t size) \
M_INLINE void \
M_F(name, _init)(buffer_t v, size_t size) \
{ \
M_BUFF3R_IF_CTE_SIZE(m_size)(M_ASSERT(size == m_size), v->size = size); \
M_ASSERT(size <= UINT_MAX); \
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); \
if (M_BUFF3R_POLICY_P(policy, M_BUFFER_DEFERRED_POP)) \
@@ -310,9 +351,9 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
\
M_BUFF3R_IF_CTE_SIZE(m_size)( /* Statically allocated */ , \
v->data = M_CALL_REALLOC(oplist, M_C(name, _el_ct), NULL, M_BUFF3R_SIZE(m_size)); \
if (v->data == NULL) { \
M_MEMORY_FULL (M_BUFF3R_SIZE(m_size)*sizeof(M_C(name, _el_ct))); \
v->data = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, 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; \
} \
) \
@@ -325,14 +366,14 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
\
M_BUFF3R_IF_CTE_SIZE(m_size)( \
static inline void \
M_INLINE void \
M_C3(m_buff3r_,name,_init)(buffer_t v) \
{ \
M_C(name, _init)(v, m_size); \
M_F(name, _init)(v, m_size); \
} \
, ) \
\
static inline void \
M_INLINE void \
M_C3(m_buff3r_,name,_clear_obj)(buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
@@ -356,8 +397,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_BUFF3R_CONTRACT(v,m_size); \
} \
\
static inline void \
M_C(name, _clear)(buffer_t v) \
M_INLINE void \
M_F(name, _clear)(buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
M_C3(m_buff3r_,name,_clear_obj)(v); \
@@ -374,8 +415,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
} \
\
static inline void \
M_C(name, _reset)(buffer_t v) \
M_INLINE void \
M_F(name, _reset)(buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_THREAD_UNSAFE)) { \
@@ -397,22 +438,22 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_BUFF3R_CONTRACT(v,m_size); \
} \
\
static inline void M_ATTR_DEPRECATED \
M_C(name, _clean)(buffer_t v) \
M_INLINE void M_ATTR_DEPRECATED \
M_F(name, _clean)(buffer_t v) \
{ \
M_C(name,_reset)(v); \
M_F(name,_reset)(v); \
} \
\
static inline void \
M_C(name, _init_set)(buffer_t dest, const buffer_t src) \
M_INLINE void \
M_F(name, _init_set)(buffer_t dest, const buffer_t src) \
{ \
/* unconst 'src', so that we can lock it (semantically it is const) */ \
M_C(name, _uptr_ct) vu; \
M_F(name, _uptr_ct) vu; \
vu.cptr = src; \
M_C(name, _ptr) v = vu.ptr; \
M_F(name, _ptr) v = vu.ptr; \
M_ASSERT (dest != v); \
M_BUFF3R_CONTRACT(v,m_size); \
M_C(name, _init)(dest, M_BUFF3R_SIZE(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); \
@@ -447,13 +488,13 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_BUFF3R_CONTRACT(dest, m_size); \
} \
\
static inline void \
M_C(name, _set)(buffer_t dest, const buffer_t src) \
M_INLINE void \
M_F(name, _set)(buffer_t dest, const buffer_t src) \
{ \
/* unconst 'src', so that we can lock it (semantically it is const) */ \
M_C(name, _uptr_ct) vu; \
M_F(name, _uptr_ct) vu; \
vu.cptr = src; \
M_C(name, _ptr) v = vu.ptr; \
M_F(name, _ptr) v = vu.ptr; \
M_BUFF3R_CONTRACT(dest,m_size); \
M_BUFF3R_CONTRACT(v,m_size); \
\
@@ -517,8 +558,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_BUFF3R_CONTRACT(dest, m_size); \
} \
\
static inline bool \
M_C(name, _empty_p)(buffer_t v) \
M_INLINE bool \
M_F(name, _empty_p)(buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
/* If the buffer has been configured with deferred pop \
@@ -531,23 +572,23 @@ M_C(name, _init)(buffer_t v, size_t size) \
return m_buff3r_number_load (v->number[0], policy) == 0; \
} \
\
static inline bool \
M_C(name, _full_p)(buffer_t v) \
M_INLINE bool \
M_F(name, _full_p)(buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
return m_buff3r_number_load (v->number[0], policy) \
== M_BUFF3R_SIZE(m_size); \
} \
\
static inline size_t \
M_C(name, _size)(buffer_t v) \
M_INLINE size_t \
M_F(name, _size)(buffer_t v) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
return m_buff3r_number_load (v->number[0], policy); \
} \
\
static inline bool \
M_C(name, _push_blocking)(buffer_t v, type const data, bool blocking) \
M_INLINE bool \
M_F(name, _push_blocking)(buffer_t v, type const data, bool blocking) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
\
@@ -555,7 +596,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
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_C(name, _full_p)(v)) { \
&& M_F(name, _full_p)(v)) { \
if (!blocking) { \
m_mutex_unlock(v->mutexPush); \
return false; \
@@ -563,14 +604,14 @@ M_C(name, _init)(buffer_t v, size_t size) \
m_cond_wait(v->there_is_room_for_data, v->mutexPush); \
} \
} else if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \
&& M_C(name, _full_p)(v)) \
&& M_F(name, _full_p)(v)) \
return false; \
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
\
size_t previousSize, idx = v->idx_prod; \
/* INDEX computation if we have to overwrite the last element */ \
if (M_UNLIKELY (M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_OVERWRITE) \
&& M_C(name, _full_p)(v))) { \
&& M_F(name, _full_p)(v))) { \
v->overwrite++; \
/* Let's overwrite the last element */ \
/* Compute the index of the last push element */ \
@@ -626,8 +667,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline bool \
M_C(name, _pop_blocking)(type *data, buffer_t v, bool blocking) \
M_INLINE bool \
M_F(name, _pop_blocking)(type *data, buffer_t v, bool blocking) \
{ \
M_BUFF3R_CONTRACT(v,m_size); \
M_ASSERT (data != NULL); \
@@ -635,14 +676,14 @@ M_C(name, _init)(buffer_t v, size_t size) \
/* Consummer 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_C(name, _empty_p)(v)) { \
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_C(name, _empty_p)(v)) \
} else if (M_F(name, _empty_p)(v)) \
return false; \
M_BUFF3R_PROTECTED_CONTRACT(policy, v, m_size); \
\
@@ -697,35 +738,35 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
\
\
static inline bool \
M_C(name, _push)(buffer_t v, type const data) \
M_INLINE bool \
M_F(name, _push)(buffer_t v, type const data) \
{ \
return M_C(name, _push_blocking)(v, data, \
return M_F(name, _push_blocking)(v, data, \
!M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING_PUSH)); \
} \
\
static inline bool \
M_C(name, _pop)(type *data, buffer_t v) \
M_INLINE bool \
M_F(name, _pop)(type *data, buffer_t v) \
{ \
return M_C(name, _pop_blocking)(data, v, \
return M_F(name, _pop_blocking)(data, v, \
!M_BUFF3R_POLICY_P((policy), M_BUFFER_UNBLOCKING_POP)); \
} \
\
static inline size_t \
M_C(name, _overwrite)(const buffer_t v) \
M_INLINE size_t \
M_F(name, _overwrite)(const buffer_t v) \
{ \
return v->overwrite; \
} \
\
static inline size_t \
M_C(name, _capacity)(const buffer_t v) \
M_INLINE size_t \
M_F(name, _capacity)(const buffer_t v) \
{ \
(void) v; /* may be unused */ \
return M_BUFF3R_SIZE(m_size); \
} \
\
static inline void \
M_C(name, _pop_release)(buffer_t v) \
M_INLINE void \
M_F(name, _pop_release)(buffer_t v) \
{ \
/* Decrement the effective number of elements in the buffer */ \
if (M_BUFF3R_POLICY_P((policy), M_BUFFER_DEFERRED_POP)) { \
@@ -737,10 +778,9 @@ M_C(name, _init)(buffer_t v, size_t size) \
} \
} \
} \
\
M_EMPLACE_QUEUE_DEF(name, buffer_t, M_C(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
/********************************** INTERNAL *********************************/
/* Definition of a a QUEUE for Many Produccer / Many Consummer
for high bandwidth scenario:
@@ -787,6 +827,13 @@ M_C(name, _init)(buffer_t v, size_t size) \
- buffer_t: name of the buffer
*/
#define M_QU3UE_MPMC_DEF_P3(name, type, policy, oplist, buffer_t) \
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)
/* Define the type of a MPMC queue */
#define M_QU3UE_MPMC_DEF_TYPE(name, type, policy, oplist, buffer_t) \
\
/* The sequence number of an element will be equal to either \
- 2* the index of the production which creates it, \
@@ -796,28 +843,30 @@ M_C(name, _init)(buffer_t v, size_t size) \
Each element is put in a separate cache line to avoid false \
sharing by multiple writing threads. \
*/ \
typedef struct M_C(name, _el_s) { \
typedef struct M_F(name, _el_s) { \
atomic_uint seq; /* Can only increase until wrapping */ \
type x; \
M_CACHELINE_ALIGN(align, atomic_uint, type); \
} M_C(name, _el_ct); \
} M_F(name, _el_ct); \
\
/* If there is only one producer and one consummer, then they won't \
typically use the same cache line, increasing performance. */ \
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
atomic_uint ProdIdx; /* Can only increase until wrapping */ \
M_CACHELINE_ALIGN(align1, atomic_uint); \
atomic_uint ConsoIdx; /* Can only increase until wrapping */ \
M_CACHELINE_ALIGN(align2, atomic_uint); \
M_C(name, _el_ct) *Tab; \
M_F(name, _el_ct) *Tab; \
unsigned int size; \
} buffer_t[1]; \
\
typedef type M_C(name, _subtype_ct); \
typedef buffer_t M_C(name, _ct); \
\
static inline bool \
M_C(name, _push)(buffer_t table, type const x) \
typedef type M_F(name, _subtype_ct); \
typedef buffer_t M_F(name, _ct); \
/* Define the core functionnalities 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_QU3UE_MPMC_CONTRACT(table); \
unsigned int idx = atomic_load_explicit(&table->ProdIdx, \
@@ -850,8 +899,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline bool \
M_C(name, _pop)(type *ptr, buffer_t table) \
M_INLINE bool \
M_F(name, _pop)(type *ptr, buffer_t table) \
{ \
M_QU3UE_MPMC_CONTRACT(table); \
M_ASSERT (ptr != NULL); \
@@ -879,8 +928,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline void \
M_C(name, _init)(buffer_t buffer, size_t size) \
M_INLINE void \
M_F(name, _init)(buffer_t buffer, size_t size) \
{ \
M_ASSERT (buffer != NULL); \
M_ASSERT( M_POWEROF2_P(size)); \
@@ -889,9 +938,9 @@ M_C(name, _init)(buffer_t v, size_t size) \
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_C(name, _el_ct), NULL, size); \
if (buffer->Tab == NULL) { \
M_MEMORY_FULL (size*sizeof(M_C(name, _el_ct) )); \
buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, size); \
if (M_UNLIKELY_NOMEM (buffer->Tab == NULL)) { \
M_MEMORY_FULL (size*sizeof(M_F(name, _el_ct) )); \
return; \
} \
for(unsigned int j = 0; j < (unsigned int) size; j++) { \
@@ -903,8 +952,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_QU3UE_MPMC_CONTRACT(buffer); \
} \
\
static inline void \
M_C(name, _clear)(buffer_t buffer) \
M_INLINE void \
M_F(name, _clear)(buffer_t buffer) \
{ \
M_QU3UE_MPMC_CONTRACT(buffer); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
@@ -928,8 +977,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
buffer->size = 3; \
} \
\
static inline size_t \
M_C(name, _size)(buffer_t table) \
M_INLINE size_t \
M_F(name, _size)(buffer_t table) \
{ \
M_QU3UE_MPMC_CONTRACT(table); \
const unsigned int iC = atomic_load_explicit(&table->ConsoIdx, memory_order_relaxed); \
@@ -947,26 +996,27 @@ M_C(name, _init)(buffer_t v, size_t size) \
return iP-iC; \
} \
\
static inline size_t \
M_C(name, _capacity)(buffer_t v) \
M_INLINE size_t \
M_F(name, _capacity)(buffer_t v) \
{ \
M_QU3UE_MPMC_CONTRACT(v); \
return v->size; \
} \
\
static inline bool \
M_C(name, _empty_p)(buffer_t v) \
M_INLINE bool \
M_F(name, _empty_p)(buffer_t v) \
{ \
return M_C(name, _size) (v) == 0; \
return M_F(name, _size) (v) == 0; \
} \
\
static inline bool \
M_C(name, _full_p)(buffer_t v) \
M_INLINE bool \
M_F(name, _full_p)(buffer_t v) \
{ \
return M_C(name, _size)(v) >= v->size; \
return M_F(name, _size)(v) >= v->size; \
} \
/********************************** INTERNAL *********************************/
/* Definition of a a QUEUE for Single Producer / Single Consummer
for high bandwidth scenario:
@@ -1012,28 +1062,38 @@ M_C(name, _init)(buffer_t v, size_t size) \
- buffer_t: name of the buffer
*/
#define M_QU3UE_SPSC_DEF_P3(name, type, policy, oplist, buffer_t) \
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)
/* Define the type of a SPSC queue */
#define M_QU3UE_SPSC_DEF_TYPE(name, type, policy, oplist, buffer_t) \
\
/* Single producer / Single consummer \
So, only one thread will write in this table. The other thread \
will only read. As such, there is no concurrent write, and no \
need to align the structure for best performance. */ \
typedef struct M_C(name, _el_s) { \
typedef struct M_F(name, _el_s) { \
type x; \
} M_C(name, _el_ct); \
} M_F(name, _el_ct); \
\
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
atomic_uint consoIdx; /* Can only increase until overflow */ \
unsigned int size; \
M_C(name, _el_ct) *Tab; \
M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_C(name, _el_ct) *); \
M_F(name, _el_ct) *Tab; \
M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_F(name, _el_ct) *); \
atomic_uint prodIdx; /* Can only increase until overflow */ \
} buffer_t[1]; \
\
typedef type M_C(name, _subtype_ct); \
typedef buffer_t M_C(name, _ct); \
typedef type M_F(name, _subtype_ct); \
typedef buffer_t M_F(name, _ct); \
/* Define the core functionnalities of a SPSC queue */
#define M_QU3UE_SPSC_DEF_CORE(name, type, policy, oplist, buffer_t) \
\
static inline bool \
M_C(name, _push)(buffer_t table, type const x) \
M_INLINE bool \
M_F(name, _push)(buffer_t table, type const x) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -1053,8 +1113,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline bool \
M_C(name, _push_move)(buffer_t table, type *x) \
M_INLINE bool \
M_F(name, _push_move)(buffer_t table, type *x) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -1074,8 +1134,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline bool \
M_C(name, _pop)(type *ptr, buffer_t table) \
M_INLINE bool \
M_F(name, _pop)(type *ptr, buffer_t table) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_ASSERT (ptr != NULL); \
@@ -1096,8 +1156,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return true; \
} \
\
static inline unsigned \
M_C(name, _push_bulk)(buffer_t table, unsigned n, type const x[]) \
M_INLINE unsigned \
M_F(name, _push_bulk)(buffer_t table, unsigned n, type const x[]) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_ASSERT (x != NULL); \
@@ -1122,8 +1182,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return max; \
} \
\
static inline unsigned \
M_C(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \
M_INLINE unsigned \
M_F(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
M_ASSERT (ptr != NULL); \
@@ -1148,8 +1208,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
return max; \
} \
\
static inline void \
M_C(name, _push_force)(buffer_t table, type const x) \
M_INLINE void \
M_F(name, _push_force)(buffer_t table, type const x) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -1171,8 +1231,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_QU3UE_SPSC_CONTRACT(table); \
} \
\
static inline size_t \
M_C(name, _size)(buffer_t table) \
M_INLINE size_t \
M_F(name, _size)(buffer_t table) \
{ \
M_QU3UE_SPSC_CONTRACT(table); \
unsigned int r = atomic_load_explicit(&table->consoIdx, \
@@ -1192,26 +1252,26 @@ M_C(name, _init)(buffer_t v, size_t size) \
return w-r; \
} \
\
static inline size_t \
M_C(name, _capacity)(buffer_t v) \
M_INLINE size_t \
M_F(name, _capacity)(buffer_t v) \
{ \
return v->size; \
} \
\
static inline bool \
M_C(name, _empty_p)(buffer_t v) \
M_INLINE bool \
M_F(name, _empty_p)(buffer_t v) \
{ \
return M_C(name, _size) (v) == 0; \
return M_F(name, _size) (v) == 0; \
} \
\
static inline bool \
M_C(name, _full_p)(buffer_t v) \
M_INLINE bool \
M_F(name, _full_p)(buffer_t v) \
{ \
return M_C(name, _size)(v) >= v->size; \
return M_F(name, _size)(v) >= v->size; \
} \
\
static inline void \
M_C(name, _init)(buffer_t buffer, size_t size) \
M_INLINE void \
M_F(name, _init)(buffer_t buffer, size_t size) \
{ \
M_ASSERT (buffer != NULL); \
M_ASSERT( M_POWEROF2_P(size)); \
@@ -1220,9 +1280,9 @@ M_C(name, _init)(buffer_t v, size_t size) \
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_C(name, _el_ct), NULL, size); \
if (buffer->Tab == NULL) { \
M_MEMORY_FULL (size*sizeof(M_C(name, _el_ct) )); \
buffer->Tab = M_CALL_REALLOC(oplist, M_F(name, _el_ct), NULL, size); \
if (M_UNLIKELY_NOMEM (buffer->Tab == NULL)) { \
M_MEMORY_FULL (size*sizeof(M_F(name, _el_ct) )); \
return; \
} \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
@@ -1233,8 +1293,8 @@ M_C(name, _init)(buffer_t v, size_t size) \
M_QU3UE_SPSC_CONTRACT(buffer); \
} \
\
static inline void \
M_C(name, _clear)(buffer_t buffer) \
M_INLINE void \
M_F(name, _clear)(buffer_t buffer) \
{ \
M_QU3UE_SPSC_CONTRACT(buffer); \
if (!M_BUFF3R_POLICY_P((policy), M_BUFFER_PUSH_INIT_POP_MOVE)) { \
@@ -1257,7 +1317,9 @@ M_C(name, _init)(buffer_t v, size_t size) \
buffer->Tab = NULL; /* safer */ \
buffer->size = 3; \
} \
\
/********************************** INTERNAL *********************************/
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
@@ -1274,23 +1336,26 @@ M_C(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_SET(M_C(name, _init_set)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,INIT_SET(M_F(name, _init_set)) \
,SET(M_F(name, _set)) \
,CLEAR(M_F(name, _clear)) \
,NAME(name) \
,TYPE(M_C(name,_ct)) \
,SUBTYPE(M_C(name, _subtype_ct)) \
,RESET(M_C(name,_reset)) \
,PUSH(M_C(name,_push)) \
,POP(M_C(name,_pop)) \
,TYPE(M_F(name,_ct)) \
,SUBTYPE(M_F(name, _subtype_ct)) \
,RESET(M_F(name,_reset)) \
,PUSH(M_F(name,_push)) \
,POP(M_F(name,_pop)) \
,OPLIST(oplist) \
,EMPTY_P(M_C(name, _empty_p)), \
,GET_SIZE(M_C(name, _size)) \
,EMPTY_P(M_F(name, _empty_p)), \
,GET_SIZE(M_F(name, _size)) \
)
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define BUFFER_DEF M_BUFFER_DEF
#define BUFFER_DEF_AS M_BUFFER_DEF_AS
//#define BUFFER_DEF_AS M_BUFFER_DEF_AS
#define BUFFER_OPLIST M_BUFFER_OPLIST
#define QUEUE_MPMC_DEF M_QUEUE_MPMC_DEF
#define QUEUE_MPMC_DEF_AS M_QUEUE_MPMC_DEF_AS
+156 -156
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Concurrent memory pool allocator
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -46,55 +46,55 @@ M_BEGIN_PROTECTED_CODE
/* Classic internal Singly List without allocation */
#define M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
\
typedef struct M_C(name, _slist_node_s) { \
struct M_C(name, _slist_node_s) *next; \
typedef struct M_F(name, _slist_node_s) { \
struct M_F(name, _slist_node_s) *next; \
type_t data; \
} M_C(name, _slist_node_ct); \
} M_F(name, _slist_node_ct); \
\
typedef struct M_C(name, _slist_node_s) *M_C(name, _slist_ct)[1]; \
typedef struct M_F(name, _slist_node_s) *M_F(name, _slist_ct)[1]; \
\
static inline void \
M_C(name, _slist_init)(M_C(name, _slist_ct) list) \
M_INLINE void \
M_F(name, _slist_init)(M_F(name, _slist_ct) list) \
{ \
*list = NULL; \
} \
\
static inline void \
M_C(name, _slist_push)(M_C(name, _slist_ct) list, \
M_C(name, _slist_node_ct) *node) \
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; \
} \
\
static inline M_C(name, _slist_node_ct) * \
M_C(name, _slist_pop)(M_C(name, _slist_ct) list) \
M_INLINE M_F(name, _slist_node_ct) * \
M_F(name, _slist_pop)(M_F(name, _slist_ct) list) \
{ \
M_ASSERT (*list != NULL); \
M_C(name, _slist_node_ct) *node = *list; \
M_F(name, _slist_node_ct) *node = *list; \
*list = node->next; \
M_IF_DEBUG(node->next = NULL;) \
return node; \
} \
\
static inline bool \
M_C(name, _slist_empty_p)(M_C(name, _slist_ct) list) \
M_INLINE bool \
M_F(name, _slist_empty_p)(M_F(name, _slist_ct) list) \
{ \
return *list == NULL; \
} \
\
static inline void \
M_C(name, _slist_move)(M_C(name, _slist_ct) list, \
M_C(name, _slist_ct) src) \
M_INLINE void \
M_F(name, _slist_move)(M_F(name, _slist_ct) list, \
M_F(name, _slist_ct) src) \
{ \
*list = *src; \
*src = NULL; \
} \
\
static inline void \
M_C(name, _slist_clear)(M_C(name, _slist_ct) list) \
M_INLINE void \
M_F(name, _slist_clear)(M_F(name, _slist_ct) list) \
{ \
M_C(name, _slist_node_ct) *it = *list, *next; \
M_F(name, _slist_node_ct) *it = *list, *next; \
while (it) { \
next = it->next; \
M_MEMORY_DEL(it); \
@@ -125,41 +125,41 @@ M_BEGIN_PROTECTED_CODE
to reduce memory consumption too (non compatible with C++ ...) */
#define M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
\
typedef struct M_C(name, _lf_node_s) { \
M_ATTR_EXTENSION _Atomic(struct M_C(name, _lf_node_s) *) next; \
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_C(name, _slist_ct) list; \
} M_C(name, _lf_node_t); \
M_F(name, _slist_ct) list; \
} M_F(name, _lf_node_t); \
\
typedef struct M_C(name, _lflist_s) { \
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) head; \
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_C(name, _lf_node_t) *) tail; \
M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) tail; \
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
M_C(name, _lf_node_t) nil; \
} M_C(name, _lflist_ct)[1]; \
M_F(name, _lf_node_t) nil; \
} M_F(name, _lflist_ct)[1]; \
\
static inline void \
M_C(name, _lflist_init)(M_C(name, _lflist_ct) list, \
M_C(name, _lf_node_t) *node) \
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); \
} \
\
static inline bool \
M_C(name, _lflist_empty_p)(M_C(name, _lflist_ct) list) \
M_INLINE bool \
M_F(name, _lflist_empty_p)(M_F(name, _lflist_ct) list) \
{ \
return atomic_load(&list->tail) == atomic_load(&list->head); \
} \
\
static inline void \
M_C(name, _lflist_push)(M_C(name, _lflist_ct) list, \
M_C(name, _lf_node_t) *node, m_core_backoff_ct bkoff) \
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_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
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); \
@@ -190,12 +190,12 @@ M_BEGIN_PROTECTED_CODE
memory_order_relaxed); \
} \
\
static inline M_C(name, _lf_node_t) * \
M_C(name, _lflist_pop)(M_C(name, _lflist_ct) list, m_core_backoff_ct bkoff) \
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_C(name, _lf_node_t) *head; \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
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); \
@@ -236,18 +236,18 @@ M_BEGIN_PROTECTED_CODE
provided that the node we return is older than the one we should return, \
Therefore, we return the previous dummy head. \
As such, it is not the original MSqueue algorithm. */ \
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \
return head; \
} \
\
/* Dequeue a node if the node is old enough */ \
static inline M_C(name, _lf_node_t) * \
M_C(name, _lflist_pop_if)(M_C(name, _lflist_ct) list, \
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_C(name, _lf_node_t) *head; \
M_C(name, _lf_node_t) *tail; \
M_C(name, _lf_node_t) *next; \
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) { \
@@ -277,28 +277,28 @@ M_BEGIN_PROTECTED_CODE
} \
} \
} \
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \
return head; \
} \
\
static inline void \
M_C(name, _lflist_clear)(M_C(name, _lflist_ct) list) \
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_C(name, _lf_node_t) *node = M_C(name, _lflist_pop)(list, bkoff); \
M_F(name, _lf_node_t) *node = M_F(name, _lflist_pop)(list, bkoff); \
if (node == NULL) break; \
M_C(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
M_F(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
memory_order_relaxed); \
M_C(name, _slist_clear)(node->list); \
M_F(name, _slist_clear)(node->list); \
M_MEMORY_DEL(node); \
node = next; \
} \
/* Dummy node to free too */ \
M_C(name, _lf_node_t) *dummy; \
M_F(name, _lf_node_t) *dummy; \
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
M_C(name, _slist_clear)(dummy->list); \
M_F(name, _slist_clear)(dummy->list); \
M_MEMORY_DEL(dummy); \
} \
@@ -306,26 +306,26 @@ M_BEGIN_PROTECTED_CODE
As such it is a non Lock-Free path. */
#define M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
\
static inline M_C(name, _lf_node_t) * \
M_C(name, _alloc_node)(unsigned int initial) \
M_INLINE M_F(name, _lf_node_t) * \
M_F(name, _alloc_node)(unsigned int initial) \
{ \
M_C(name, _lf_node_t) * node; \
node = M_MEMORY_ALLOC(M_C(name, _lf_node_t)); \
if (M_UNLIKELY(node == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
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_C(name, _lf_node_t) *) 0); \
atomic_init(&node->next, (M_F(name, _lf_node_t) *) 0); \
atomic_init(&node->cpt, 0UL); \
M_C(name, _slist_init)(node->list); \
M_F(name, _slist_init)(node->list); \
for(unsigned i = 0; i < initial; i++) { \
M_C(name, _slist_node_ct) *n; \
n = M_MEMORY_ALLOC(M_C(name, _slist_node_ct)); \
if (M_UNLIKELY(n == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
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_C(name, _slist_push)(node->list, n); \
M_F(name, _slist_push)(node->list, n); \
} \
return node; \
} \
@@ -388,99 +388,99 @@ M_BEGIN_PROTECTED_CODE
*/
#define M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
\
typedef struct M_C(name, _lfmp_thread_s) { \
M_C(name, _slist_ct) free; \
M_C(name, _slist_ct) to_be_reclaimed; \
M_CACHELINE_ALIGN(align1, M_C(name, _slist_ct), M_C(name, _slist_ct)); \
} M_C(name, _lfmp_thread_ct); \
typedef struct M_F(name, _lfmp_thread_s) { \
M_F(name, _slist_ct) free; \
M_F(name, _slist_ct) to_be_reclaimed; \
M_CACHELINE_ALIGN(align1, M_F(name, _slist_ct), M_F(name, _slist_ct)); \
} M_F(name, _lfmp_thread_ct); \
\
static inline void \
M_C(name, _lfmp_thread_init)(M_C(name, _lfmp_thread_ct) *t) \
M_INLINE void \
M_F(name, _lfmp_thread_init)(M_F(name, _lfmp_thread_ct) *t) \
{ \
M_C(name, _slist_init)(t->free); \
M_C(name, _slist_init)(t->to_be_reclaimed); \
M_F(name, _slist_init)(t->free); \
M_F(name, _slist_init)(t->to_be_reclaimed); \
} \
\
static inline void \
M_C(name, _lfmp_thread_clear)(M_C(name, _lfmp_thread_ct) *t) \
M_INLINE void \
M_F(name, _lfmp_thread_clear)(M_F(name, _lfmp_thread_ct) *t) \
{ \
M_ASSERT(M_C(name, _slist_empty_p)(t->to_be_reclaimed)); \
M_C(name, _slist_clear)(t->free); \
M_C(name, _slist_clear)(t->to_be_reclaimed); \
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_C(name, _s) { \
typedef struct M_F(name, _s) { \
unsigned initial; \
M_C(name, _lfmp_thread_ct) *thread_data; \
M_C(name, _lflist_ct) free; \
M_C(name, _lflist_ct) to_be_reclaimed; \
M_C(name, _lflist_ct) empty; \
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_C(name, _t)[1]; \
} M_F(name, _t)[1]; \
\
/* Garbage collect of the nodes of the mempool on sleep */ \
static inline void \
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_C(name, _s) *mempool = \
M_TYPE_FROM_FIELD(struct M_C(name, _s), data, m_cmemp00l_list_ct, mempool_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_C(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
M_C(name, _lf_node_t) *node; \
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_C(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \
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_C(name, _alloc_node)(0); \
node = M_F(name, _alloc_node)(0); \
M_ASSERT(node != NULL); \
} \
M_ASSERT(M_C(name, _slist_empty_p)(node->list)); \
M_C(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
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_C(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \
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_C(name, _lf_node_t) *node; \
node = M_C(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
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_C(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
M_F(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_C(name, _init)(M_C(name, _t) mem, m_gc_t gc_mem, \
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_C(name, _lfmp_thread_ct), NULL, max_thread); \
if (mem->thread_data == NULL) { \
M_MEMORY_FULL(max_thread * sizeof(M_C(name, _lfmp_thread_ct))); \
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_C(name, _lfmp_thread_init)(&mem->thread_data[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_C(name, _lflist_init)(mem->free, M_C(name, _alloc_node)(init_node_count)); \
M_C(name, _lflist_init)(mem->to_be_reclaimed, M_C(name, _alloc_node)(init_node_count)); \
M_C(name, _lflist_init)(mem->empty, M_C(name, _alloc_node)(0)); \
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_C(name, _lflist_push)(mem->free, M_C(name, _alloc_node)(init_node_count), \
M_F(name, _lflist_push)(mem->free, M_F(name, _alloc_node)(init_node_count), \
gc_mem->thread_data[0].bkoff); \
M_C(name, _lflist_push)(mem->empty, M_C(name, _alloc_node)(0), \
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 */ \
@@ -490,56 +490,56 @@ M_BEGIN_PROTECTED_CODE
mem->gc_mem = gc_mem; \
} \
\
static inline void \
M_C(name, _clear)(M_C(name, _t) 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_C(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
M_F(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
} \
M_MEMORY_FREE(mem->thread_data); \
mem->thread_data = NULL; \
M_C(name, _lflist_clear)(mem->empty); \
M_C(name, _lflist_clear)(mem->free); \
M_ASSERT(M_C(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
M_C(name, _lflist_clear)(mem->to_be_reclaimed); \
M_F(name, _lflist_clear)(mem->empty); \
M_F(name, _lflist_clear)(mem->free); \
M_ASSERT(M_F(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
M_F(name, _lflist_clear)(mem->to_be_reclaimed); \
/* TODO: Unregister from the GC? */ \
} \
\
static inline type_t * \
M_C(name, _new)(M_C(name, _t) mem, m_gc_tid_t id) \
M_INLINE type_t * \
M_F(name, _new)(M_F(name, _t) mem, m_gc_tid_t id) \
{ \
M_C(name, _slist_node_ct) *snode; \
M_C(name, _lf_node_t) *node; \
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_C(name, _slist_empty_p)(mem->thread_data[id].free))) { \
snode = M_C(name, _slist_pop)(mem->thread_data[id].free); \
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_C(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \
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_C(name, _alloc_node)(mem->initial); \
node = M_F(name, _alloc_node)(mem->initial); \
M_ASSERT(node != NULL); \
M_ASSERT(!M_C(name, _slist_empty_p)(node->list)); \
M_ASSERT(!M_F(name, _slist_empty_p)(node->list)); \
} \
M_C(name, _slist_move)(mem->thread_data[id].free, node->list); \
M_F(name, _slist_move)(mem->thread_data[id].free, node->list); \
/* Push back the empty group */ \
M_ASSERT (M_C(name, _slist_empty_p)(node->list)); \
M_C(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
M_ASSERT (M_F(name, _slist_empty_p)(node->list)); \
M_F(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
} \
} \
\
static inline void \
M_C(name, _del)(M_C(name, _t) mem, type_t *d, m_gc_tid_t id) \
M_INLINE void \
M_F(name, _del)(M_F(name, _t) mem, type_t *d, m_gc_tid_t id) \
{ \
M_C(name, _slist_node_ct) *snode; \
M_F(name, _slist_node_ct) *snode; \
M_ASSERT( d != NULL); \
snode = M_TYPE_FROM_FIELD(M_C(name, _slist_node_ct), d, type_t, data); \
M_C(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
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); \
} \
@@ -579,7 +579,7 @@ typedef struct m_gc_s {
m_cmemp00l_list_ct *mempool_list;
} m_gc_t[1];
static inline void
M_INLINE void
m_gc_init(m_gc_t gc_mem, size_t max_thread)
{
M_ASSERT(gc_mem != NULL);
@@ -588,7 +588,7 @@ m_gc_init(m_gc_t gc_mem, size_t max_thread)
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 (gc_mem->thread_data == NULL) {
if (M_UNLIKELY_NOMEM (gc_mem->thread_data == NULL)) {
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_ct));
return;
}
@@ -600,7 +600,7 @@ m_gc_init(m_gc_t gc_mem, size_t max_thread)
gc_mem->mempool_list = NULL;
}
static inline void
M_INLINE void
m_gc_clear(m_gc_t gc_mem)
{
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
@@ -613,7 +613,7 @@ m_gc_clear(m_gc_t gc_mem)
m_genint_clear(gc_mem->thread_alloc);
}
static inline m_gc_tid_t
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);
@@ -622,7 +622,7 @@ m_gc_attach_thread(m_gc_t gc_mem)
return M_ASSIGN_CAST(m_gc_tid_t, id);
}
static inline void
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);
@@ -632,7 +632,7 @@ m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id)
m_genint_push(gc_mem->thread_alloc, id);
}
static inline void
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);
@@ -643,7 +643,7 @@ m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
atomic_store(&gc_mem->thread_data[id].ticket, t);
}
static inline m_gc_ticket_ct
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);
@@ -654,7 +654,7 @@ m_cmemp00l_gc_min_ticket(m_gc_t gc_mem)
return min;
}
static inline void
M_INLINE void
m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id)
{
/* Increase life time of the thread */
@@ -690,13 +690,13 @@ typedef struct m_vlapool_lfmp_thread_s {
M_CACHELINE_ALIGN(align1, m_vlapool_slist_ct);
} m_vlapool_lfmp_thread_ct;
static inline void
M_INLINE void
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_ct *t)
{
m_vlapool_slist_init(t->to_be_reclaimed);
}
static inline void
M_INLINE void
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_ct *t)
{
M_ASSERT(m_vlapool_slist_empty_p(t->to_be_reclaimed));
@@ -712,7 +712,7 @@ typedef struct m_vlapool_s {
} m_vlapool_t[1];
/* Garbage collect of the nodes of the vla mempool on sleep */
static inline void
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)
{
@@ -751,14 +751,14 @@ m_cmemp00l_vlapool_on_sleep(m_gc_t gc_mem, m_cmemp00l_list_ct *data,
}
}
static inline void
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 (mem->thread_data == NULL) {
if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) {
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_ct));
return;
}
@@ -777,7 +777,7 @@ m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem)
mem->gc_mem = gc_mem;
}
static inline void
M_INLINE void
m_vlapool_clear(m_vlapool_t mem)
{
const unsigned max_thread = mem->gc_mem->max_thread;
@@ -792,7 +792,7 @@ m_vlapool_clear(m_vlapool_t mem)
/* TODO: Unregister from the GC? */
}
static inline void *
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);
@@ -811,7 +811,7 @@ m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size)
return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data));
}
static inline void
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);
+260 -243
View File
File diff suppressed because it is too large. Load diff
+426 -125
View File
File diff suppressed because it is too large. Load diff
+288 -279
View File
File diff suppressed because it is too large. Load diff
+469 -468
View File
File diff suppressed because it is too large. Load diff
+72 -65
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Function Object module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -33,7 +33,7 @@
* FUNC_OBJ_ITF_DEF(name, retcode type, type of param1, type of param 2, ...)
*/
#define M_FUNC_OBJ_ITF_DEF(name, ...) \
M_FUNC_OBJ_ITF_DEF_AS(name, M_C(name,_t), __VA_ARGS__)
M_FUNC_OBJ_ITF_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define a function object interface of name 'name'
@@ -66,7 +66,7 @@
* FUNC_OBJ_INS_DEF(name, base_name, (param1, ...), { return param1 * self->member1 }, (member1, int), ...)
*/
#define M_FUNC_OBJ_INS_DEF(name, base_name, param_list, ...) \
M_FUNC_OBJ_INS_DEF_AS(name, M_C(name,_t), base_name, param_list, __VA_ARGS__)
M_FUNC_OBJ_INS_DEF_AS(name, M_F(name,_t), base_name, param_list, __VA_ARGS__)
/* Define a function object instance of name 'name' based on the interface 'base_name'
@@ -91,9 +91,9 @@
/********************************************************************************/
/********************************** INTERNAL ************************************/
/********************************************************************************/
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
/*****************************************************************************/
/* To be used by M_IF_FUNCOBJ macro defined in m-core.
NOTE: It is reversed (0 instead of 1) so that it can be used in M_IF reliabely.
@@ -111,9 +111,9 @@
/* Specialization of the OPLIST in function if there is at least one member or not */
#define M_FUNC0BJ_INS_NO_ATTR_OPLIST(name) ( \
NAME(name), \
TYPE(M_C(name, _ct)), \
CLEAR(M_C(name, _clear)), \
INIT(M_C(name,_init)) \
TYPE(M_F(name, _ct)), \
CLEAR(M_F(name, _clear)), \
INIT(M_F(name,_init)) \
)
/* Validate the oplist before going further */
@@ -127,40 +127,42 @@
/* Define the oplist of the instance */
#define M_FUNC0BJ_INS_ATTR_OPLIST_P3(name, ...) ( \
NAME(name), \
TYPE(M_C(name, _ct)), \
INIT_WITH(M_C(name, _init_with)), \
CLEAR(M_C(name, _clear)), \
M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_C(name,_init)),), \
TYPE(M_F(name, _ct)), \
INIT_WITH(M_F(name, _init_with)), \
CLEAR(M_F(name, _clear)), \
M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \
PROPERTIES(( LET_AS_INIT_WITH(1) )) \
)
/******************************** INTERNAL ***********************************/
/* Specialization of the definition a function object interface of name 'name'
* with a function like "retcode (void)" that doesn't have any input parameters.
* Define the following types to be used by instance:
* - M_C(name, _retcode_ct): internal type of the return code
* - M_C(name, _callback_ct): internal type of the callback.
* - M_C(name, _ct): synonym of main type used by oplist.
* - M_F(name, _retcode_ct): internal type of the return code
* - M_F(name, _callback_ct): internal type of the callback.
* - M_F(name, _ct): synonym of main type used by oplist.
*/
#define M_FUNC0BJ_ITF_NO_PARAM_DEF(name, interface_t, retcode) \
\
/* Forward declaration */ \
struct M_C(name, _s); \
struct M_F(name, _s); \
\
/* Internal type for instance */ \
typedef retcode M_C(name, _retcode_ct); \
typedef retcode M_F(name, _retcode_ct); \
/* No parameters to the callback */ \
typedef retcode(*M_C(name, _callback_ct))(struct M_C(name, _s) *); \
typedef retcode(*M_F(name, _callback_ct))(struct M_F(name, _s) *); \
\
typedef struct M_C(name, _s) { \
M_C(name, _callback_ct) callback; \
typedef struct M_F(name, _s) { \
M_F(name, _callback_ct) callback; \
} interface_t[1]; \
\
/* Internal type for oplist & instance */ \
typedef interface_t M_C(name, _ct); \
typedef interface_t M_F(name, _ct); \
\
static inline retcode \
M_C(name, _call)(interface_t funcobj) \
M_INLINE retcode \
M_F(name, _call)(interface_t funcobj) \
{ \
M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \
funcobj->callback(funcobj); \
@@ -171,32 +173,32 @@
* with a function like retcode, type of param1, type of param 2, ...
* with mandatory input parameters.
* Define the following types to be used by instance:
* - M_C(name, _retcode_ct): internal type of the return code
* - M_C(name, _callback_ct): internal type of the callback.
* - M_F(name, _retcode_ct): internal type of the return code
* - M_F(name, _callback_ct): internal type of the callback.
* - M_C4(name, _param_, num, _ct) for each parameter defined
* - M_C(name, _ct): synonym of main type used by oplist.
* - M_F(name, _ct): synonym of main type used by oplist.
*/
#define M_FUNC0BJ_ITF_PARAM_DEF(name, interface_t, retcode, ...) \
\
/* Forward declaration */ \
struct M_C(name, _s); \
struct M_F(name, _s); \
\
/* Internal types for instance */ \
typedef retcode M_C(name, _retcode_ct); \
typedef retcode M_F(name, _retcode_ct); \
/* Define types for all parameters */ \
M_MAP3(M_FUNC0BJ_BASE_TYPE, name, __VA_ARGS__) \
/* Define callback type with all parameters */ \
typedef retcode(*M_C(name, _callback_ct))(struct M_C(name, _s) *, __VA_ARGS__); \
typedef retcode(*M_F(name, _callback_ct))(struct M_F(name, _s) *, __VA_ARGS__); \
\
typedef struct M_C(name, _s) { \
M_C(name, _callback_ct) callback; \
typedef struct M_F(name, _s) { \
M_F(name, _callback_ct) callback; \
} interface_t[1]; \
\
/* Internal type for oplist & instance */ \
typedef interface_t M_C(name, _ct); \
typedef interface_t M_F(name, _ct); \
\
static inline retcode \
M_C(name, _call)(interface_t funcobj \
M_INLINE retcode \
M_F(name, _call)(interface_t funcobj \
M_MAP3(M_FUNC0BJ_BASE_ARGLIST, name, __VA_ARGS__) ) \
{ \
/* If the retcode is 'void', don't return the value of the callback */ \
@@ -205,52 +207,54 @@
}
/******************************** INTERNAL ***********************************/
/* Specialization of the definition a function object instance of name 'name'
* with no member attribute.
*/
#define M_FUNC0BJ_INS_NO_ATTR_DEF(name, instance_t, base_name, param_list, callback_core) \
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
M_C(base_name, _callback_ct) callback; \
} instance_t[1]; \
\
/* Internal type for oplist */ \
typedef instance_t M_C(name, _ct); \
typedef instance_t M_F(name, _ct); \
\
static inline M_C(base_name, _retcode_ct) \
M_C(name, _callback)(M_C(base_name, _ct) _self \
M_INLINE M_C(base_name, _retcode_ct) \
M_F(name, _callback)(M_C(base_name, _ct) _self \
M_IF_EMPTY(M_OPFLAT param_list)( \
/* No param */, \
M_MAP3(M_FUNC0BJ_INS_ARGLIST, base_name, M_OPFLAT param_list) \
) \
) \
{ \
struct M_C(name, _s) *self = (struct M_C(name, _s) *)_self; \
struct M_F(name, _s) *self = (struct M_F(name, _s) *)_self; \
(void) self; /* maybe unused */ \
callback_core; \
} \
\
static inline void \
M_C(name, _init_with)(instance_t obj) \
M_INLINE void \
M_F(name, _init_with)(instance_t obj) \
{ \
obj->callback = M_C(name, _callback); \
obj->callback = M_F(name, _callback); \
} \
\
static inline void \
M_C(name, _clear)(instance_t obj) \
M_INLINE void \
M_F(name, _clear)(instance_t obj) \
{ \
(void) obj; /* nothing to do */ \
} \
\
static inline struct M_C(base_name, _s) * \
M_C(name, _as_interface)(instance_t obj) \
M_INLINE struct M_C(base_name, _s) * \
M_F(name, _as_interface)(instance_t obj) \
{ \
return (struct M_C(base_name, _s) *) obj; \
} \
\
static inline void \
M_C(name, _init)(instance_t obj) \
M_INLINE void \
M_F(name, _init)(instance_t obj) \
{ \
obj->callback = M_C(name, _callback); \
obj->callback = M_F(name, _callback); \
} \
@@ -290,7 +294,7 @@
/* Expand the Function Object with members */
#define M_FUNC0BJ_INS_ATTR_DEF_P3(name, instance_t, base_name, param_list, callback_core, ...) \
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
/* Callback is the mandatory first argument */ \
M_C(base_name, _callback_ct) callback; \
/* All the member attribute of the Function Object */ \
@@ -298,12 +302,12 @@
} instance_t[1]; \
\
/* Internal type for oplist */ \
typedef instance_t M_C(name, _ct); \
typedef instance_t M_F(name, _ct); \
\
M_FUNC0BJ_CONTROL_ALL_OPLIST(name, __VA_ARGS__) \
\
static inline M_C(base_name, _retcode_ct) \
M_C(name, _callback)(M_C(base_name, _ct) _self \
M_INLINE M_C(base_name, _retcode_ct) \
M_F(name, _callback)(M_C(base_name, _ct) _self \
M_IF_EMPTY(M_OPFLAT param_list)( \
/* No param */, \
M_MAP3(M_FUNC0BJ_INS_ARGLIST, base_name, M_OPFLAT param_list) \
@@ -312,36 +316,36 @@
{ \
/* Let's go through an uintptr_t to avoid [broken] aliasing detection by compiler */ \
uintptr_t __self = (uintptr_t) _self; \
struct M_C(name, _s) *self = (struct M_C(name, _s) *)(void*)__self; \
struct M_F(name, _s) *self = (struct M_F(name, _s) *)(void*)__self; \
(void) self; /* maybe unused */ \
callback_core; \
} \
\
static inline void \
M_C(name, _init_with)(instance_t obj M_MAP(M_FUNC0BJ_INS_ATTR_LIST, __VA_ARGS__)) \
M_INLINE void \
M_F(name, _init_with)(instance_t obj M_MAP(M_FUNC0BJ_INS_ATTR_LIST, __VA_ARGS__)) \
{ \
obj->callback = M_C(name, _callback); \
obj->callback = M_F(name, _callback); \
M_MAP(M_FUNC0BJ_INS_ATTR_INIT_SET, __VA_ARGS__); \
} \
\
static inline void \
M_C(name, _clear)(instance_t obj) \
M_INLINE void \
M_F(name, _clear)(instance_t obj) \
{ \
M_MAP(M_FUNC0BJ_INS_ATTR_CLEAR, __VA_ARGS__); \
} \
\
static inline struct M_C(base_name, _s) * \
M_C(name, _as_interface)(instance_t obj) \
M_INLINE struct M_C(base_name, _s) * \
M_F(name, _as_interface)(instance_t obj) \
{ \
return (struct M_C(base_name, _s) *) obj; \
} \
\
M_IF(M_FUNC0BJ_TEST_METHOD_P(INIT, __VA_ARGS)) \
( \
static inline void \
M_C(name, _init)(instance_t obj) \
M_INLINE void \
M_F(name, _init)(instance_t obj) \
{ \
obj->callback = M_C(name, _callback); \
obj->callback = M_F(name, _callback); \
M_MAP(M_FUNC0BJ_INS_ATTR_INIT, __VA_ARGS__); \
} \
, /* END OF INIT METHOD */ ) \
@@ -397,6 +401,9 @@
#define M_FUNC0BJ_CONTROL_OPLIST(name, a) \
M_CHECK_COMPATIBLE_OPLIST(name, M_RET_ARG1 a, M_RET_ARG2 a, M_RET_ARG3 a)
/******************************** INTERNAL ***********************************/
#if M_USE_SMALL_NAME
#define FUNC_OBJ_ITF_DEF M_FUNC_OBJ_ITF_DEF
#define FUNC_OBJ_ITF_DEF_AS M_FUNC_OBJ_ITF_DEF_AS
+7 -7
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Integer Generator (GENINT) module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -103,14 +103,14 @@ typedef struct m_genint_s {
* meaning it can be used to index global unique resources shared
* for all threads.
*/
static inline void
M_INLINE 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);
if (M_UNLIKELY (ptr == NULL)) {
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(alloc);
return;
}
@@ -126,7 +126,7 @@ m_genint_init(m_genint_t s, unsigned int n)
}
/* Clear an integer generator (Destructor) */
static inline void
M_INLINE void
m_genint_clear(m_genint_t s)
{
M_GEN1NT_CONTRACT(s);
@@ -135,7 +135,7 @@ m_genint_clear(m_genint_t s)
}
/* Return the maximum integer that the generator will provide */
static inline size_t
M_INLINE size_t
m_genint_size(m_genint_t s)
{
M_GEN1NT_CONTRACT(s);
@@ -144,7 +144,7 @@ m_genint_size(m_genint_t s)
/* Get an unique integer from the integer generator.
* NOTE: For a typical case, the amortized cost is one CAS per pop. */
static inline unsigned int
M_INLINE unsigned int
m_genint_pop(m_genint_t s)
{
M_GEN1NT_CONTRACT(s);
@@ -204,7 +204,7 @@ m_genint_pop(m_genint_t s)
/* Restore a used integer in the integer generator.
* NOTE: For a typical case, the amortized cost is one CAS per pop */
static inline void
M_INLINE void
m_genint_push(m_genint_t s, unsigned int n)
{
M_GEN1NT_CONTRACT(s);
+149 -138
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Intrusive List module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -47,7 +47,7 @@
USAGE:
ILIST_DEF(name, type [, oplist_of_the_type]) */
#define M_ILIST_DEF(name, ...) \
M_ILIST_DEF_AS(name, M_C(name,_t), M_C(name,_it_t), __VA_ARGS__)
M_ILIST_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
/* Define a doubly-linked intrusive list of a given type
@@ -72,7 +72,9 @@
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
/*****************************************************************************/
/* Define the basic structure to be added in all objects. */
typedef struct m_il1st_head_s {
@@ -93,35 +95,36 @@ typedef struct m_il1st_head_s {
/* Define the oplist of an ilist of type */
#define M_IL1ST_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)), \
CLEAR(M_C(name, _clear)), \
INIT_MOVE(M_C(name, _init_move)), \
MOVE(M_C(name, _move)), \
(INIT(M_F(name, _init)), \
CLEAR(M_F(name, _clear)), \
INIT_MOVE(M_F(name, _init_move)), \
MOVE(M_F(name, _move)), \
NAME(name), \
TYPE(M_C(name,_ct)), \
RESET(M_C(name,_reset)), \
SUBTYPE(M_C(name,_subtype_ct)), \
EMPTY_P(M_C(name,_empty_p)), \
IT_TYPE(M_C(name,_it_ct)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name,_it_set)), \
IT_LAST(M_C(name,_it_last)), \
IT_END(M_C(name,_it_end)), \
IT_END_P(M_C(name,_end_p)), \
IT_LAST_P(M_C(name,_last_p)), \
IT_EQUAL_P(M_C(name,_it_equal_p)), \
IT_NEXT(M_C(name,_next)), \
IT_PREVIOUS(M_C(name,_previous)), \
IT_REF(M_C(name,_ref)), \
IT_CREF(M_C(name,_cref)), \
IT_REMOVE(M_C(name,_remove)), \
M_IF_METHOD(NEW, oplist)(IT_INSERT(M_C(name,_insert)),), \
TYPE(M_F(name,_ct)), \
RESET(M_F(name,_reset)), \
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_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_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_C(name,_splice_back)) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
SPLICE_BACK(M_F(name,_splice_back)) \
)
/******************************** INTERNAL ***********************************/
/* Contract respected by all intrusive lists */
#define M_IL1ST_CONTRACT(name, list) do { \
M_ASSERT(list != NULL); \
@@ -160,19 +163,27 @@ typedef struct m_il1st_head_s {
it_t: iterator of the intrusive list (name##_it_t)
*/
#define M_IL1ST_DEF_P3(name, type, oplist, list_t, it_t) \
M_IL1ST_DEF_TYPE(name, type, oplist, list_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_IL1ST_DEF_CORE(name, type, oplist, list_t, it_t) \
/* Used of internal macro from m-list */ \
M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t)
/* Define the type of an intrusive list */
#define M_IL1ST_DEF_TYPE(name, type, oplist, list_t, it_t) \
\
/* Define the list as a structure containing pointers \
* to the front & back nodes */ \
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
struct m_il1st_head_s name; \
} list_t[1]; \
\
/* Define internal types pointers to such a list */ \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
/* Define iterator of such a list */ \
typedef struct M_C(name, _it_s) { \
typedef struct M_F(name, _it_s) { \
struct m_il1st_head_s *head; \
struct m_il1st_head_s *previous; \
struct m_il1st_head_s *current; \
@@ -180,14 +191,15 @@ typedef struct m_il1st_head_s {
} it_t[1]; \
\
/* Define types used by oplist */ \
typedef type M_C(name, _subtype_ct); \
typedef list_t M_C(name, _ct); \
typedef it_t M_C(name, _it_ct); \
typedef type M_F(name, _subtype_ct); \
typedef list_t M_F(name, _ct); \
typedef it_t M_F(name, _it_ct); \
/* Define core functions for intrusive lists */
#define M_IL1ST_DEF_CORE(name, type, oplist, list_t, it_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(list_t list) \
M_INLINE void \
M_F(name, _init)(list_t list) \
{ \
M_ASSERT (list != NULL); \
list->name.next = &list->name; \
@@ -195,8 +207,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
static inline void \
M_C(name, _reset)(list_t list) \
M_INLINE void \
M_F(name, _reset)(list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
for(struct m_il1st_head_s *it = list->name.next, *next ; \
@@ -217,31 +229,31 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
static inline void \
M_C(name, _clear)(list_t list) \
M_INLINE void \
M_F(name, _clear)(list_t list) \
{ \
/* Nothing to do more than clean the list itself */ \
M_C(name, _reset)(list); \
M_F(name, _reset)(list); \
/* For safety purpose (create invalid represenation of object) */ \
list->name.next = NULL; \
list->name.prev = NULL; \
} \
\
static inline bool \
M_C(name, _empty_p)(const list_t list) \
M_INLINE bool \
M_F(name, _empty_p)(const list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
return list->name.next == &list->name; \
} \
\
\
static inline void \
M_C(name, _init_move)(list_t list, list_t ref) \
M_INLINE void \
M_F(name, _init_move)(list_t list, list_t ref) \
{ \
M_IL1ST_CONTRACT(name, ref); \
M_ASSERT (list != ref); \
M_C(name,_init)(list); \
if (!M_C(name,_empty_p)(ref)) { \
M_F(name,_init)(list); \
if (!M_F(name,_empty_p)(ref)) { \
list->name.next = ref->name.next; \
list->name.prev = ref->name.prev; \
list->name.next->prev = &list->name; \
@@ -252,15 +264,15 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
static inline void \
M_C(name, _move)(list_t list, list_t ref) \
M_INLINE void \
M_F(name, _move)(list_t list, list_t ref) \
{ \
M_C(name, _clear)(list); \
M_C(name, _init_move)(list, ref); \
M_F(name, _clear)(list); \
M_F(name, _init_move)(list, ref); \
} \
\
static inline size_t \
M_C(name, _size)(const list_t list) \
M_INLINE size_t \
M_F(name, _size)(const list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
size_t s = 0; \
@@ -273,8 +285,8 @@ typedef struct m_il1st_head_s {
return s; \
} \
\
static inline void \
M_C(name, _push_back)(list_t list, type *obj) \
M_INLINE void \
M_F(name, _push_back)(list_t list, type *obj) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (obj != NULL); \
@@ -286,8 +298,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
static inline void \
M_C(name, _push_front)(list_t list, type *obj) \
M_INLINE void \
M_F(name, _push_front)(list_t list, type *obj) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (obj != NULL); \
@@ -299,8 +311,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list); \
} \
\
static inline void \
M_C(name, _push_after)(type *obj_pos, type *obj) \
M_INLINE void \
M_F(name, _push_after)(type *obj_pos, type *obj) \
{ \
M_ASSERT (obj_pos != NULL && obj != NULL); \
/* We don't have the list, so we have no contract at list level */ \
@@ -312,8 +324,8 @@ typedef struct m_il1st_head_s {
next->prev = &obj->name; \
} \
\
static inline void \
M_C(name, _init_field)(type *obj) \
M_INLINE void \
M_F(name, _init_field)(type *obj) \
{ \
M_ASSERT (obj != NULL); \
/* Init the fields of the node. To be used in object constructor */ \
@@ -321,8 +333,8 @@ typedef struct m_il1st_head_s {
obj->name.prev = NULL; \
} \
\
static inline void \
M_C(name, _unlink)(type *obj) \
M_INLINE void \
M_F(name, _unlink)(type *obj) \
{ \
M_ASSERT (obj != NULL); \
/* We don't have the list, so we have no contract at list level */ \
@@ -336,26 +348,26 @@ typedef struct m_il1st_head_s {
obj->name.prev = NULL; \
} \
\
static inline type * \
M_C(name, _back)(const list_t list) \
M_INLINE type * \
M_F(name, _back)(const list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT(!M_C(name, _empty_p)(list)); \
M_ASSERT(!M_F(name, _empty_p)(list)); \
return M_TYPE_FROM_FIELD(type, list->name.prev, \
struct m_il1st_head_s, name); \
} \
\
static inline type * \
M_C(name, _front)(const list_t list) \
M_INLINE type * \
M_F(name, _front)(const list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT(!M_C(name, _empty_p)(list)); \
M_ASSERT(!M_F(name, _empty_p)(list)); \
return M_TYPE_FROM_FIELD(type, list->name.next, \
struct m_il1st_head_s, name); \
} \
\
static inline type * \
M_C(name, _next_obj)(const list_t list, type const *obj) \
M_INLINE type * \
M_F(name, _next_obj)(const list_t list, type const *obj) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (obj != NULL); \
@@ -365,8 +377,8 @@ typedef struct m_il1st_head_s {
struct m_il1st_head_s, name); \
} \
\
static inline type * \
M_C(name, _previous_obj)(const list_t list, type const *obj) \
M_INLINE type * \
M_F(name, _previous_obj)(const list_t list, type const *obj) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (obj != NULL); \
@@ -376,8 +388,8 @@ typedef struct m_il1st_head_s {
struct m_il1st_head_s, name); \
} \
\
static inline void \
M_C(name, _it)(it_t it, const list_t list) \
M_INLINE void \
M_F(name, _it)(it_t it, const list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (it != NULL); \
@@ -388,8 +400,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_NODE_CONTRACT(it->current); \
} \
\
static inline void \
M_C(name, _it_set)(it_t it, const it_t cit) \
M_INLINE void \
M_F(name, _it_set)(it_t it, const it_t cit) \
{ \
M_ASSERT (it != NULL && cit != NULL); \
it->head = cit->head; \
@@ -399,8 +411,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_NODE_CONTRACT(it->current); \
} \
\
static inline void \
M_C(name, _it_last)(it_t it, list_t const list) \
M_INLINE void \
M_F(name, _it_last)(it_t it, list_t const list) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (it != NULL); \
@@ -411,8 +423,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_NODE_CONTRACT(it->current); \
} \
\
static inline void \
M_C(name, _it_end)(it_t it, list_t const list) \
M_INLINE void \
M_F(name, _it_end)(it_t it, list_t const list) \
{ \
M_ASSERT (it != NULL && list != NULL); \
it->head = list->name.next->prev; \
@@ -422,24 +434,24 @@ typedef struct m_il1st_head_s {
M_IL1ST_NODE_CONTRACT(it->current); \
} \
\
static inline bool \
M_C(name, _end_p)(const it_t it) \
M_INLINE bool \
M_F(name, _end_p)(const it_t it) \
{ \
M_ASSERT (it != NULL); \
M_IL1ST_NODE_CONTRACT(it->current); \
return it->current == it->head; \
} \
\
static inline bool \
M_C(name, _last_p)(const it_t it) \
M_INLINE bool \
M_F(name, _last_p)(const it_t it) \
{ \
M_ASSERT (it != NULL); \
M_IL1ST_NODE_CONTRACT(it->current); \
return it->next == it->head || it->current == it->head; \
} \
\
static inline void \
M_C(name, _next)(it_t it) \
M_INLINE void \
M_F(name, _next)(it_t it) \
{ \
M_ASSERT (it != NULL); \
/* Cannot check node for it->current: it may have been deleted! */ \
@@ -453,8 +465,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_NODE_CONTRACT(it->current); \
} \
\
static inline void \
M_C(name, _previous)(it_t it) \
M_INLINE void \
M_F(name, _previous)(it_t it) \
{ \
M_ASSERT (it != NULL); \
/* Cannot check node for it->current: it may have been deleted! */ \
@@ -468,92 +480,92 @@ typedef struct m_il1st_head_s {
M_IL1ST_NODE_CONTRACT(it->current); \
} \
\
static inline bool \
M_C(name, _it_equal_p)(const it_t it1, const it_t it2 ) \
M_INLINE bool \
M_F(name, _it_equal_p)(const it_t it1, const it_t it2 ) \
{ \
M_ASSERT (it1 != NULL && it2 != NULL); \
/* No need to check for next & previous */ \
return it1->head == it2->head && it1->current == it2->current; \
} \
\
static inline type * \
M_C(name, _ref)(const it_t it) \
M_INLINE type * \
M_F(name, _ref)(const it_t it) \
{ \
M_ASSERT (it != NULL && it->current != NULL); \
M_IL1ST_NODE_CONTRACT(it->current); \
/* check if 'it' was not deleted */ \
M_ASSERT (it->current->next == it->next); \
M_ASSERT (it->current->prev == it->previous); \
M_ASSERT (!M_C(name, _end_p)(it)); \
M_ASSERT (!M_F(name, _end_p)(it)); \
return M_TYPE_FROM_FIELD(type, it->current, \
struct m_il1st_head_s, name); \
} \
\
static inline type const * \
M_C(name, _cref)(const it_t it) \
M_INLINE type const * \
M_F(name, _cref)(const it_t it) \
{ \
type *ptr = M_C(name, _ref)(it); \
type *ptr = M_F(name, _ref)(it); \
return M_CONST_CAST(type, ptr); \
} \
\
static inline void \
M_C(name, _remove)(list_t list, it_t it) \
M_INLINE void \
M_F(name, _remove)(list_t list, it_t it) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_IL1ST_NODE_CONTRACT(it->current); \
(void)list; /* list param is not used */ \
type *obj = M_TYPE_FROM_FIELD(type, it->current, \
struct m_il1st_head_s, name); \
M_C(name, _unlink)(obj); \
M_F(name, _unlink)(obj); \
M_CALL_CLEAR(oplist, obj); \
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
M_C(name, _next)(it); \
M_F(name, _next)(it); \
} \
\
M_IF_METHOD2(NEW, INIT_SET, oplist)( \
static inline void \
M_C(name, _insert)(list_t list, it_t it, type x) \
M_INLINE void \
M_F(name, _insert)(list_t list, it_t it, type x) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_IL1ST_NODE_CONTRACT(it->current); \
type *p = M_CALL_NEW(oplist, type); \
if (M_UNLIKELY (p == NULL)) { \
if (M_UNLIKELY_NOMEM (p == NULL)) { \
M_MEMORY_FULL (sizeof (type)); \
return ; \
} \
M_CALL_INIT_SET(oplist, *p, x); \
type *obj = M_C(name, _ref)(it); \
M_C(name, _push_after)(obj, p); \
type *obj = M_F(name, _ref)(it); \
M_F(name, _push_after)(obj, p); \
it->current = p; \
(void) list; \
M_IL1ST_CONTRACT(name, list); \
} \
, /* NEW & INIT_SET not defined */) \
\
static inline type * \
M_C(name, _pop_back)(list_t list) \
M_INLINE type * \
M_F(name, _pop_back)(list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (!M_C(name, _empty_p)(list)); \
type *obj = M_C(name, _back)(list); \
M_ASSERT (!M_F(name, _empty_p)(list)); \
type *obj = M_F(name, _back)(list); \
list->name.prev = list->name.prev->prev; \
list->name.prev->next = &list->name; \
return obj; \
} \
\
static inline type * \
M_C(name, _pop_front)(list_t list) \
M_INLINE type * \
M_F(name, _pop_front)(list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
M_ASSERT (!M_C(name, _empty_p)(list)); \
type *obj = M_C(name, _front)(list); \
M_ASSERT (!M_F(name, _empty_p)(list)); \
type *obj = M_F(name, _front)(list); \
list->name.next = list->name.next->next; \
list->name.next->prev = &list->name; \
return obj; \
} \
\
static inline void \
M_C(name, _splice)(list_t list1, list_t list2) \
M_INLINE void \
M_F(name, _splice)(list_t list1, list_t list2) \
{ \
M_IL1ST_CONTRACT(name, list1); \
M_IL1ST_CONTRACT(name, list2); \
@@ -569,36 +581,36 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, list2); \
} \
\
static inline void \
M_C(name, _splice_back)(list_t nv, list_t ov, it_t it) \
M_INLINE void \
M_F(name, _splice_back)(list_t nv, list_t ov, it_t it) \
{ \
M_IL1ST_CONTRACT(name, nv); \
M_IL1ST_CONTRACT(name, ov); \
M_IL1ST_NODE_CONTRACT(it->current); \
M_ASSERT (it != NULL); \
(void) ov; \
type *obj = M_C(name, _ref)(it); \
M_C(name, _unlink)(obj); \
M_C(name, _push_back)(nv, obj); \
M_C(name, _next)(it); \
type *obj = M_F(name, _ref)(it); \
M_F(name, _unlink)(obj); \
M_F(name, _push_back)(nv, obj); \
M_F(name, _next)(it); \
M_IL1ST_CONTRACT(name, nv); \
M_IL1ST_CONTRACT(name, ov); \
} \
\
static inline void \
M_C(name, _splice_at)(list_t nlist, it_t npos, \
M_INLINE void \
M_F(name, _splice_at)(list_t nlist, it_t npos, \
list_t olist, it_t opos) \
{ \
M_IL1ST_CONTRACT(name, nlist); \
M_IL1ST_CONTRACT(name, olist); \
M_ASSERT (npos != NULL && opos != NULL); \
M_ASSERT (!M_C(name, _end_p)(opos)); \
M_ASSERT (!M_F(name, _end_p)(opos)); \
/* npos may be end */ \
(void) olist, (void) nlist; \
type *obj = M_C(name, _ref)(opos); \
type *obj = M_F(name, _ref)(opos); \
struct m_il1st_head_s *ref = npos->current; \
/* Remove object */ \
M_C(name, _unlink)(obj); \
M_F(name, _unlink)(obj); \
/* Push 'obj' after 'ref' */ \
struct m_il1st_head_s *next = ref->next; \
ref->next = &obj->name; \
@@ -606,7 +618,7 @@ typedef struct m_il1st_head_s {
obj->name.prev = ref; \
next->prev = &obj->name; \
/* Move iterator in old list */ \
M_C(name, _next)(opos); \
M_F(name, _next)(opos); \
/* Set npos iterator to new position of object */ \
npos->previous = ref; \
npos->current = &obj->name; \
@@ -615,8 +627,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, olist); \
} \
\
static inline void \
M_C(name, _swap)(list_t d, list_t e) \
M_INLINE void \
M_F(name, _swap)(list_t d, list_t e) \
{ \
M_IL1ST_CONTRACT(name, d); \
M_IL1ST_CONTRACT(name, e); \
@@ -638,8 +650,8 @@ typedef struct m_il1st_head_s {
M_IL1ST_CONTRACT(name, e); \
} \
\
static inline void \
M_C(name, _reverse)(list_t list) \
M_INLINE void \
M_F(name, _reverse)(list_t list) \
{ \
M_IL1ST_CONTRACT(name, list); \
struct m_il1st_head_s *next, *it; \
@@ -653,11 +665,10 @@ typedef struct m_il1st_head_s {
it->prev = next; \
M_IL1ST_CONTRACT(name, list); \
} \
\
/* Used of internal macro from m-list */ \
M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t)
/******************************** INTERNAL ***********************************/
#if M_USE_SMALL_NAME
#define ILIST_INTERFACE M_ILIST_INTERFACE
#define ILIST_DEF M_ILIST_DEF
+61 -50
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - INTRUSIVE SHARED PTR Module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -43,25 +43,25 @@ M_BEGIN_PROTECTED_CODE
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_C(name, _cpt)
atomic_int M_F(name, _cpt)
/* Value of the interface field for static intialization (Uses C99 designated element). */
#define M_ISHARED_PTR_STATIC_DESIGNATED_INIT(name, type) \
.M_C(name, _cpt) = ATOMIC_VAR_INIT(0)
.M_F(name, _cpt) = ATOMIC_VAR_INIT(0)
/* Value of the interface field for static intialization (Uses C89 designated element). */
#define M_ISHARED_PTR_STATIC_INIT(name, type) \
ATOMIC_VAR_INIT(0)
/* Define the intrusive shared pointer type and its static inline functions.
/* 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_C(name,_t), __VA_ARGS__)
M_ISHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define the intrusive shared pointer type and its static inline functions
/* 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, ...) \
@@ -72,7 +72,9 @@ M_BEGIN_PROTECTED_CODE
M_END_PROTECTED_CODE
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/******************************** INTERNAL ***********************************/
/*****************************************************************************/
// Deferred evaluation
#define M_ISHAR3D_PTR_OPLIST_P1(arg) M_ISHAR3D_PTR_OPLIST_P2 arg
@@ -88,19 +90,19 @@ M_BEGIN_PROTECTED_CODE
// Define the oplist
#define M_ISHAR3D_PTR_OPLIST_P3(name, oplist) ( \
INIT(M_INIT_DEFAULT), \
INIT_SET(API_4(M_C(name, _init_set))), \
SET(M_C(name, _set) M_IPTR), \
CLEAR(M_C(name, _clear)), \
RESET(M_C(name, _reset) M_IPTR), \
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_C(name, _ct)), \
TYPE(M_F(name, _ct)), \
OPLIST(oplist), \
SUBTYPE(M_C(name, _subtype_ct)) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
SUBTYPE(M_F(name, _subtype_ct)) \
)
/******************************** INTERNAL ***********************************/
// Deferred evaluatioin
#define M_ISHAR3D_PTR_DEF_P1(arg) M_ID( M_ISHAR3D_PTR_DEF_P2 arg )
@@ -113,31 +115,38 @@ M_BEGIN_PROTECTED_CODE
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_C(name, _ct); \
typedef type M_C(name, _subtype_ct); \
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_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline shared_t \
M_C(name, _init)(type *ptr) \
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_C(name, _cpt), 2); \
atomic_init(&ptr->M_F(name, _cpt), 2); \
} \
return ptr; \
} \
\
static inline shared_t \
M_C(name, _init_set)(shared_t shared) \
M_INLINE shared_t \
M_F(name, _init_set)(shared_t shared) \
{ \
if (M_LIKELY (shared != NULL)) { \
int n = atomic_fetch_add(&(shared->M_C(name, _cpt)), 2); \
int n = atomic_fetch_add(&(shared->M_F(name, _cpt)), 2); \
(void) n; \
} \
return shared; \
@@ -147,91 +156,93 @@ M_BEGIN_PROTECTED_CODE
M_IF_DISABLED_METHOD(NEW, oplist) \
( \
/* This function is only for static object */ \
static inline shared_t \
M_C(name, _init_once)(type *shared) \
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_C(name, _cpt))); \
int o = atomic_load(&(shared->M_F(name, _cpt))); \
int n; \
do { \
n = o + 1 + (o != 0); \
} while (!atomic_compare_exchange_strong(&(shared->M_C(name, _cpt)), &o, n)); \
} 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_C(name, _cpt)), 1); \
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_C(name, _cpt)))&1) != 0 ) { \
while ((atomic_load(&(shared->M_F(name, _cpt)))&1) != 0 ) { \
m_core_backoff_wait(bkoff); \
} \
} \
M_ASSERT( (atomic_load(&(shared->M_C(name, _cpt)))&1) == 0); \
M_ASSERT( (atomic_load(&(shared->M_F(name, _cpt)))&1) == 0); \
} \
return shared; \
} \
, \
/* This function is only for dynamic object */ \
static inline shared_t \
M_C(name, _init_new)(void) \
M_INLINE shared_t \
M_F(name, _init_new)(void) \
{ \
type *ptr = M_CALL_NEW(oplist, type); \
if (ptr == NULL) { \
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
M_MEMORY_FULL(sizeof(type)); \
return NULL; \
} \
M_CALL_INIT(oplist, *ptr); \
atomic_init (&ptr->M_C(name, _cpt), 2); \
atomic_init (&ptr->M_F(name, _cpt), 2); \
return ptr; \
} \
/* End of NEW */) \
, /* End of INIT */) \
\
static inline void \
M_C(name, _clear)(shared_t shared) \
M_INLINE void \
M_F(name, _clear)(shared_t shared) \
{ \
if (shared != NULL) { \
if (atomic_fetch_sub(&(shared->M_C(name, _cpt)), 2) == 2) { \
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);) \
} \
} \
} \
\
static inline void \
M_C(name, _clear_ptr)(shared_t *shared) \
M_INLINE void \
M_F(name, _clear_ptr)(shared_t *shared) \
{ \
M_ASSERT(shared != NULL); \
M_C(name, _clear)(*shared); \
M_F(name, _clear)(*shared); \
*shared = NULL; \
} \
\
static inline void \
M_C(name, _reset)(shared_t *shared) \
M_INLINE void \
M_F(name, _reset)(shared_t *shared) \
{ \
M_C(name, _clear)(*shared); \
M_F(name, _clear)(*shared); \
*shared = NULL; \
} \
\
static inline void \
M_C(name, _set)(shared_t *ptr, shared_t shared) \
M_INLINE void \
M_F(name, _set)(shared_t *ptr, shared_t shared) \
{ \
M_ASSERT (ptr != NULL); \
if (M_LIKELY (*ptr != shared)) { \
M_C(name, _clear)(*ptr); \
*ptr = M_C(name, _init_set)(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
+394 -388
View File
File diff suppressed because it is too large. Load diff
+50 -36
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - MEMPOOL module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -40,13 +40,9 @@
*ptr = 17;
mempool_uint_free(m, ptr);
mempool_uint_clear(m); // Give back memory to system
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_MEMPOOL_DEF(name, type) \
M_MEMPOOL_DEF_AS(name, M_C(name,_t), type)
M_MEMPOOL_DEF_AS(name, M_F(name,_t), type)
/* Fast, fixed Size, thread unsafe allocator based on memory region.
@@ -70,40 +66,56 @@
#endif
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/********************************** 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_C(name,_union_s) { \
typedef union M_F(name,_union_s) { \
type t; \
union M_C(name,_union_s) *next; \
} M_C(name,_union_ct); \
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_C(name,_segment_s) { \
typedef struct M_F(name,_segment_s) { \
unsigned int count; \
struct M_C(name,_segment_s) *next; \
M_C(name,_union_ct) tab[M_USE_MEMPOOL_MAX_PER_SEGMENT(type)]; \
} M_C(name,_segment_ct); \
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_C(name, _s) { \
M_C(name,_union_ct) *free_list; \
M_C(name,_segment_ct) *current_segment; \
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) \
\
static inline void \
M_C(name,_init)(name_t mem) \
M_INLINE void \
M_F(name,_init)(name_t mem) \
{ \
mem->free_list = NULL; \
mem->current_segment = M_MEMORY_ALLOC(M_C(name,_segment_ct)); \
if (mem->current_segment == NULL) { \
M_MEMORY_FULL(sizeof (M_C(name,_segment_ct))); \
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; \
@@ -111,13 +123,13 @@
M_M3MPOOL_CONTRACT(mem, type); \
} \
\
static inline void \
M_C(name,_clear)(name_t mem) \
M_INLINE void \
M_F(name,_clear)(name_t mem) \
{ \
M_M3MPOOL_CONTRACT(mem, type); \
M_C(name,_segment_ct) *segment = mem->current_segment; \
M_F(name,_segment_ct) *segment = mem->current_segment; \
while (segment != NULL) { \
M_C(name,_segment_ct) *next = segment->next; \
M_F(name,_segment_ct) *next = segment->next; \
M_MEMORY_DEL (segment); \
segment = next; \
} \
@@ -126,26 +138,26 @@
mem->current_segment = NULL; \
} \
\
static inline type * \
M_C(name,_alloc)(name_t mem) \
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_C(name,_union_ct) *ret = mem->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_C(name,_segment_ct) *segment = mem->current_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_C(name,_segment_ct) *new_segment = M_MEMORY_ALLOC (M_C(name,_segment_ct)); \
if (M_UNLIKELY (new_segment == NULL)) { \
M_MEMORY_FULL(sizeof (M_C(name,_segment_ct))); \
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; \
@@ -161,13 +173,13 @@
return &ret->t; \
} \
\
static inline void \
M_C(name,_free)(name_t mem, type *ptr) \
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_C(name,_union_ct) *ret = (M_C(name,_union_ct) *)(uintptr_t)ptr; \
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; \
@@ -184,6 +196,8 @@
} while (0)
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define MEMPOOL_DEF M_MEMPOOL_DEF
#define MEMPOOL_DEF_AS M_MEMPOOL_DEF_AS
+5 -502
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Thin Mutex & Thread wrapper
* M*LIB - Thin Mutex & Thread wrapper (compatibility layer)
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -22,504 +22,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.
*/
#ifndef MSTARLIB_MUTEX_H
#define MSTARLIB_MUTEX_H
/* Auto-detect the thread backend to use if the user has not override it */
#ifndef M_USE_THREAD_BACKEND
# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
&& !defined(__STDC_NO_THREADS__)
# define M_USE_THREAD_BACKEND 1
# elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
# define M_USE_THREAD_BACKEND 2
# else
# define M_USE_THREAD_BACKEND 3
# endif
#endif
/****************************** C11 version ********************************/
#if M_USE_THREAD_BACKEND == 1
#include <threads.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
/* Define a mutex type based on C11 definition */
typedef mtx_t m_mutex_t[1];
/* Define a condition variable type based on C11 definition */
typedef cnd_t m_cond_t[1];
/* Define a thread type based on C11 definition */
typedef thrd_t m_thread_t[1];
/* Initialize the mutex (constructor) */
static inline void m_mutex_init(m_mutex_t m)
{
int rc = mtx_init(m, mtx_plain);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (rc == 0, "mutex");
}
/* Clear the mutex (destructor) */
static inline void m_mutex_clear(m_mutex_t m)
{
mtx_destroy(m);
}
/* Lock the mutex */
static inline void m_mutex_lock(m_mutex_t m)
{
mtx_lock(m);
}
/* Unlock the mutex */
static inline void m_mutex_unlock(m_mutex_t m)
{
mtx_unlock(m);
}
/* Initialize the condition variable (constructor) */
static inline void m_cond_init(m_cond_t c)
{
int rc = cnd_init(c);
// Abort program in case of initialization failure
// There is really nothing else to do if the object cannot be constructed
M_ASSERT_INIT (rc == 0, "conditional variable");
}
/* Clear the condition variable (destructor) */
static inline void m_cond_clear(m_cond_t c)
{
cnd_destroy(c);
}
/* Signal the condition variable to at least one waiting thread */
static inline void m_cond_signal(m_cond_t c)
{
cnd_signal(c);
}
/* Signal the condition variable to all waiting threads */
static inline void m_cond_broadcast(m_cond_t c)
{
cnd_broadcast(c);
}
/* Wait for signaling the condition variable by another thread */
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
{
cnd_wait(c, m);
}
/* Create the thread (constructor) and start it */
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
{
int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
// Abort program in case of initialization failure
M_ASSERT_INIT (rc == thrd_success, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
static inline void m_thread_join(m_thread_t t)
{
int rc = thrd_join(*t, NULL);
M_ASSERT (rc == thrd_success);
// Avoid warning about variable unused.
(void) rc;
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
static inline void m_thread_yield(void)
{
thrd_yield();
}
/* Sleep the thread for at least usec microseconds.
Return true if the sleep was successful */
static inline bool m_thread_sleep(unsigned long long usec)
{
struct timespec tv;
tv.tv_sec = (long) (usec / 1000000ULL);
tv.tv_nsec = (long) ((usec % 1000000ULL) * 1000UL);
int retval = thrd_sleep(&tv, NULL);
return retval == 0;
}
// a helper structure for m_once_call
typedef once_flag m_once_t[1];
// Initial value for m_once_t
#define M_ONCE_INIT_VALUE { ONCE_FLAG_INIT }
// Call the function exactly once
static inline void m_once_call(m_once_t o, void (*func)(void))
{
call_once(o,func);
}
// Attribute to use to allocate a global variable to a thread.
#define M_THREAD_ATTR _Thread_local
M_END_PROTECTED_CODE
/****************************** WIN32 version ******************************/
#elif M_USE_THREAD_BACKEND == 2
/* CLANG provides some useless and wrong warnings:
* - _WIN32_WINNT starts with '_' which is reserved by the standard
* as per the MSVC compiler, it is needed to be defined by the user
* to define which version of windows it want to be compatible with.
* - windows.h may be different than the case used by the file sytem
* there is however no normalized case.
*
* So, theses warnings have to be ignored and are disabled.
*
* We cannot add theses warnings in M_BEGIN_PROTECTED_CODE
* as they need to be disabled **BEFORE** including any system header
* and m-core includes some system headers.
* So we need to disable them explictly here.
*/
#if defined(__clang__) && __clang_major__ >= 4
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"")
_Pragma("clang diagnostic ignored \"-Wnonportable-system-include-path\"")
#endif
/* CriticalSection & ConditionVariable are available from Windows Vista */
#ifndef WINVER
#define WINVER _WIN32_WINNT_VISTA
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif
/* Include system headers */
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
#if defined(__clang__) && __clang_major__ >= 4
_Pragma("clang diagnostic pop")
#endif
M_BEGIN_PROTECTED_CODE
/* Define a thread type based on WINDOWS definition */
typedef HANDLE m_thread_t[1];
/* Define a mutex type based on WINDOWS definition */
typedef CRITICAL_SECTION m_mutex_t[1];
/* Define a condition variable type based on WINDOWS definition */
typedef CONDITION_VARIABLE m_cond_t[1];
/* Initialize a mutex (Constructor)*/
static inline void m_mutex_init(m_mutex_t m)
{
InitializeCriticalSection(m);
}
/* Clear a mutex (destructor) */
static inline void m_mutex_clear(m_mutex_t m)
{
DeleteCriticalSection(m);
}
/* Lock a mutex */
static inline void m_mutex_lock(m_mutex_t m)
{
EnterCriticalSection(m);
}
/* Unlock a mutex */
static inline void m_mutex_unlock(m_mutex_t m)
{
LeaveCriticalSection(m);
}
/* Initialize a condition variable (constructor) */
static inline void m_cond_init(m_cond_t c)
{
InitializeConditionVariable(c);
}
/* Clear a condition variable (destructor) */
static inline void m_cond_clear(m_cond_t c)
{
(void) c; // There is no destructor for this object.
}
/* Signal a condition variable to at least one waiting thread */
static inline void m_cond_signal(m_cond_t c)
{
WakeConditionVariable(c);
}
/* Signal a condition variable to all waiting threads */
static inline void m_cond_broadcast(m_cond_t c)
{
WakeAllConditionVariable(c);
}
/* Wait for a condition variable */
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
{
SleepConditionVariableCS(c, m, INFINITE);
}
/* Create a thread (constructor) and start it */
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (uintptr_t) func, arg, 0, NULL);
M_ASSERT_INIT (*t != NULL, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
static inline void m_thread_join(m_thread_t t)
{
DWORD dwWaitResult = WaitForSingleObject(*t, INFINITE);
(void) dwWaitResult;
M_ASSERT (dwWaitResult == WAIT_OBJECT_0);
CloseHandle(*t);
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
static inline void m_thread_yield(void)
{
Sleep(0);
}
/* Sleep the thread for at least usec microseconds
Return true if the sleep was successful */
static inline bool m_thread_sleep(unsigned long long usec)
{
LARGE_INTEGER ft;
M_ASSERT (usec <= LLONG_MAX);
ft.QuadPart = -(10LL*(long long) usec);
HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
M_ASSERT_INIT (hd != NULL, "timer");
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
CloseHandle(hd);
return dwWaitResult == WAIT_OBJECT_0;
}
typedef INIT_ONCE m_once_t[1];
#define M_ONCE_INIT_VALUE { INIT_ONCE_STATIC_INIT }
static inline BOOL CALLBACK m_once_callback( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
{
void (*func)(void);
(void) InitOnce;
(void) lpContext;
func = (void (*)(void))(uintptr_t) Parameter;
(*func)();
return TRUE;
}
static inline void m_once_call(m_once_t o, void (*func)(void))
{
InitOnceExecuteOnce(o, m_once_callback, (void*)(intptr_t)func, NULL);
}
#if defined(_MSC_VER)
// Attribute to use to allocate a global variable to a thread (MSVC def).
# define M_THREAD_ATTR __declspec( thread )
#else
// Attribute to use to allocate a global variable to a thread (GCC def).
# define M_THREAD_ATTR __thread
#endif
M_END_PROTECTED_CODE
/**************************** PTHREAD version ******************************/
#else
#include <pthread.h>
#ifdef _POSIX_PRIORITY_SCHEDULING
#include <sched.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
/* Define a mutex type based on PTHREAD definition */
typedef pthread_mutex_t m_mutex_t[1];
/* Define a condition variable type based on PTHREAD definition */
typedef pthread_cond_t m_cond_t[1];
/* Define a thread type based on PTHREAD definition */
typedef pthread_t m_thread_t[1];
/* Initialize the mutex (constructor) */
static inline void m_mutex_init(m_mutex_t m)
{
int _rc = pthread_mutex_init(m, NULL);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (_rc == 0, "mutex");
}
/* Clear the mutex (destructor) */
static inline void m_mutex_clear(m_mutex_t m)
{
pthread_mutex_destroy(m);
}
/* Lock the mutex */
static inline void m_mutex_lock(m_mutex_t m)
{
pthread_mutex_lock(m);
}
/* Unlock the mutex */
static inline void m_mutex_unlock(m_mutex_t m)
{
pthread_mutex_unlock(m);
}
/* Lazy lock initialization */
#define M_MUTEXI_INIT_VALUE { PTHREAD_MUTEX_INITIALIZER }
/* Internal function compatible with lazy lock */
static inline void m_mutexi_lazy_lock(m_mutex_t m)
{
pthread_mutex_lock(m);
}
/* Initialize the condition variable (constructor) */
static inline void m_cond_init(m_cond_t c)
{
int _rc = pthread_cond_init(c, NULL);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (_rc == 0, "conditional variable");
}
/* Clear the condition variable (destructor) */
static inline void m_cond_clear(m_cond_t c)
{
pthread_cond_destroy(c);
}
/* Signal a condition variable to at least a waiting thread */
static inline void m_cond_signal(m_cond_t c)
{
pthread_cond_signal(c);
}
/* Signal a condition variable to all waiting threads */
static inline void m_cond_broadcast(m_cond_t c)
{
pthread_cond_broadcast(c);
}
/* Waiting for a condition variable */
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
{
pthread_cond_wait(c, m);
}
/* Create a thread (constructor) and start it */
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (_rc == 0, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
static inline void m_thread_join(m_thread_t t)
{
int _rc = pthread_join(*t, NULL);
(void)_rc; // Avoid warning about variable unused.
M_ASSERT (_rc == 0);
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
static inline void m_thread_yield(void)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield();
#endif
}
/* Sleep for at least usec microseconds
Return true if the sleep was successful */
static inline bool m_thread_sleep(unsigned long long usec)
{
struct timeval tv;
/* We don't want to use usleep or nanosleep so that
we remain compatible with strict C99 build */
tv.tv_sec = (time_t) (usec / 1000000ULL);
tv.tv_usec = (suseconds_t) (usec % 1000000ULL);
int retval = select(1, NULL, NULL, NULL, &tv);
return retval == 0;
}
typedef pthread_once_t m_once_t[1];
#define M_ONCE_INIT_VALUE { PTHREAD_ONCE_INIT }
static inline void m_once_call(m_once_t o, void (*func)(void))
{
pthread_once(o,func);
}
#define M_THREAD_ATTR __thread
M_END_PROTECTED_CODE
#endif
// TODO: Obsolete M_LOCK macro.
/* M_LOCK macro. Allow simple locking encapsulation.
USAGE:
static M_LOCK_DECL(name);
int f(int n) {
M_LOCK(name) {
// Exclusive access
}
}
*/
/* NOTE: Either using direct support by the OS (WIN32/PTHREAD)
or using C11's ONCE mechanism */
#ifdef M_MUTEXI_INIT_VALUE
# define M_LOCK_DECL(name) m_mutex_t name = M_MUTEXI_INIT_VALUE
# define M_LOCK(name) \
M_LOCKI_DO(name, M_C(local_cont_, __LINE__), m_mutexi_lazy_lock, m_mutex_unlock)
#else
# define M_LOCK_DECL(name) \
m_mutex_t name; \
static void M_C(m_mutex_init_, name)(void) { \
m_mutex_init(name); \
} \
m_once_t M_C(m_once_, name) = M_ONCE_INIT_VALUE
# define M_LOCKI_BY_ONCE(name) \
(m_once_call(M_C(m_once_, name), M_C(m_mutex_init_, name)), \
m_mutex_lock(name), (void) 0 )
# define M_LOCK(name) \
M_LOCKI_DO(name, M_C(local_cont_, __LINE__), M_LOCKI_BY_ONCE, m_mutex_unlock)
#endif
#define M_LOCKI_DO(name, cont, lock_func, unlock_func) \
for(bool cont = true \
; cont && (lock_func (name), true); \
(unlock_func (name), cont = false))
#if defined(__GNUC__) && __GNUC__ >= 4
#warning "m-mutex.h is an obsolete header. Use m-thread.h instead."
#endif
#include "m-thread.h"
+244 -222
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - dynamic priority queue module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -33,7 +33,7 @@
/* Define a prioqueue of a given type and its associated functions.
USAGE: PRIOQUEUE_DEF(name, type [, oplist_of_the_type]) */
#define M_PRIOQUEUE_DEF(name, ...) \
M_PRIOQUEUE_DEF_AS(name, M_C(name,_t), M_C(name,_it_t), __VA_ARGS__)
M_PRIOQUEUE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
/* Define a prioqueue of a given type and its associated functions.
@@ -55,8 +55,9 @@
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
@@ -72,41 +73,43 @@
/* Define oplist of a priority queue */
#define M_PR1OQUEUE_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
(INIT(M_F(name, _init)) \
,INIT_SET(M_F(name, _init_set)) \
,INIT_WITH(API_1(M_INIT_VAI)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
,INIT_MOVE(M_C(name, _init_move)) \
,MOVE(M_C(name, _move)) \
,SWAP(M_C(name, _swap)) \
,SET(M_F(name, _set)) \
,CLEAR(M_F(name, _clear)) \
,INIT_MOVE(M_F(name, _init_move)) \
,MOVE(M_F(name, _move)) \
,SWAP(M_F(name, _swap)) \
,NAME(name) \
,TYPE(M_C(name,_ct)) \
,SUBTYPE(M_C(name, _subtype_ct)) \
,RESET(M_C(name,_reset)) \
,PUSH(M_C(name,_push)) \
,POP(M_C(name,_pop)) \
,TYPE(M_F(name,_ct)) \
,SUBTYPE(M_F(name, _subtype_ct)) \
,RESET(M_F(name,_reset)) \
,PUSH(M_F(name,_push)) \
,POP(M_F(name,_pop)) \
,OPLIST(oplist) \
,EMPTY_P(M_C(name, _empty_p)) \
,GET_SIZE(M_C(name, _size)) \
,IT_TYPE(M_C(name, _it_ct)) \
,IT_FIRST(M_C(name,_it)) \
,IT_END(M_C(name,_it_end)) \
,IT_SET(M_C(name,_it_set)) \
,IT_END_P(M_C(name,_end_p)) \
,IT_EQUAL_P(M_C(name,_it_equal_p)) \
,IT_LAST_P(M_C(name,_last_p)) \
,IT_NEXT(M_C(name,_next)) \
,IT_CREF(M_C(name,_cref)) \
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
,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(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)),) \
)
/********************************** INTERNAL *********************************/
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define M_PR1OQUEUE_DEF_P1(arg) M_ID( M_PR1OQUEUE_DEF_P2 arg )
@@ -127,368 +130,387 @@
- it_t: iterator of the container
*/
#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_C(name, _array), type, oplist) \
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)
/* Define the types */
#define M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \
\
/* Define the priority queue over the defined array */ \
typedef struct M_C(name, _s) { \
M_C(name, _array_t) array; \
typedef struct M_F(name, _s) { \
M_F(name, _array_t) array; \
} prioqueue_t[1]; \
/* Define the pointer references to the priority queue */ \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
/* The iterator is the same one as the one of the internal array */ \
typedef M_C(name, _array_it_t) it_t; \
typedef M_F(name, _array_it_t) it_t; \
\
/* Definition of the internal types used by the oplist */ \
typedef prioqueue_t M_C(name, _ct); \
typedef type M_C(name, _subtype_ct); \
typedef it_t M_C(name, _it_ct); \
typedef prioqueue_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
typedef it_t M_F(name, _it_ct); \
/* Define the core functions */
#define M_PR1OQUEUE_DEF_CORE(name, type, oplist, prioqueue_t, it_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(prioqueue_t p) \
M_INLINE void \
M_F(name, _init)(prioqueue_t p) \
{ \
M_C(name, _array_init)(p->array); \
M_F(name, _array_init)(p->array); \
} \
\
static inline void \
M_C(name, _init_set)(prioqueue_t p, prioqueue_t const o) \
M_INLINE void \
M_F(name, _init_set)(prioqueue_t p, prioqueue_t const o) \
{ \
M_C(name, _array_init_set)(p->array, o->array); \
M_F(name, _array_init_set)(p->array, o->array); \
} \
\
static inline void \
M_C(name, _set)(prioqueue_t p, prioqueue_t const o) \
M_INLINE void \
M_F(name, _set)(prioqueue_t p, prioqueue_t const o) \
{ \
M_C(name, _array_set)(p->array, o->array); \
M_F(name, _array_set)(p->array, o->array); \
} \
\
static inline void \
M_C(name, _clear)(prioqueue_t p) \
M_INLINE void \
M_F(name, _clear)(prioqueue_t p) \
{ \
M_C(name, _array_clear)(p->array); \
M_F(name, _array_clear)(p->array); \
} \
\
static inline void \
M_C(name, _init_move)(prioqueue_t p, prioqueue_t o) \
M_INLINE void \
M_F(name, _init_move)(prioqueue_t p, prioqueue_t o) \
{ \
M_C(name, _array_init_move)(p->array, o->array); \
M_F(name, _array_init_move)(p->array, o->array); \
} \
\
static inline void \
M_C(name, _move)(prioqueue_t p, prioqueue_t o) \
M_INLINE void \
M_F(name, _move)(prioqueue_t p, prioqueue_t o) \
{ \
M_C(name, _array_move)(p->array, o->array); \
M_F(name, _array_move)(p->array, o->array); \
} \
\
static inline void \
M_C(name, _swap)(prioqueue_t p, prioqueue_t o) \
M_INLINE void \
M_F(name, _swap)(prioqueue_t p, prioqueue_t o) \
{ \
M_C(name, _array_swap)(p->array, o->array); \
M_F(name, _array_swap)(p->array, o->array); \
} \
\
static inline void \
M_C(name, _reset)(prioqueue_t p) \
M_INLINE void \
M_F(name, _reset)(prioqueue_t p) \
{ \
M_C(name, _array_reset)(p->array); \
M_F(name, _array_reset)(p->array); \
} \
\
static inline size_t \
M_C(name, _i_parent)(size_t i) \
M_INLINE size_t \
M_F(name, _i_parent)(size_t i) \
{ \
M_ASSERT (i > 0); \
return (i - 1) / 2; \
} \
\
static inline size_t \
M_C(name, _i_lchild)(size_t i) \
M_INLINE size_t \
M_F(name, _i_lchild)(size_t i) \
{ \
M_ASSERT(i <= ((SIZE_MAX)-2)/2); \
return 2*i + 1; \
} \
\
static inline size_t \
M_C(name, _i_rchild)(size_t i) \
M_INLINE size_t \
M_F(name, _i_rchild)(size_t i) \
{ \
M_ASSERT(i <= ((SIZE_MAX)-2)/2); \
return 2*i + 2; \
} \
\
static inline int \
M_C(name, _i_cmp)(const prioqueue_t p, size_t i, size_t j) \
M_INLINE int \
M_F(name, _i_cmp)(const prioqueue_t p, size_t i, size_t j) \
{ \
return M_CALL_CMP(oplist, *M_C(name, _array_cget)(p->array, i), \
*M_C(name, _array_cget)(p->array, j)); \
return M_CALL_CMP(oplist, *M_F(name, _array_cget)(p->array, i), \
*M_F(name, _array_cget)(p->array, j)); \
} \
\
static inline bool \
M_C(name, _empty_p)(prioqueue_t const p) \
M_INLINE bool \
M_F(name, _empty_p)(prioqueue_t const p) \
{ \
return M_C(name, _array_empty_p)(p->array); \
return M_F(name, _array_empty_p)(p->array); \
} \
\
static inline size_t \
M_C(name, _size)(prioqueue_t const p) \
M_INLINE size_t \
M_F(name, _size)(prioqueue_t const p) \
{ \
return M_C(name, _array_size)(p->array); \
return M_F(name, _array_size)(p->array); \
} \
\
static inline void \
M_C(name, _push)(prioqueue_t p, type const x) \
M_INLINE void \
M_F(name, _push)(prioqueue_t p, type const x) \
{ \
/* Push back the new element at the end of the array */ \
M_C(name, _array_push_back)(p->array, x); \
M_F(name, _array_push_back)(p->array, x); \
\
/* Reorder the array by swapping with its parent \
* until it reaches the right position */ \
size_t i = M_C(name, _array_size)(p->array)-1; \
size_t i = M_F(name, _array_size)(p->array)-1; \
while (i > 0) { \
size_t j = M_C(name, _i_parent)(i); \
if (M_C(name, _i_cmp)(p, j, i) <= 0) \
size_t j = M_F(name, _i_parent)(i); \
if (M_F(name, _i_cmp)(p, j, i) <= 0) \
break; \
M_C(name, _array_swap_at) (p->array, i, j); \
M_F(name, _array_swap_at) (p->array, i, j); \
i = j; \
} \
} \
\
static inline type const * \
M_C(name, _front)(prioqueue_t const p) \
M_INLINE type const * \
M_F(name, _front)(prioqueue_t const p) \
{ \
return M_C(name, _array_cget)(p->array, 0); \
return M_F(name, _array_cget)(p->array, 0); \
} \
\
static inline void \
M_C(name, _pop)(type *x, prioqueue_t p) \
M_INLINE void \
M_F(name, _pop)(type *x, prioqueue_t p) \
{ \
/* Swap the front element with the last element */ \
size_t size = M_C(name, _array_size)(p->array)-1; \
M_C(name, _array_swap_at) (p->array, 0, size); \
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_C(name, _array_pop_back)(x, p->array); \
M_F(name, _array_pop_back)(x, p->array); \
\
/* Reorder the heap */ \
size_t i = 0; \
while (true) { \
size_t child = M_C(name, _i_lchild)(i); \
size_t child = M_F(name, _i_lchild)(i); \
if (child >= size) \
break; \
size_t otherChild = M_C(name, _i_rchild)(i); \
size_t otherChild = M_F(name, _i_rchild)(i); \
if (otherChild < size \
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
&& M_F(name, _i_cmp)(p, otherChild, child) < 0 ) { \
child = otherChild; \
} \
if (M_C(name, _i_cmp)(p, i, child) <= 0) \
if (M_F(name, _i_cmp)(p, i, child) <= 0) \
break; \
M_C(name, _array_swap_at) (p->array, i, child); \
M_F(name, _array_swap_at) (p->array, i, child); \
i = child; \
} \
} \
\
/* Define iterators over the array iterator */ \
static inline void \
M_C(name, _it)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_last)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it_last)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_end)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_it_end)(it, v->array); \
} \
\
static inline void \
M_C(name, _it_set)(it_t it, const it_t org) \
{ \
M_C(name, _array_it_set)(it, org); \
} \
\
static inline bool \
M_C(name, _end_p)(const it_t it) \
{ \
return M_C(name, _array_end_p)(it); \
} \
\
static inline bool \
M_C(name, _last_p)(const it_t it) \
{ \
return M_C(name, _array_last_p)(it); \
} \
\
static inline bool \
M_C(name, _it_equal_p)(const it_t it1, \
const it_t it2) \
{ \
return M_C(name, _array_it_equal_p)(it1, it2); \
} \
\
static inline void \
M_C(name, _next)(it_t it) \
{ \
M_C(name, _array_next)(it); \
} \
\
static inline void \
M_C(name, _previous)(it_t it) \
{ \
M_C(name, _array_previous)(it); \
} \
\
static inline type const * \
M_C(name, _cref)(const it_t it) \
{ \
return M_C(name, _array_cref)(it); \
} \
\
M_IF_METHOD(EQUAL, oplist) \
( \
/* EQUAL & CMP may be uncorrelated */ \
static inline bool \
M_C(name, _equal_p)(prioqueue_t const p, prioqueue_t const q) \
M_INLINE bool \
M_F(name, _equal_p)(prioqueue_t const p, prioqueue_t const q) \
{ \
return M_C(name, _array_equal_p)(p->array, q->array); \
return M_F(name, _array_equal_p)(p->array, q->array); \
} \
\
static inline bool \
M_C(name, _erase)(prioqueue_t p, type x) \
M_INLINE size_t \
M_F(name, _i_find)(prioqueue_t p, type const x) \
{ \
/* First pass: search for an item EQUAL to x */ \
/* NOTE: An HASHMAP may be a good idea to optimize this pass. */ \
size_t size = M_C(name, _array_size)(p->array); \
size_t size = M_F(name, _array_size)(p->array); \
size_t i = 0; \
for(i = 0; i < size; i++) { \
if (M_CALL_EQUAL(oplist, *M_C(name, _array_cget)(p->array, i), x)) \
/* We cannot use CMP and the partial order to go faster \
EQUAL & CMP may be uncorrelated */ \
if (M_CALL_EQUAL(oplist, *M_F(name, _array_cget)(p->array, i), x)) \
break; \
} \
return i; \
} \
\
M_INLINE bool \
M_F(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); \
size_t i = M_F(name, _i_find)(p, x); \
/* If x is not found, then stop */ \
if (i >= size) \
return false; \
/* Swap the found item and the last element */ \
size--; \
M_C(name, _array_swap_at) (p->array, i, size); \
M_C(name, _array_pop_back)(NULL, p->array); \
M_F(name, _array_swap_at) (p->array, i, size); \
M_F(name, _array_pop_back)(NULL, p->array); \
/* Move back the last swapped element to its right position in the heap */ \
while (true) { \
size_t child = M_C(name, _i_lchild)(i); \
size_t child = M_F(name, _i_lchild)(i); \
if (child >= size) break; \
size_t otherChild = M_C(name, _i_rchild)(i); \
size_t otherChild = M_F(name, _i_rchild)(i); \
if (otherChild < size \
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
&& M_F(name, _i_cmp)(p, otherChild, child) < 0 ) { \
child = otherChild; \
} \
if (M_C(name, _i_cmp)(p, i, child) <= 0) break; \
M_C(name, _array_swap_at) (p->array, i, child); \
if (M_F(name, _i_cmp)(p, i, child) <= 0) break; \
M_F(name, _array_swap_at) (p->array, i, child); \
i = child; \
} \
return true; \
} \
\
static inline void \
M_C(name, _update)(prioqueue_t p, type const xold, type const xnew) \
M_INLINE void \
M_F(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 */ \
size_t size = M_C(name, _array_size)(p->array); \
size_t i = 0; \
for(i = 0; i < size; i++) { \
if (M_CALL_EQUAL(oplist, *M_C(name, _array_cget)(p->array, i), xold)) \
break; \
} \
size_t size = M_F(name, _array_size)(p->array); \
size_t i = M_F(name, _i_find)(p, xold); \
/* We shall have found the item */ \
M_ASSERT (i < size); \
/* Test if the position of the old data is further or nearer than the new */ \
int cmp = M_CALL_CMP(oplist, *M_C(name, _array_cget)(p->array, i), xnew); \
int cmp = M_CALL_CMP(oplist, *M_F(name, _array_cget)(p->array, i), xnew); \
/* Set the found item to the new element */ \
M_C(name, _array_set_at) (p->array, i, xnew); \
M_F(name, _array_set_at) (p->array, i, xnew); \
if (cmp < 0) { \
/* Move back the updated element to its new position, further in the heap */ \
while (true) { \
size_t child = M_C(name, _i_lchild)(i); \
size_t child = M_F(name, _i_lchild)(i); \
if (child >= size) break; \
size_t otherChild = M_C(name, _i_rchild)(i); \
size_t otherChild = M_F(name, _i_rchild)(i); \
if (otherChild < size \
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
&& M_F(name, _i_cmp)(p, otherChild, child) < 0 ) { \
child = otherChild; \
} \
if (M_C(name, _i_cmp)(p, i, child) <= 0) break; \
M_C(name, _array_swap_at) (p->array, i, child); \
if (M_F(name, _i_cmp)(p, i, child) <= 0) break; \
M_F(name, _array_swap_at) (p->array, i, child); \
i = child; \
} \
} else { \
/* Move back the updated element to its new position, nearest in the heap */ \
while (i > 0) { \
size_t parent = M_C(name, _i_parent)(i); \
if (M_C(name, _i_cmp)(p, parent, i) <= 0) break; \
M_C(name, _array_swap_at) (p->array, i, parent); \
size_t parent = M_F(name, _i_parent)(i); \
if (M_F(name, _i_cmp)(p, parent, i) <= 0) break; \
M_F(name, _array_swap_at) (p->array, i, parent); \
i = parent; \
} \
} \
} \
, /* No EQUAL */ ) \
/* Define the IT based functions */
#define M_PR1OQUEUE_DEF_IT(name, type, oplist, prioqueue_t, it_t) \
\
M_IF_METHOD(OUT_STR, oplist)( \
static inline void \
M_C(name, _out_str)(FILE *file, const prioqueue_t p) \
/* Define iterators over the array iterator */ \
M_INLINE void \
M_F(name, _it)(it_t it, prioqueue_t const v) \
{ \
M_C(name, _array_out_str)(file, p->array); \
M_F(name, _array_it)(it, v->array); \
} \
\
M_INLINE void \
M_F(name, _it_last)(it_t it, prioqueue_t const v) \
{ \
M_F(name, _array_it_last)(it, v->array); \
} \
\
M_INLINE void \
M_F(name, _it_end)(it_t it, prioqueue_t const v) \
{ \
M_F(name, _array_it_end)(it, v->array); \
} \
\
M_INLINE void \
M_F(name, _it_set)(it_t it, const it_t org) \
{ \
M_F(name, _array_it_set)(it, org); \
} \
\
M_INLINE bool \
M_F(name, _end_p)(const it_t it) \
{ \
return M_F(name, _array_end_p)(it); \
} \
\
M_INLINE bool \
M_F(name, _last_p)(const it_t it) \
{ \
return M_F(name, _array_last_p)(it); \
} \
\
M_INLINE bool \
M_F(name, _it_equal_p)(const it_t it1, \
const it_t it2) \
{ \
return M_F(name, _array_it_equal_p)(it1, it2); \
} \
\
M_INLINE void \
M_F(name, _next)(it_t it) \
{ \
M_F(name, _array_next)(it); \
} \
\
M_INLINE void \
M_F(name, _previous)(it_t it) \
{ \
M_F(name, _array_previous)(it); \
} \
\
M_INLINE type const * \
M_F(name, _cref)(const it_t it) \
{ \
return M_F(name, _array_cref)(it); \
} \
/* Define the IO functions */
#define M_PR1OQUEUE_DEF_IO(name, type, oplist, prioqueue_t, it_t) \
M_IF_METHOD(OUT_STR, oplist)( \
M_INLINE void \
M_F(name, _out_str)(FILE *file, const prioqueue_t p) \
{ \
M_F(name, _array_out_str)(file, p->array); \
} \
,/* No OUT_STR */) \
\
M_IF_METHOD(IN_STR, oplist)( \
static inline bool \
M_C(name, _in_str)(prioqueue_t p, FILE *file) \
M_INLINE bool \
M_F(name, _in_str)(prioqueue_t p, FILE *file) \
{ \
return M_C(name, _array_in_str)(p->array, file); \
return M_F(name, _array_in_str)(p->array, file); \
} \
,/* No IN_STR */) \
\
M_IF_METHOD(GET_STR, oplist)( \
static inline void \
M_C(name, _get_str)(string_t str, const prioqueue_t p, bool append) \
M_INLINE void \
M_F(name, _get_str)(string_t str, const prioqueue_t p, bool append) \
{ \
M_C(name, _array_get_str)(str, p->array, append); \
M_F(name, _array_get_str)(str, p->array, append); \
} \
,/* No GET_STR */) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
static inline bool \
M_C(name, _parse_str)(prioqueue_t p, const char str[], const char **endp) \
M_INLINE bool \
M_F(name, _parse_str)(prioqueue_t p, const char str[], const char **endp) \
{ \
return M_C(name, _array_parse_str)(p->array, str, endp); \
return M_F(name, _array_parse_str)(p->array, str, endp); \
} \
,/* No PARSE_STR */) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, const prioqueue_t p) \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, const prioqueue_t p) \
{ \
return M_C(name, _array_out_serial)(f, p->array); \
return M_F(name, _array_out_serial)(f, p->array); \
} \
,/* No OUT_SERIAL */) \
\
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(prioqueue_t p, m_serial_read_t f) \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(prioqueue_t p, m_serial_read_t f) \
{ \
return M_C(name, _array_in_serial)(p->array, f); \
return M_F(name, _array_in_serial)(p->array, f); \
} \
,/* No in_SERIAL */) \
\
M_EMPLACE_QUEUE_DEF(name, prioqueue_t, M_C(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
// TODO: set all & remove all function
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define PRIOQUEUE_DEF M_PRIOQUEUE_DEF
#define PRIOQUEUE_DEF_AS M_PRIOQUEUE_DEF_AS
+214 -206
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - RED BLACK TREE module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -30,7 +30,7 @@
/* Define a Red/Black binary tree of a given type.
USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */
#define M_RBTREE_DEF(name, ...) \
M_RBTREE_DEF_AS(name, M_C(name,_t), M_C(name,_it_t), __VA_ARGS__)
M_RBTREE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
/* Define a Red/Black binary tree of a given type
@@ -39,8 +39,8 @@
#define M_RBTREE_DEF_AS(name, name_t, it_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_RBTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, M_C(name, _node_ct), it_t ), \
(name, __VA_ARGS__, name_t, M_C(name, _node_ct), it_t ))) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, M_F(name, _node_ct), it_t ), \
(name, __VA_ARGS__, name_t, M_F(name, _node_ct), it_t ))) \
M_END_PROTECTED_CODE
@@ -72,48 +72,48 @@
NOTE: IT_REF is not exported so that the container appears as not modifiable
by algorithm.*/
#define M_RBTR33_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)), \
INIT_SET(M_C(name, _init_set)), \
(INIT(M_F(name, _init)), \
INIT_SET(M_F(name, _init_set)), \
INIT_WITH(API_1(M_INIT_EMPLACE_VAI)), \
SET(M_C(name, _set)), \
CLEAR(M_C(name, _clear)), \
INIT_MOVE(M_C(name, _init_move)), \
MOVE(M_C(name, _move)), \
SWAP(M_C(name, _swap)), \
SET(M_F(name, _set)), \
CLEAR(M_F(name, _clear)), \
INIT_MOVE(M_F(name, _init_move)), \
MOVE(M_F(name, _move)), \
SWAP(M_F(name, _swap)), \
NAME(name), \
TYPE(M_C(name,_ct)), \
SUBTYPE(M_C(name, _subtype_ct)), \
EMPTY_P(M_C(name,_empty_p)), \
GET_SIZE(M_C(name, _size)), \
IT_TYPE(M_C(name, _it_ct)), \
IT_FIRST(M_C(name,_it)), \
IT_SET(M_C(name,_it_set)), \
IT_LAST(M_C(name,_it_last)), \
IT_END(M_C(name,_it_end)), \
IT_END_P(M_C(name,_end_p)), \
IT_LAST_P(M_C(name,_last_p)), \
IT_EQUAL_P(M_C(name,_it_equal_p)), \
IT_NEXT(M_C(name,_next)), \
IT_PREVIOUS(M_C(name,_previous)), \
IT_CREF(M_C(name,_cref)), \
IT_REMOVE(M_C(name,_remove)), \
RESET(M_C(name,_reset)), \
PUSH(M_C(name,_push)), \
GET_MIN(M_C(name,_min)), \
GET_MAX(M_C(name,_max)), \
M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),), \
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),), \
M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
TYPE(M_F(name,_ct)), \
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(M_F(name,_reset)), \
PUSH(M_F(name,_push)), \
GET_MIN(M_F(name,_min)), \
GET_MAX(M_F(name,_max)), \
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 *********************************/
/* Max depth of the binary tree
It is at worst twice the depth of a perfectly even tree with maximum elements.
The maximum number of elements is the max of size_t.
@@ -175,73 +175,78 @@ typedef enum {
- node_t: alias for the node of an element of the container
*/
#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)
/* Define the types associated to a R/B Tree */
#define M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \
\
/* Node of Red/Black tree. \
Each node has up to two child, a color (Red or black) \
and the data stored in it */ \
typedef struct M_C(name, _node_s) { \
struct M_C(name, _node_s) *child[2]; \
typedef struct M_F(name, _node_s) { \
struct M_F(name, _node_s) *child[2]; \
type data; \
m_rbtr33_color_e color; \
} node_t; \
\
/* Define the Red/Black tree */ \
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
size_t size; /* Number of elements in the tree */ \
node_t *node; /* Root node of the tree */ \
} tree_t[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
/* Iterator on a tree. The iterator stores the full path to the \
current node through all its parents and its depth */ \
typedef struct M_C(name, _it_s) { \
typedef struct M_F(name, _it_s) { \
node_t *stack[M_RBTR33_MAX_STACK]; \
int8_t which[M_RBTR33_MAX_STACK]; \
unsigned int cpt; \
} it_t[1]; \
\
/* Definition of the alias used by the oplists */ \
typedef type M_C(name, _subtype_ct); \
typedef tree_t M_C(name, _ct); \
typedef it_t M_C(name, _it_ct); \
\
typedef type M_F(name, _subtype_ct); \
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_C(name, _mempool), node_t) \
MEMPOOL_DEF(M_F(name, _mempool), node_t) \
/* Definition of the global variable used to reference this pool */ \
M_GET_MEMPOOL_LINKAGE oplist M_C(name, _mempool_t) M_GET_MEMPOOL oplist; \
M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \
/* Allocator function */ \
static inline node_t *M_C3(m_rbtr33_,name,_new)(void) { \
return M_C(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \
M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \
return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \
} \
/* Deallocator function */ \
static inline void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
M_C(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \
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 */ \
/* Callic allocator function (common case) */ \
static inline node_t *M_C3(m_rbtr33_,name,_new)(void) { \
/* Classic Allocator function (common case) */ \
M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \
return M_CALL_NEW(oplist, node_t); \
} \
/* Callic deallocator function (common case) */ \
static inline void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
/* Classic deallocator function (common case) */ \
M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
M_CALL_DEL(oplist, ptr); \
} ) \
\
/* Check if the given oplist is compatible with the given type */ \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
/* Generate all functions */ \
M_RBTR33_DEF_P4(name, type, oplist, tree_t, node_t, it_t)
#define M_RBTR33_DEF_P4(name, type, oplist, tree_t, node_t, it_t) \
/* Define the core functions */
#define M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \
\
static inline void \
M_C(name, _init)(tree_t tree) \
M_INLINE void \
M_F(name, _init)(tree_t tree) \
{ \
M_ASSERT (tree != NULL); \
tree->size = 0; \
@@ -249,8 +254,8 @@ typedef enum {
M_RBTR33_CONTRACT(tree); \
} \
\
static inline void \
M_C(name, _reset)(tree_t tree) \
M_INLINE void \
M_F(name, _reset)(tree_t tree) \
{ \
M_RBTR33_CONTRACT(tree); \
node_t *stack[M_RBTR33_MAX_STACK]; \
@@ -294,15 +299,15 @@ typedef enum {
tree->size = 0; \
} \
\
static inline void \
M_C(name, _clear)(tree_t tree) \
M_INLINE void \
M_F(name, _clear)(tree_t tree) \
{ \
/* Nothing more than clean the tree as everything is cleared */ \
M_C(name, _reset)(tree); \
M_F(name, _reset)(tree); \
} \
\
static inline void \
M_C(name, _push)(tree_t tree, type const data) \
M_INLINE void \
M_F(name, _push)(tree_t tree, type const data) \
{ \
M_RBTR33_CONTRACT(tree); \
node_t *tab[M_RBTR33_MAX_STACK]; \
@@ -312,7 +317,7 @@ typedef enum {
/* If there is no root node, create a new node */ \
if (n == NULL) { \
n = M_C3(m_rbtr33_,name,_new)(); \
if (M_UNLIKELY (n == NULL)) { \
if (M_UNLIKELY_NOMEM (n == NULL)) { \
M_MEMORY_FULL(sizeof (node_t)); \
return; \
} \
@@ -353,7 +358,7 @@ typedef enum {
} \
/* Create new node to store the data */ \
n = M_C3(m_rbtr33_,name,_new)(); \
if (M_UNLIKELY (n == NULL) ) { \
if (M_UNLIKELY_NOMEM (n == NULL) ) { \
M_MEMORY_FULL (sizeof (node_t)); \
return; \
} \
@@ -427,15 +432,15 @@ typedef enum {
M_RBTR33_CONTRACT (tree); \
} \
\
static inline size_t \
M_C(name, _size)(const tree_t tree) \
M_INLINE size_t \
M_F(name, _size)(const tree_t tree) \
{ \
M_RBTR33_CONTRACT (tree); \
return tree->size; \
} \
\
/* Set the iterator to the first (child=0) or last (child=1) element */ \
static inline void \
M_INLINE void \
M_C3(m_rbtr33_,name,_it)(it_t it, const tree_t tree, int child) \
{ \
M_RBTR33_CONTRACT (tree); \
@@ -457,20 +462,20 @@ typedef enum {
it->cpt = cpt; \
} \
\
static inline void \
M_C(name, _it)(it_t it, const tree_t tree) \
M_INLINE void \
M_F(name, _it)(it_t it, const tree_t tree) \
{ \
M_C3(m_rbtr33_,name,_it)(it, tree, 0); \
} \
\
static inline void \
M_C(name, _it_last)(it_t it, const tree_t tree) \
M_INLINE void \
M_F(name, _it_last)(it_t it, const tree_t tree) \
{ \
M_C3(m_rbtr33_,name,_it)(it, tree, 1); \
} \
\
static inline void \
M_C(name, _it_end)(it_t it, const tree_t tree) \
M_INLINE void \
M_F(name, _it_end)(it_t it, const tree_t tree) \
{ \
M_RBTR33_CONTRACT (tree); \
M_ASSERT (it != NULL); \
@@ -478,22 +483,22 @@ typedef enum {
it->cpt = 0; \
} \
\
static inline void \
M_C(name, _it_set)(it_t it, const it_t ref) \
M_INLINE void \
M_F(name, _it_set)(it_t it, const it_t ref) \
{ \
M_ASSERT (it != NULL && ref != NULL); \
*it = *ref; \
} \
\
static inline bool \
M_C(name, _end_p)(const it_t it) \
M_INLINE bool \
M_F(name, _end_p)(const it_t it) \
{ \
M_ASSERT (it != NULL); \
return it->cpt == 0; \
} \
\
/* Go to the next (child = 0)or previous element (child = 1) */ \
static inline void \
M_INLINE void \
M_C3(m_rbtr33_,name,_next)(it_t it, int child) \
{ \
M_ASSERT (it != NULL); \
@@ -525,20 +530,20 @@ typedef enum {
it->cpt = cpt; \
} \
\
static inline void \
M_C(name, _next)(it_t it) \
M_INLINE void \
M_F(name, _next)(it_t it) \
{ \
M_C3(m_rbtr33_,name,_next)(it, 0); \
} \
\
static inline void \
M_C(name, _previous)(it_t it) \
M_INLINE void \
M_F(name, _previous)(it_t it) \
{ \
M_C3(m_rbtr33_,name,_next)(it, 1); \
} \
\
static inline type * \
M_C(name, _ref)(const it_t it) \
M_INLINE type * \
M_F(name, _ref)(const it_t it) \
{ \
M_ASSERT(it != NULL); \
/* There shall be at least one element */ \
@@ -547,14 +552,14 @@ typedef enum {
return &(it->stack[it->cpt-1]->data); \
} \
\
static inline type const * \
M_C(name, _cref)(const it_t it) \
M_INLINE type const * \
M_F(name, _cref)(const it_t it) \
{ \
return M_CONST_CAST(type, M_C(name, _ref)(it)); \
return M_CONST_CAST(type, M_F(name, _ref)(it)); \
} \
\
static inline bool \
M_C(name, _it_equal_p)(const it_t it1, const it_t it2) \
M_INLINE bool \
M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \
{ \
M_ASSERT(it1 != NULL && it2 != NULL); \
/* There can be no element */ \
@@ -564,8 +569,8 @@ typedef enum {
&& (it1->cpt == 0 || it1->stack[it1->cpt-1] == it2->stack[it2->cpt-1]); \
} \
\
static inline void \
M_C(name, _it_from)(it_t it, const tree_t tree, type const data) \
M_INLINE void \
M_F(name, _it_from)(it_t it, const tree_t tree, type const data) \
{ \
M_RBTR33_CONTRACT (tree); \
M_ASSERT (it != NULL); \
@@ -589,12 +594,12 @@ typedef enum {
/* The iterator found may be strictly lower than data. \
In this case, go to the next element */ \
if (cmp < 0) { \
M_C(name, _next)(it); \
M_F(name, _next)(it); \
} \
} \
\
static inline bool \
M_C(name, _it_until_p)(it_t it, type const data) \
M_INLINE bool \
M_F(name, _it_until_p)(it_t it, type const data) \
{ \
M_ASSERT (it != NULL); \
if (M_UNLIKELY(it->cpt == 0)) return true; \
@@ -604,8 +609,8 @@ typedef enum {
return (cmp >= 0); \
} \
\
static inline bool \
M_C(name, _it_while_p)(it_t it, type const data) \
M_INLINE bool \
M_F(name, _it_while_p)(it_t it, type const data) \
{ \
M_ASSERT (it != NULL); \
if (M_UNLIKELY(it->cpt == 0)) return false; \
@@ -615,8 +620,8 @@ typedef enum {
return (cmp <= 0); \
} \
\
static inline type * \
M_C(name, _min)(const tree_t tree) \
M_INLINE type * \
M_F(name, _min)(const tree_t tree) \
{ \
M_RBTR33_CONTRACT (tree); \
node_t *n = tree->node; \
@@ -628,8 +633,8 @@ typedef enum {
return &n->data; \
} \
\
static inline type * \
M_C(name, _max)(const tree_t tree) \
M_INLINE type * \
M_F(name, _max)(const tree_t tree) \
{ \
M_RBTR33_CONTRACT (tree); \
node_t *n = tree->node; \
@@ -641,20 +646,20 @@ typedef enum {
return &n->data; \
} \
\
static inline type const * \
M_C(name, _cmin)(const tree_t tree) \
M_INLINE type const * \
M_F(name, _cmin)(const tree_t tree) \
{ \
return M_CONST_CAST(type, M_C(name, _min)(tree)); \
return M_CONST_CAST(type, M_F(name, _min)(tree)); \
} \
\
static inline type const * \
M_C(name, _cmax)(const tree_t tree) \
M_INLINE type const * \
M_F(name, _cmax)(const tree_t tree) \
{ \
return M_CONST_CAST(type, M_C(name, _max)(tree)); \
return M_CONST_CAST(type, M_F(name, _max)(tree)); \
} \
\
static inline type * \
M_C(name, _get)(const tree_t tree, type const data) \
M_INLINE type * \
M_F(name, _get)(const tree_t tree, type const data) \
{ \
M_RBTR33_CONTRACT (tree); \
node_t *n = tree->node; \
@@ -672,19 +677,19 @@ typedef enum {
return NULL; \
} \
\
static inline type const * \
M_C(name, _cget)(const tree_t tree, type const data) \
M_INLINE type const * \
M_F(name, _cget)(const tree_t tree, type const data) \
{ \
return M_CONST_CAST(type, M_C(name, _get)(tree, data)); \
return M_CONST_CAST(type, M_F(name, _get)(tree, data)); \
} \
\
/* Create a copy of the given node (recursively) */ \
static inline node_t * \
M_INLINE node_t * \
M_C3(m_rbtr33_,name,_copy_node)(const node_t *o) \
{ \
if (o == NULL) return NULL; \
node_t *n = M_C3(m_rbtr33_,name,_new)(); \
if (M_UNLIKELY (n == NULL) ) { \
if (M_UNLIKELY_NOMEM (n == NULL) ) { \
M_MEMORY_FULL (sizeof (node_t)); \
return NULL; \
} \
@@ -695,8 +700,8 @@ typedef enum {
return n; \
} \
\
static inline void \
M_C(name, _init_set)(tree_t tree, const tree_t ref) \
M_INLINE void \
M_F(name, _init_set)(tree_t tree, const tree_t ref) \
{ \
M_RBTR33_CONTRACT (ref); \
M_ASSERT (tree != NULL && tree != ref); \
@@ -706,18 +711,18 @@ typedef enum {
M_RBTR33_CONTRACT (tree); \
} \
\
static inline void \
M_C(name, _set)(tree_t tree, const tree_t ref) \
M_INLINE void \
M_F(name, _set)(tree_t tree, const tree_t ref) \
{ \
M_RBTR33_CONTRACT (tree); \
M_RBTR33_CONTRACT (ref); \
if (tree == ref) return; \
M_C(name,_clear)(tree); \
M_C(name,_init_set)(tree, ref); \
M_F(name,_clear)(tree); \
M_F(name,_init_set)(tree, ref); \
} \
\
static inline void \
M_C(name, _init_move)(tree_t tree, tree_t ref) \
M_INLINE void \
M_F(name, _init_move)(tree_t tree, tree_t ref) \
{ \
M_RBTR33_CONTRACT (ref); \
M_ASSERT (tree != NULL && tree != ref); \
@@ -728,19 +733,19 @@ typedef enum {
M_RBTR33_CONTRACT (tree); \
} \
\
static inline void \
M_C(name, _move)(tree_t tree, tree_t ref) \
M_INLINE void \
M_F(name, _move)(tree_t tree, tree_t ref) \
{ \
M_RBTR33_CONTRACT (tree); \
M_RBTR33_CONTRACT (ref); \
M_ASSERT (tree != ref); \
M_C(name,_clear)(tree); \
M_C(name,_init_move)(tree, ref); \
M_F(name,_clear)(tree); \
M_F(name,_init_move)(tree, ref); \
M_RBTR33_CONTRACT (tree); \
} \
\
static inline void \
M_C(name, _swap)(tree_t tree1, tree_t tree2) \
M_INLINE void \
M_F(name, _swap)(tree_t tree1, tree_t tree2) \
{ \
M_RBTR33_CONTRACT (tree1); \
M_RBTR33_CONTRACT (tree2); \
@@ -750,27 +755,27 @@ typedef enum {
M_RBTR33_CONTRACT (tree2); \
} \
\
static inline bool \
M_C(name, _empty_p)(const tree_t tree) \
M_INLINE bool \
M_F(name, _empty_p)(const tree_t tree) \
{ \
M_RBTR33_CONTRACT (tree); \
return tree->size == 0; \
} \
\
/* Take care of the case n == NULL too */ \
static inline bool \
M_INLINE bool \
M_C3(m_rbtr33_,name,_black_p)(const node_t *n) \
{ \
return (n == NULL) ? true : M_RBTR33_IS_BLACK(n); \
} \
\
static inline void \
M_INLINE void \
M_C3(m_rbtr33_,name,_set_black)(node_t *n) \
{ \
if (n != NULL) M_RBTR33_SET_BLACK(n); \
} \
\
static inline node_t * \
M_INLINE node_t * \
M_C3(m_rbtr33_,name,_rotate)(node_t *pp, node_t *ppp, const bool right) \
{ \
M_ASSERT (pp != NULL && ppp != NULL); \
@@ -787,7 +792,7 @@ typedef enum {
\
M_IF_DEBUG( \
/* Compute the depth of a node */ \
static size_t \
M_INLINE size_t \
M_C3(m_rbtr33_,name,_compute_depth)(const node_t *n) \
{ \
if (n == NULL) return 1; \
@@ -796,8 +801,8 @@ typedef enum {
} \
) \
\
static inline bool \
M_C(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \
M_INLINE bool \
M_F(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \
{ \
M_RBTR33_CONTRACT (tree); \
node_t *tab[M_RBTR33_MAX_STACK]; \
@@ -956,22 +961,22 @@ typedef enum {
return true; \
} \
\
static inline void M_C(name,_remove)(tree_t t, it_t it) \
M_INLINE void M_F(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_C(name,_cref)(it)); \
M_C(name,_next)(it); \
M_C(name, _pop_at)(NULL, t, data); \
M_CALL_INIT_SET(oplist, 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 */ \
if (!M_C(name, _end_p)(it)) { \
M_C(name, _it_from)(it, t, *M_C(name,_cref)(it)); \
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)( \
static inline bool M_C(name,_equal_p)(const tree_t t1, const tree_t t2) { \
M_INLINE bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \
M_RBTR33_CONTRACT(t1); \
M_RBTR33_CONTRACT(t2); \
if (t1->size != t2->size) return false; \
@@ -981,41 +986,43 @@ typedef enum {
structuraly different but functionnaly equal (you get this by \
constructing the tree in a different way). We have to \
compare the ordered value within the tree. */ \
M_C(name, _it)(it1, t1); \
M_C(name, _it)(it2, t2); \
while (!M_C(name, _end_p)(it1) \
&& !M_C(name, _end_p)(it2)) { \
type const *ref1 = M_C(name, _cref)(it1); \
type const *ref2 = M_C(name, _cref)(it2); \
M_F(name, _it)(it1, t1); \
M_F(name, _it)(it2, t2); \
while (!M_F(name, _end_p)(it1) \
&& !M_F(name, _end_p)(it2)) { \
type const *ref1 = M_F(name, _cref)(it1); \
type const *ref2 = M_F(name, _cref)(it2); \
if (M_CALL_EQUAL(oplist, *ref1, *ref2) == false) \
return false; \
M_C(name, _next)(it1); \
M_C(name, _next)(it2); \
M_F(name, _next)(it1); \
M_F(name, _next)(it2); \
} \
return M_C(name, _end_p)(it1) \
&& M_C(name, _end_p)(it2); \
return M_F(name, _end_p)(it1) \
&& M_F(name, _end_p)(it2); \
} \
, /* NO EQUAL METHOD */ ) \
\
M_IF_METHOD(HASH, oplist)( \
static inline size_t M_C(name,_hash)(const tree_t t1) { \
M_INLINE size_t M_F(name,_hash)(const tree_t t1) { \
M_RBTR33_CONTRACT(t1); \
M_HASH_DECL(hash); \
/* NOTE: We can't compute the hash directly for the same reason \
than for EQUAL operator. */ \
it_t it1; \
M_C(name, _it)(it1, t1); \
while (!M_C(name, _end_p)(it1)) { \
type const *ref1 = M_C(name, _cref)(it1); \
M_F(name, _it)(it1, t1); \
while (!M_F(name, _end_p)(it1)) { \
type const *ref1 = M_F(name, _cref)(it1); \
M_HASH_UP(hash, M_CALL_HASH(oplist, *ref1)); \
M_C(name, _next)(it1); \
M_F(name, _next)(it1); \
} \
return M_HASH_FINAL (hash); \
} \
, /* NO HASH METHOD */ ) \
\
/* 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)( \
static inline void M_C(name, _get_str)(m_string_t str, \
M_INLINE void M_F(name, _get_str)(m_string_t str, \
tree_t const t1, bool append) { \
M_RBTR33_CONTRACT(t1); \
M_ASSERT(str != NULL); \
@@ -1023,35 +1030,35 @@ typedef enum {
/* NOTE: The print is really naive, and not really efficient */ \
bool commaToPrint = false; \
it_t it1; \
M_C(name, _it)(it1, t1); \
while (!M_C(name, _end_p)(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); \
commaToPrint = true; \
type const *ref1 = M_C(name, _cref)(it1); \
type const *ref1 = M_F(name, _cref)(it1); \
M_CALL_GET_STR(oplist, str, *ref1, true); \
M_C(name, _next)(it1); \
M_F(name, _next)(it1); \
} \
m_string_push_back (str, ']'); \
} \
, /* NO GET_STR */ ) \
\
M_IF_METHOD(OUT_STR, oplist)( \
static inline void \
M_C(name, _out_str)(FILE *file, tree_t const rbtree) \
M_INLINE void \
M_F(name, _out_str)(FILE *file, tree_t const rbtree) \
{ \
M_RBTR33_CONTRACT(rbtree); \
M_ASSERT (file != NULL); \
fputc ('[', file); \
it_t it; \
bool commaToPrint = false; \
for (M_C(name, _it)(it, rbtree) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
for (M_F(name, _it)(it, rbtree) ; \
!M_F(name, _end_p)(it); \
M_F(name, _next)(it)){ \
if (commaToPrint) \
fputc (M_GET_SEPARATOR oplist, file); \
commaToPrint = true; \
type const *item = M_C(name, _cref)(it); \
type const *item = M_F(name, _cref)(it); \
M_CALL_OUT_STR(oplist, file, *item); \
} \
fputc (']', file); \
@@ -1059,12 +1066,12 @@ typedef enum {
, /* no out_str */ ) \
\
M_IF_METHOD(PARSE_STR, oplist)( \
static inline bool \
M_C(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \
M_INLINE bool \
M_F(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \
{ \
M_RBTR33_CONTRACT(rbtree); \
M_ASSERT (str != NULL); \
M_C(name,_reset)(rbtree); \
M_F(name,_reset)(rbtree); \
bool success = false; \
int c = *str++; \
if (M_UNLIKELY (c != '[')) goto exit; \
@@ -1078,7 +1085,7 @@ typedef enum {
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
do { c = *str++; } while (isspace(c)); \
if (b == false || c == 0) goto exit_clear; \
M_C(name, _push)(rbtree, item); \
M_F(name, _push)(rbtree, item); \
} while (c == M_GET_SEPARATOR oplist); \
success = (c == ']'); \
exit_clear: \
@@ -1090,12 +1097,12 @@ typedef enum {
, /* no parse_str */ ) \
\
M_IF_METHOD(IN_STR, oplist)( \
static inline bool \
M_C(name, _in_str)(tree_t rbtree, FILE *file) \
M_INLINE bool \
M_F(name, _in_str)(tree_t rbtree, FILE *file) \
{ \
M_RBTR33_CONTRACT(rbtree); \
M_ASSERT (file != NULL); \
M_C(name,_reset)(rbtree); \
M_F(name,_reset)(rbtree); \
int c = fgetc(file); \
if (M_UNLIKELY (c != '[')) return false; \
c = fgetc(file); \
@@ -1108,7 +1115,7 @@ typedef enum {
bool b = M_CALL_IN_STR(oplist, item, file); \
do { c = fgetc(file); } while (isspace(c)); \
if (b == false || c == EOF) break; \
M_C(name, _push)(rbtree, item); \
M_F(name, _push)(rbtree, item); \
} while (c == M_GET_SEPARATOR oplist); \
M_CALL_CLEAR(oplist, item); \
return c == ']'; \
@@ -1116,21 +1123,21 @@ typedef enum {
, /* no in_str */ ) \
\
M_IF_METHOD(OUT_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
M_INLINE m_serial_return_code_t \
M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
{ \
M_RBTR33_CONTRACT(t1); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
m_serial_local_t local; \
m_serial_return_code_t ret; \
M_C(name, _subtype_ct) const *item; \
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); \
for (M_C(name, _it)(it, t1) ; \
!M_C(name, _end_p)(it); \
M_C(name, _next)(it)){ \
item = M_C(name, _cref)(it); \
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 |= M_CALL_OUT_SERIAL(oplist, f, *item); \
@@ -1142,8 +1149,8 @@ typedef enum {
, /* no OUT_SERIAL */ ) \
\
M_IF_METHOD(IN_SERIAL, oplist)( \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(tree_t t1, m_serial_read_t f) \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \
{ \
M_RBTR33_CONTRACT(t1); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
@@ -1151,23 +1158,24 @@ typedef enum {
m_serial_return_code_t ret; \
size_t estimated_size = 0; \
type key; \
M_C(name,_reset)(t1); \
M_F(name,_reset)(t1); \
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
M_CALL_INIT(oplist, key); \
do { \
ret = M_CALL_IN_SERIAL(oplist, key, f); \
if (ret != M_SERIAL_OK_DONE) { break; } \
M_C(name, _push)(t1, key); \
M_F(name, _push)(t1, key); \
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
M_CALL_CLEAR(oplist, key); \
return ret; \
} \
, /* no in_serial */ ) \
\
M_EMPLACE_QUEUE_DEF(name, tree_t, M_C(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
/********************************** INTERNAL *********************************/
// TODO: specialized _sort shall do nothing, but shall check the requested order. How ?
#if M_USE_SMALL_NAME
#define RBTREE_DEF M_RBTREE_DEF
+67 -121
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Serial BIN
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -41,7 +41,7 @@ M_BEGIN_PROTECTED_CODE
* Write size_t in the stream in a compact form to reduce consumption
* (and I/O bandwidth)
*/
static inline bool
M_INLINE bool
m_ser1al_bin_write_size(FILE *f, const size_t size)
{
bool b;
@@ -88,7 +88,7 @@ m_ser1al_bin_write_size(FILE *f, const size_t size)
* Read size_t in the stream from a compact form to reduce consumption
* (and I/O bandwidth)
*/
static inline bool
M_INLINE bool
m_ser1al_bin_read_size(FILE *f, size_t *size)
{
int c;
@@ -111,7 +111,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_boolean(m_serial_write_t serial, const bool data)
{
FILE *f = (FILE *)serial->data[0].p;
@@ -121,69 +121,54 @@ 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 */
static inline m_serial_return_code_t
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)
{
int8_t i8;
int16_t i16;
int32_t i32;
int64_t i64;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
switch (size_of_type) {
case 1:
i8 = (int8_t) data;
if (size_of_type == 1) {
int8_t i8 = (int8_t) data;
n = fwrite (M_ASSIGN_CAST(const void*, &i8), sizeof i8, 1, f);
break;
case 2:
i16 = (int16_t) data;
} else if (size_of_type == 2) {
int16_t i16 = (int16_t) data;
n = fwrite (M_ASSIGN_CAST(const void*, &i16), sizeof i16, 1, f);
break;
case 4:
i32 = (int32_t) data;
} else if (size_of_type == 4) {
int32_t i32 = (int32_t) data;
n = fwrite (M_ASSIGN_CAST(const void*, &i32), sizeof i32, 1, f);
break;
case 8:
i64 = (int64_t) data;
} else {
M_ASSERT(size_of_type == 8);
int64_t i64 = (int64_t) data;
n = fwrite (M_ASSIGN_CAST(const void*, &i64), sizeof i64, 1, f);
break;
default:
M_ASSERT_INIT(false, "an integer of suitable size (8/16/32/64)");
break;
}
return n == 1 ? 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 */
static inline m_serial_return_code_t
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)
{
float f1;
double f2;
long double f3;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
if (size_of_type == sizeof f1) {
f1 = (float) data;
if (size_of_type == sizeof (float) ) {
float f1 = (float) data;
n = fwrite (M_ASSIGN_CAST(const void*, &f1), sizeof f1, 1, f);
} else if (size_of_type == sizeof f2) {
f2 = (double) data;
} else if (size_of_type == sizeof (double) ) {
double f2 = (double) data;
n = fwrite (M_ASSIGN_CAST(const void*, &f2), sizeof f2, 1, f);
} else if (size_of_type == sizeof f3) {
f3 = (long double) data;
n = fwrite (M_ASSIGN_CAST(const void*, &f3), sizeof f3, 1, f);
} else {
M_ASSERT_INIT(false, "a float of suitable size");
M_ASSERT(size_of_type == sizeof (long double) );
long double f3 = (long double) data;
n = fwrite (M_ASSIGN_CAST(const void*, &f3), sizeof f3, 1, f);
}
return n == 1 ? 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_string(m_serial_write_t serial, const char data[], size_t length)
{
M_ASSERT_SLOW(length == strlen(data) );
@@ -203,50 +188,39 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; //Unused
if (number_of_elements == (size_t)-1) return M_SERIAL_FAIL_RETRY;
FILE *f = (FILE *)serial->data[0].p;
size_t n = fwrite (M_ASSIGN_CAST(const void*, &number_of_elements), sizeof number_of_elements, 1, f);
local->data[0].b = (number_of_elements == 0);
return n == 1 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
}
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial)
{
FILE *f = (FILE *)serial->data[0].p;
// Need separator if we don't know the real size
if (local->data[0].b) {
size_t n = 0xABCDEF;
n = fwrite (M_ASSIGN_CAST(const void*, &n), sizeof n, 1, f);
return n == 1 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
} else {
return M_SERIAL_OK_CONTINUE;
}
(void) local; // Unused
(void) serial; // Unused
return M_SERIAL_OK_CONTINUE;
}
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial)
{
FILE *f = (FILE *)serial->data[0].p;
// Need mark if we don't know the real size
if (local->data[0].b) {
size_t n = 0x12345678;
n = fwrite (M_ASSIGN_CAST(const void*, &n), sizeof n, 1, f);
return n == 1 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
} else {
return M_SERIAL_OK_CONTINUE;
}
(void) local; // Unused
(void) serial; // Unused
return M_SERIAL_OK_CONTINUE;
}
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -258,7 +232,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -268,7 +242,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -281,7 +255,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -294,7 +268,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) field_name;
@@ -307,7 +281,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -335,13 +309,13 @@ static const m_serial_write_interface_t m_ser1al_bin_write_interface = {
m_ser1al_bin_write_variant_end
};
static inline void m_serial_bin_write_init(m_serial_write_t serial, FILE *f)
M_INLINE void m_serial_bin_write_init(m_serial_write_t serial, FILE *f)
{
serial->m_interface = &m_ser1al_bin_write_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
static inline void m_serial_bin_write_clear(m_serial_write_t serial)
M_INLINE void m_serial_bin_write_clear(m_serial_write_t serial)
{
(void) serial; // Nothing to do
}
@@ -363,7 +337,7 @@ typedef m_serial_write_t m_serial_bin_write_t;
/* Read from the stream 'serial' a boolean.
Set '*b' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_boolean(m_serial_read_t serial, bool *b){
FILE *f = (FILE*) serial->data[0].p;
size_t n = fread (M_ASSIGN_CAST(void*, b), sizeof (bool), 1, f);
@@ -373,7 +347,7 @@ m_ser1al_bin_read_boolean(m_serial_read_t serial, bool *b){
/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes.
Set '*i' with the integer value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){
int8_t i8;
int16_t i16;
@@ -381,26 +355,19 @@ m_ser1al_bin_read_integer(m_serial_read_t serial, long long *i, const size_t siz
int64_t i64;
size_t n;
FILE *f = (FILE *)serial->data[0].p;
switch (size_of_type) {
case 1:
if (size_of_type == 1) {
n = fread (M_ASSIGN_CAST(void*, &i8), sizeof i8, 1, f);
*i = i8;
break;
case 2:
} else if (size_of_type == 2) {
n = fread (M_ASSIGN_CAST(void*, &i16), sizeof i16, 1, f);
*i = i16;
break;
case 4:
} else if (size_of_type == 4) {
n = fread (M_ASSIGN_CAST(void*, &i32), sizeof i32, 1, f);
*i = i32;
break;
case 8:
} else {
M_ASSERT(size_of_type == 8);
n = fread (M_ASSIGN_CAST(void*, &i64), sizeof i64, 1, f);
*i = i64;
break;
default:
M_ASSERT_INIT(false, "an integer of suitable size");
break;
}
return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
@@ -408,7 +375,7 @@ m_ser1al_bin_read_integer(m_serial_read_t serial, long long *i, const size_t siz
/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes.
Set '*r' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){
float f1;
double f2;
@@ -421,11 +388,10 @@ m_ser1al_bin_read_float(m_serial_read_t serial, long double *r, const size_t siz
} else if (size_of_type == sizeof f2) {
n = fread (M_ASSIGN_CAST(void*, &f2), sizeof f2, 1, f);
*r = f2;
} else if (size_of_type == sizeof f3) {
} else {
M_ASSERT(size_of_type == sizeof f3);
n = fread (M_ASSIGN_CAST(void*, &f3), sizeof f3, 1, f);
*r = f3;
} else {
M_ASSERT_INIT(false, "a float of suitable size");
}
return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
@@ -433,7 +399,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_string(m_serial_read_t serial, struct string_s *s){
FILE *f = (FILE*) serial->data[0].p;
M_ASSERT(f != NULL && s != NULL);
@@ -458,53 +424,33 @@ m_ser1al_bin_read_string(m_serial_read_t serial, struct string_s *s){
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
FILE *f = (FILE*) serial->data[0].p;
size_t n = fread (M_ASSIGN_CAST(void*, num), sizeof *num, 1, f);
if (n != 1)
return m_core_serial_fail();
local->data[0].b = (*num == 0);
local->data[1].s = *num;
if (local->data[0].b) {
// Size not know ==> use of marker in the stream.
size_t p;
n = fread (M_ASSIGN_CAST(void*, &p), sizeof p, 1, f);
if (n != 1)
return m_core_serial_fail();
return p == 0xABCDEF ? M_SERIAL_OK_CONTINUE : p == 0x12345678 ? M_SERIAL_OK_DONE : m_core_serial_fail();
}
return M_SERIAL_OK_CONTINUE;
return (n != 1) ? m_core_serial_fail() : (local->data[1].s == 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE;
}
/* Continue reading from the stream 'serial' an array.
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_array_next(m_serial_local_t local, m_serial_read_t serial)
{
FILE *f = (FILE*) serial->data[0].p;
if (local->data[0].b) {
// Size not know ==> use of marker in the stream.
size_t p, n;
n = fread (M_ASSIGN_CAST(void*, &p), sizeof p, 1, f);
if (n != 1)
return m_core_serial_fail();
return p == 0xABCDEF ? M_SERIAL_OK_CONTINUE : p == 0x12345678 ? M_SERIAL_OK_DONE : m_core_serial_fail();
} else {
M_ASSERT(local->data[1].s > 0);
local->data[1].s --;
return local->data[1].s == 0 ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE;
}
(void) serial; // Unused
M_ASSERT(local->data[1].s > 0);
local->data[1].s --;
return local->data[1].s == 0 ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE;
}
/* Continue reading from the stream 'serial' the value separator
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_map_value(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -515,7 +461,7 @@ m_ser1al_bin_read_map_value(m_serial_local_t local, m_serial_read_t serial)
/* Start reading a tuple from the stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
{
(void) serial;
@@ -529,7 +475,7 @@ m_ser1al_bin_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
Return M_SERIAL_OK_DONE if it succeeds and the tuple ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id)
{
(void) serial;
@@ -546,7 +492,7 @@ m_ser1al_bin_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const
Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues,
Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id)
{
(void) field_name;
@@ -560,7 +506,7 @@ m_ser1al_bin_read_variant_start(m_serial_local_t local, m_serial_read_t serial,
/* End reading a variant from the stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds and the variant ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_bin_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -584,13 +530,13 @@ static const m_serial_read_interface_t m_ser1al_bin_read_interface = {
m_ser1al_bin_read_variant_end
};
static inline void m_serial_bin_read_init(m_serial_read_t serial, FILE *f)
M_INLINE void m_serial_bin_read_init(m_serial_read_t serial, FILE *f)
{
serial->m_interface = &m_ser1al_bin_read_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
static inline void m_serial_bin_read_clear(m_serial_read_t serial)
M_INLINE void m_serial_bin_read_clear(m_serial_read_t serial)
{
(void) serial; // Nothing to do
}
+76 -73
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Serial JSON
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -36,7 +36,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_boolean(m_serial_write_t serial, const bool data)
{
FILE *f = (FILE *)serial->data[0].p;
@@ -46,7 +46,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) size_of_type; // Ignored
@@ -57,7 +57,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) size_of_type; // Ignored
@@ -68,7 +68,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t length)
{
M_ASSERT_SLOW(length == strlen(data) );
@@ -81,7 +81,7 @@ m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t le
v2->u.heap.alloc = length + 1;
v2->ptr = (char*)ptr;
m_string_out_str(f, v2);
return M_SERIAL_OK_DONE;
return M_UNLIKELY(ferror(f)) ? m_core_serial_fail() : M_SERIAL_OK_DONE;
}
/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'.
@@ -90,7 +90,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -102,7 +102,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -113,7 +113,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -128,7 +128,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -140,7 +140,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -151,7 +151,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -162,7 +162,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -175,7 +175,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -186,7 +186,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -198,7 +198,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -212,7 +212,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -231,7 +231,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
{
(void) local; // argument not used
@@ -262,14 +262,14 @@ static const m_serial_write_interface_t m_ser1al_json_write_interface = {
};
/* Initialize the JSON serial object for writing any object to JSON format in the given FILE */
static inline void m_serial_json_write_init(m_serial_write_t serial, FILE *f)
M_INLINE void m_serial_json_write_init(m_serial_write_t serial, FILE *f)
{
serial->m_interface = &m_ser1al_json_write_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
/* CLear the JSON serial object for writing*/
static inline void m_serial_json_write_clear(m_serial_write_t serial)
M_INLINE void m_serial_json_write_clear(m_serial_write_t serial)
{
(void) serial; // Nothing to do
}
@@ -288,20 +288,21 @@ typedef m_serial_write_t m_serial_json_write_t;
/********************************************************************************/
/* Helper function to skip a space */
static inline int
M_INLINE int
m_ser1al_json_read_skip (FILE *f)
{
int c;
do {
// c is an int, and is the value of the character read as unsigned char
c = fgetc(f);
} while (isspace(c));
} while (c != EOF && isspace(c));
return c;
}
/* Read from the stream 'serial' a boolean.
Set '*b' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_boolean(m_serial_read_t serial, bool *b){
FILE *f = (FILE*) serial->data[0].p;
int c = m_ser1al_json_read_skip(f);
@@ -333,7 +334,7 @@ m_ser1al_json_read_boolean(m_serial_read_t serial, bool *b){
/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes.
Set '*i' with the integer value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){
(void) size_of_type; // Ignored
FILE *f = (FILE*) serial->data[0].p;
@@ -343,7 +344,7 @@ m_ser1al_json_read_integer(m_serial_read_t serial, long long *i, const size_t si
/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes.
Set '*r' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){
(void) size_of_type; // Ignored
FILE *f = (FILE*) serial->data[0].p;
@@ -353,7 +354,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE 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.
@@ -368,7 +369,7 @@ m_ser1al_json_read_string(m_serial_read_t serial, struct m_string_s *s){
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
(void) local; // argument not used
@@ -386,7 +387,7 @@ m_ser1al_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, s
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_array_next(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -404,7 +405,7 @@ m_ser1al_json_read_array_next(m_serial_local_t local, m_serial_read_t serial)
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_OK_DONE if it succeeds and the map ends (the map is empty),
m_core_serial_fail() otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
(void) local; // argument not used
@@ -421,7 +422,7 @@ m_ser1al_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, siz
/* Continue reading from the stream 'serial' the value separator
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_map_value(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -436,7 +437,7 @@ m_ser1al_json_read_map_value(m_serial_local_t local, m_serial_read_t serial)
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_OK_DONE if it succeeds and the map ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_map_next(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -450,7 +451,7 @@ m_ser1al_json_read_map_next(m_serial_local_t local, m_serial_read_t serial)
/* Start reading a tuple from the stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -467,7 +468,7 @@ m_ser1al_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
Return M_SERIAL_OK_DONE if it succeeds and the tuple ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id)
{
(void) local; // argument not used
@@ -506,7 +507,7 @@ m_ser1al_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, cons
Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues,
Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id)
{
(void) local; // argument not used
@@ -541,7 +542,7 @@ m_ser1al_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial,
/* End reading a variant from the stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds and the variant ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -569,14 +570,14 @@ static const m_serial_read_interface_t m_ser1al_json_read_interface = {
};
/* Initialize the JSON serial object for reading any object from JSON format in the given FILE */
static inline void m_serial_json_read_init(m_serial_read_t serial, FILE *f)
M_INLINE void m_serial_json_read_init(m_serial_read_t serial, FILE *f)
{
serial->m_interface = &m_ser1al_json_read_interface;
serial->data[0].p = M_ASSIGN_CAST(void*, f);
}
/* Clear the JSON serial object for reading from the FILE */
static inline void m_serial_json_read_clear(m_serial_read_t serial)
M_INLINE void m_serial_json_read_clear(m_serial_read_t serial)
{
(void) serial; // Nothing to do
}
@@ -596,7 +597,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE 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;
@@ -606,7 +607,7 @@ m_ser1al_str_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 */
static inline m_serial_return_code_t
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)
{
(void) size_of_type; // Ignored
@@ -617,7 +618,7 @@ m_ser1al_str_json_write_integer(m_serial_write_t serial,const long long data, co
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_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)
{
(void) size_of_type; // Ignored
@@ -628,7 +629,7 @@ m_ser1al_str_json_write_float(m_serial_write_t serial, const long double data, c
/* Write the null-terminated string 'data'into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_write_string(m_serial_write_t serial, const char data[], size_t length)
{
M_ASSERT_SLOW(length == strlen(data) );
@@ -650,7 +651,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -662,7 +663,7 @@ m_ser1al_str_json_write_array_start(m_serial_local_t local, m_serial_write_t ser
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE 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
@@ -673,7 +674,7 @@ m_ser1al_str_json_write_array_next(m_serial_local_t local, m_serial_write_t seri
/* End the writing of an array into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE 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
@@ -688,7 +689,7 @@ 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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -700,7 +701,7 @@ m_ser1al_str_json_write_map_start(m_serial_local_t local, m_serial_write_t seria
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE 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
@@ -711,7 +712,7 @@ m_ser1al_str_json_write_map_value(m_serial_local_t local, m_serial_write_t seria
/* Write a map separator between elements of a map into the serial stream 'serial' if needed.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE 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
@@ -722,7 +723,7 @@ m_ser1al_str_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 */
static inline m_serial_return_code_t
M_INLINE 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
@@ -735,7 +736,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE 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
@@ -746,7 +747,7 @@ m_ser1al_str_json_write_tuple_start(m_serial_local_t local, m_serial_write_t ser
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_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)
{
(void) local; // argument not used
@@ -758,7 +759,7 @@ m_ser1al_str_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial
/* End the write of a tuple into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE 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
@@ -772,7 +773,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 */
static inline m_serial_return_code_t
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)
{
(void) local; // argument not used
@@ -791,7 +792,7 @@ m_ser1al_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t s
/* End Writing a variant into the serial stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE 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
@@ -821,14 +822,14 @@ static const m_serial_write_interface_t m_ser1al_str_json_write_interface = {
};
/* Initialize the JSON serial object for writing any object to JSON format in the given FILE */
static inline void m_serial_str_json_write_init(m_serial_write_t serial, m_string_t s)
M_INLINE void m_serial_str_json_write_init(m_serial_write_t serial, m_string_t s)
{
serial->m_interface = &m_ser1al_str_json_write_interface;
serial->data[0].p = M_ASSIGN_CAST(struct m_string_s *, s);
}
/* CLear the JSON serial object for writing*/
static inline void m_serial_str_json_write_clear(m_serial_write_t serial)
M_INLINE void m_serial_str_json_write_clear(m_serial_write_t serial)
{
(void) serial; // Nothing to do
}
@@ -846,7 +847,7 @@ typedef m_serial_write_t m_serial_str_json_write_t;
/********************************************************************************/
/* Helper function to read a character and advance the stream */
static inline char
M_INLINE char
m_ser1al_str_json_getc(const char **p)
{
char c = **p;
@@ -856,7 +857,7 @@ m_ser1al_str_json_getc(const char **p)
}
/* Helper function to skip space(s) */
static inline void
M_INLINE void
m_ser1al_str_json_skip (const char **p)
{
while (isspace (**p)) {
@@ -865,7 +866,7 @@ m_ser1al_str_json_skip (const char **p)
}
/* Helper function to read a string with characters not present in reject */
static inline void
M_INLINE void
m_ser1al_str_json_gets(size_t alloc, char field[], const char reject[], const char **s)
{
M_ASSERT (alloc > 0);
@@ -880,7 +881,7 @@ m_ser1al_str_json_gets(size_t alloc, char field[], const char reject[], const ch
/* Read from the stream 'serial' a boolean.
Set '*b' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_boolean(m_serial_read_t serial, bool *b){
const char **f = &serial->data[0].cstr;
m_ser1al_str_json_skip(f);
@@ -913,7 +914,7 @@ m_ser1al_str_json_read_boolean(m_serial_read_t serial, bool *b){
/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes.
Set '*i' with the integer value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){
(void) size_of_type; // Ignored
const char **f = &serial->data[0].cstr;
@@ -927,7 +928,7 @@ m_ser1al_str_json_read_integer(m_serial_read_t serial, long long *i, const size_
/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes.
Set '*r' with the boolean value if succeeds
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){
(void) size_of_type; // Ignored
const char **f = &serial->data[0].cstr;
@@ -941,7 +942,7 @@ 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 */
static inline m_serial_return_code_t
M_INLINE 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);
@@ -958,7 +959,7 @@ m_ser1al_str_json_read_string(m_serial_read_t serial, struct m_string_s *s){
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
(void) local; // argument not used
@@ -972,6 +973,7 @@ m_ser1al_str_json_read_array_start(m_serial_local_t local, m_serial_read_t seria
m_ser1al_str_json_skip(f);
if (M_UNLIKELY (**f == ']')) {
c = m_ser1al_str_json_getc(f);
assert( c == ']');
return M_SERIAL_OK_DONE;
} else {
return M_SERIAL_OK_CONTINUE;
@@ -982,7 +984,7 @@ m_ser1al_str_json_read_array_start(m_serial_local_t local, m_serial_read_t seria
Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue,
M_SERIAL_OK_DONE if it succeeds and the array ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_array_next(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -999,7 +1001,7 @@ m_ser1al_str_json_read_array_next(m_serial_local_t local, m_serial_read_t serial
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_OK_DONE if it succeeds and the map ends (the map is empty),
m_core_serial_fail() otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
{
(void) local; // argument not used
@@ -1013,6 +1015,7 @@ m_ser1al_str_json_read_map_start(m_serial_local_t local, m_serial_read_t serial,
m_ser1al_str_json_skip(f);
if (M_UNLIKELY (**f == '}')) {
c = m_ser1al_str_json_getc(f);
assert(c == '}');
return M_SERIAL_OK_DONE;
} else {
return M_SERIAL_OK_CONTINUE;
@@ -1022,7 +1025,7 @@ m_ser1al_str_json_read_map_start(m_serial_local_t local, m_serial_read_t serial,
/* Continue reading from the stream 'serial' the value separator
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_map_value(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -1036,7 +1039,7 @@ m_ser1al_str_json_read_map_value(m_serial_local_t local, m_serial_read_t serial)
Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue,
M_SERIAL_OK_DONE if it succeeds and the map ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_map_next(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -1049,7 +1052,7 @@ m_ser1al_str_json_read_map_next(m_serial_local_t local, m_serial_read_t serial)
/* Start reading a tuple from the stream 'serial'.
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -1065,7 +1068,7 @@ m_ser1al_str_json_read_tuple_start(m_serial_local_t local, m_serial_read_t seria
Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues,
Return M_SERIAL_OK_DONE if it succeeds and the tuple ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id)
{
(void) local; // argument not used
@@ -1106,7 +1109,7 @@ m_ser1al_str_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial,
Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues,
Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty),
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id)
{
(void) local; // argument not used
@@ -1143,7 +1146,7 @@ m_ser1al_str_json_read_variant_start(m_serial_local_t local, m_serial_read_t ser
/* End reading a variant from the stream 'serial'.
Return M_SERIAL_OK_DONE if it succeeds and the variant ends,
M_SERIAL_FAIL otherwise */
static inline m_serial_return_code_t
M_INLINE m_serial_return_code_t
m_ser1al_str_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
{
(void) local; // argument not used
@@ -1170,14 +1173,14 @@ static const m_serial_read_interface_t m_ser1al_str_json_read_interface = {
};
/* Initialize the JSON serial object for reading any object from JSON format in the given const string */
static inline void m_serial_str_json_read_init(m_serial_read_t serial, const char str[])
M_INLINE void m_serial_str_json_read_init(m_serial_read_t serial, const char str[])
{
serial->m_interface = &m_ser1al_str_json_read_interface;
serial->data[0].cstr = str;
}
/* Clear the JSON serial object for reading from the FILE */
static inline const char *m_serial_str_json_read_clear(m_serial_read_t serial)
M_INLINE const char *m_serial_str_json_read_clear(m_serial_read_t serial)
{
// Nothing to clear. Return pointer to the last data parsed.
return serial->data[0].cstr;
+128 -110
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SHARED Module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -31,18 +31,10 @@
M_BEGIN_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 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_C(name,_t), __VA_ARGS__)
M_SHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define shared pointer and its function
@@ -56,10 +48,18 @@ M_BEGIN_PROTECTED_CODE
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_C(name,_t), __VA_ARGS__)
M_SHARED_PTR_RELAXED_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define relaxed shared pointer and its function (thread unsafe)
@@ -77,7 +77,7 @@ M_BEGIN_PROTECTED_CODE
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_C(name,_t), M_C(name,_it_t), __VA_ARGS__)
M_SHARED_RESOURCE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
/* Define shared resource and its function
@@ -93,7 +93,9 @@ M_BEGIN_PROTECTED_CODE
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
// deferred evaluation
#define M_SHAR3D_PTR_OPLIST_P1(arg) M_SHAR3D_PTR_OPLIST_P2 arg
@@ -107,19 +109,16 @@ M_BEGIN_PROTECTED_CODE
((M_LIB_ERROR(ARGUMENT_OF_SHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
#define M_SHAR3D_PTR_OPLIST_P3(name, oplist) ( \
INIT(M_C(name, _init)), \
CLEAR(M_C(name, _clear)), \
INIT_SET(M_C(name, _init_set)), \
SET(M_C(name, _set)) \
INIT_MOVE(M_C(name, _init_move)), \
RESET(M_C(name, _reset)), \
MOVE(M_C(name, _move)), \
SWAP(M_C(name, _swap)) \
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_C(name, _ct)) \
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
,TYPE(M_F(name, _ct)) \
)
// OPLIST to handle a counter of atomic type
@@ -137,10 +136,13 @@ M_BEGIN_PROTECTED_CODE
IT_CREF(m_shar3d_integer_cref))
/* Atomic like interface for basic integers */
static inline void m_shar3d_integer_init_set(int *p, int val) { *p = val; }
static inline int m_shar3d_integer_add(int *p, int val) { int r = *p; *p += val; return r; }
static inline int m_shar3d_integer_sub(int *p, int val) { int r = *p; *p -= val; return r; }
static inline int m_shar3d_integer_cref(int *p) { return *p; }
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 { \
@@ -161,44 +163,51 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
/* 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) \
/* Define the types */
#define M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t) \
\
typedef struct M_C(name, _s){ \
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_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
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_C(name, _ct); \
typedef shared_t M_F(name, _ct); \
\
typedef struct M_C(name, combine_s) { \
typedef struct M_F(name, combine_s) { \
type data; \
struct M_C(name, _s) ptr; \
} M_C(name, combine_ct)[1]; \
struct M_F(name, _s) ptr; \
} M_F(name, combine_ct)[1]; \
/* Define the core functions */
#define M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(shared_t shared) \
M_INLINE void \
M_F(name, _init)(shared_t shared) \
{ \
*shared = NULL; \
} \
\
static inline void \
M_C(name, _init2)(shared_t shared, type *data) \
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_C(name, _s) *ptr; \
struct M_F(name, _s) *ptr; \
if (M_UNLIKELY (data == NULL)) { \
*shared = NULL; \
return; \
} \
ptr = M_CALL_NEW(oplist, struct M_C(name, _s)); \
if (M_UNLIKELY (ptr == NULL)) { \
M_MEMORY_FULL(sizeof(struct M_C(name, _s))); \
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; \
@@ -209,17 +218,17 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
} \
\
M_IF_METHOD(INIT, oplist)( \
static inline void \
M_C(name, _init_new)(shared_t shared) \
M_INLINE void \
M_F(name, _init_new)(shared_t shared) \
{ \
/* NOTE: Alloc 1 struct with both structures. */ \
struct M_C(name, combine_s) *p = \
M_CALL_NEW(oplist, struct M_C(name, combine_s)); \
if (M_UNLIKELY (p == NULL)) { \
M_MEMORY_FULL(sizeof(struct M_C(name, combine_s))); \
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_C(name, _s) *ptr = &p->ptr; \
struct M_F(name, _s) *ptr = &p->ptr; \
type *data = &p->data; \
M_CALL_INIT( oplist, *data); \
ptr->data = data; \
@@ -230,15 +239,15 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
} \
, /* No INIT */ ) \
\
static inline bool \
M_C(name, _NULL_p)(const shared_t shared) \
M_INLINE bool \
M_F(name, _NULL_p)(const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
return *shared == NULL; \
} \
\
static inline void \
M_C(name, _init_set)(shared_t dest, \
M_INLINE void \
M_F(name, _init_set)(shared_t dest, \
const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
@@ -251,8 +260,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
} \
\
static inline void \
M_C(name, _clear)(shared_t dest) \
M_INLINE void \
M_F(name, _clear)(shared_t dest) \
{ \
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
if (*dest != NULL) { \
@@ -272,25 +281,25 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
} \
\
static inline void \
M_C(name, _reset)(shared_t dest) \
M_INLINE void \
M_F(name, _reset)(shared_t dest) \
{ \
/* NOTE: Clear will also set dest to NULL */ \
M_C(name, _clear)(dest); \
M_F(name, _clear)(dest); \
} \
\
static inline void \
M_C(name, _set)(shared_t 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_C(name, _clear)(dest); \
M_C(name, _init_set)(dest, shared); \
M_F(name, _clear)(dest); \
M_F(name, _init_set)(dest, shared); \
} \
\
static inline void \
M_C(name, _init_move)(shared_t dest, \
M_INLINE void \
M_F(name, _init_move)(shared_t dest, \
shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
@@ -300,31 +309,31 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
M_SHAR3D_CONTRACT(dest, cpt_oplist); \
} \
\
static inline void \
M_C(name, _move)(shared_t dest, \
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_C(name, _clear)(dest); \
M_C(name, _init_move)(dest, shared); \
M_F(name, _clear)(dest); \
M_F(name, _init_move)(dest, shared); \
} \
\
static inline void \
M_C(name, _swap)(shared_t p1, \
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_C(name, _s)*, *p1, *p2); \
M_SWAP (struct M_F(name, _s)*, *p1, *p2); \
M_SHAR3D_CONTRACT(p1, cpt_oplist); \
M_SHAR3D_CONTRACT(p2, cpt_oplist); \
} \
\
static inline bool \
M_C(name, _equal_p)(const shared_t p1, \
M_INLINE bool \
M_F(name, _equal_p)(const shared_t p1, \
const shared_t p2) \
{ \
M_SHAR3D_CONTRACT(p1, cpt_oplist); \
@@ -332,8 +341,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
return *p1 == *p2; \
} \
\
static inline type const * \
M_C(name, _cref)(const shared_t shared) \
M_INLINE type const * \
M_F(name, _cref)(const shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT(*shared != NULL); \
@@ -342,8 +351,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
return M_CONST_CAST (type, data); \
} \
\
static inline type * \
M_C(name, _ref)(shared_t shared) \
M_INLINE type * \
M_F(name, _ref)(shared_t shared) \
{ \
M_SHAR3D_CONTRACT(shared, cpt_oplist); \
M_ASSERT(*shared != NULL); \
@@ -351,10 +360,9 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
M_ASSERT (data != NULL); \
return data; \
} \
/********************************** SHARED RESOURCE ************************************/
/********************************** INTERNAL *********************************/
#define M_SHAR3D_RESOURCE_CONTRACT(s) do { \
M_ASSERT (s != NULL); \
@@ -373,35 +381,43 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
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_C(name, _atype_s) { \
typedef struct M_F(name, _atype_s) { \
atomic_uint cpt; \
type x; \
M_CACHELINE_ALIGN(align, type, atomic_uint); \
} M_C(name, _atype_ct); \
} M_F(name, _atype_ct); \
\
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
m_genint_t core; \
M_C(name, _atype_ct) *buffer; \
M_F(name, _atype_ct) *buffer; \
} shared_t[1]; \
\
typedef struct M_C(name, _it_s) { \
typedef struct M_F(name, _it_s) { \
unsigned int idx; \
struct M_C(name, _s) *ref; \
struct M_F(name, _s) *ref; \
} it_t[1]; \
\
/* Internal Types for oplist */ \
typedef shared_t M_C(name, _ct); \
\
static inline void \
M_C(name, _init)(shared_t s, size_t n) \
typedef shared_t M_F(name, _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_C(name, _atype_ct), NULL, n); \
if (M_UNLIKELY (s->buffer == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _atype_ct)) * n); \
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++) { \
@@ -412,8 +428,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
M_SHAR3D_RESOURCE_CONTRACT(s); \
} \
\
static inline void \
M_C(name, _clear)(shared_t s) \
M_INLINE void \
M_F(name, _clear)(shared_t s) \
{ \
M_SHAR3D_RESOURCE_CONTRACT(s); \
size_t n = m_genint_size(s->core); \
@@ -425,8 +441,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
m_genint_clear(s->core); \
} \
\
static inline void \
M_C(name, _it)(it_t it, shared_t s) \
M_INLINE void \
M_F(name, _it)(it_t it, shared_t s) \
{ \
M_SHAR3D_RESOURCE_CONTRACT(s); \
M_ASSERT (it != NULL); \
@@ -439,31 +455,31 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
} \
} \
\
static inline bool \
M_C(name, _end_p)(it_t it) \
M_INLINE bool \
M_F(name, _end_p)(it_t it) \
{ \
M_ASSERT (it != NULL); \
return it->idx == M_GENINT_ERROR; \
} \
\
static inline type * \
M_C(name, _ref)(it_t it) \
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; \
} \
\
static inline type const * \
M_C(name, _cref)(it_t it) \
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); \
} \
\
static inline void \
M_C(name, _end)(it_t it, shared_t s) \
M_INLINE void \
M_F(name, _end)(it_t it, shared_t s) \
{ \
M_SHAR3D_RESOURCE_CONTRACT(s); \
M_ASSERT (it != NULL); \
@@ -478,8 +494,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
} \
} \
\
static inline void \
M_C(name, _it_set)(it_t itd, it_t its) \
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); \
@@ -494,6 +510,8 @@ static inline int m_shar3d_integer_cref(int *p) { return *p; }
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
+137 -114
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - SNAPSHOT Module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -34,7 +34,7 @@ M_BEGIN_PROTECTED_CODE
/* Define a Single Producer Single Consummer snapshot and its functions
USAGE: SNAPSHOT_SPSC_DEF(name, type[, oplist]) */
#define M_SNAPSHOT_SPSC_DEF(name, ...) \
M_SNAPSHOT_SPSC_DEF_AS(name, M_C(name,_t), __VA_ARGS__)
M_SNAPSHOT_SPSC_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define a Single Producer Single Consummer snapshot and its functions
@@ -51,7 +51,7 @@ M_BEGIN_PROTECTED_CODE
/* Define a Single Producer Multiple Consummer snapshot and its functions
USAGE: SNAPSHOT_SPMC_DEF(name, type[, oplist]) */
#define M_SNAPSHOT_SPMC_DEF(name, ...) \
M_SNAPSHOT_SPMC_DEF_AS(name, M_C(name,_t), __VA_ARGS__)
M_SNAPSHOT_SPMC_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define a Single Producer Multiple Consummer snapshot and its functions
@@ -68,7 +68,7 @@ M_BEGIN_PROTECTED_CODE
/* Define a Multiple Producer Multiple Consummer snapshot and its functions
USAGE: SNAPSHOT_MPMC_DEF(name, type[, oplist]) */
#define M_SNAPSHOT_MPMC_DEF(name, ...) \
M_SNAPSHOT_MPMC_DEF_AS(name, M_C(name,_t), __VA_ARGS__)
M_SNAPSHOT_MPMC_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define a Multiple Producer Multiple Consummer snapshot and its functions
@@ -90,7 +90,9 @@ M_BEGIN_PROTECTED_CODE
(__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
// deferred evaluation of the input
#define M_SNAPSH0T_OPLIST_P1(arg) M_SNAPSH0T_OPLIST_P2 arg
@@ -105,20 +107,20 @@ M_BEGIN_PROTECTED_CODE
/* Define the oplist of a snapshot */
#define M_SNAPSH0T_OPLIST_P3(name, oplist) \
(INIT(M_C(name, _init)) \
,INIT_SET(M_C(name, _init_set)) \
,SET(M_C(name, _set)) \
,CLEAR(M_C(name, _clear)) \
(INIT(M_F(name, _init)) \
,INIT_SET(M_F(name, _init_set)) \
,SET(M_F(name, _set)) \
,CLEAR(M_F(name, _clear)) \
,NAME(name) \
,TYPE(M_C(name, _ct)) \
,SUBTYPE(M_C(name, _subtype_ct)) \
,TYPE(M_F(name, _ct)) \
,SUBTYPE(M_F(name, _subtype_ct)) \
,OPLIST(oplist) \
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_C(name, _init_move)),) \
,M_IF_METHOD(MOVE, oplist)(MOVE(M_C(name, _move)),) \
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \
,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \
)
/******************************** INTERNAL SPSC **********************************/
/********************************** INTERNAL *********************************/
/* Flag defining the atomic state of a snapshot:
* - r: Index of the read buffer Range [0..2]
@@ -168,28 +170,35 @@ M_BEGIN_PROTECTED_CODE
/* Expand the type and the functions of a SPSC snapshot */
#define M_SNAPSH0T_SPSC_DEF_P3(name, type, oplist, snapshot_t) \
M_SNAPSH0T_SPSC_DEF_TYPE(name, type, oplist, snapshot_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_SNAPSH0T_SPSC_DEF_CORE(name, type, oplist, snapshot_t) \
/* Define the type */
#define M_SNAPSH0T_SPSC_DEF_TYPE(name, type, oplist, snapshot_t) \
\
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _aligned_type_s) { \
typedef struct M_F(name, _aligned_type_s) { \
type x; \
M_CACHELINE_ALIGN(align, type); \
} M_C(name, _aligned_type_ct); \
} M_F(name, _aligned_type_ct); \
\
typedef struct M_C(name, _s) { \
M_C(name, _aligned_type_ct) data[M_SNAPSH0T_SPSC_MAX_BUFFER]; \
typedef struct M_F(name, _s) { \
M_F(name, _aligned_type_ct) data[M_SNAPSH0T_SPSC_MAX_BUFFER]; \
atomic_uchar flags; \
} snapshot_t[1]; \
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
\
/* Define internal types for oplist */ \
typedef snapshot_t M_C(name, _ct); \
typedef type M_C(name, _subtype_ct); \
typedef snapshot_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
/* Define the core functions */
#define M_SNAPSH0T_SPSC_DEF_CORE(name, type, oplist, snapshot_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(snapshot_t snap) \
M_INLINE void \
M_F(name, _init)(snapshot_t snap) \
{ \
M_ASSERT(snap != NULL); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
@@ -199,8 +208,8 @@ M_BEGIN_PROTECTED_CODE
M_SNAPSH0T_SPSC_CONTRACT(snap); \
} \
\
static inline void \
M_C(name, _clear)(snapshot_t snap) \
M_INLINE void \
M_F(name, _clear)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
for(int i = 0; i < M_SNAPSH0T_SPSC_MAX_BUFFER; i++) { \
@@ -209,8 +218,8 @@ M_BEGIN_PROTECTED_CODE
} \
\
/* const is missing for org due to use of atomic_load of org */ \
static inline void \
M_C(name, _init_set)(snapshot_t snap, snapshot_t org) \
M_INLINE void \
M_F(name, _init_set)(snapshot_t snap, snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(org); \
M_ASSERT(snap != NULL && snap != org); \
@@ -222,8 +231,8 @@ M_BEGIN_PROTECTED_CODE
} \
\
/* const is missing for org due to use of atomic_load of org */ \
static inline void \
M_C(name, _set)(snapshot_t snap, snapshot_t org) \
M_INLINE void \
M_F(name, _set)(snapshot_t snap, snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
M_SNAPSH0T_SPSC_CONTRACT(org); \
@@ -235,8 +244,8 @@ M_BEGIN_PROTECTED_CODE
} \
\
M_IF_METHOD(INIT_MOVE, oplist)( \
static inline void \
M_C(name, _init_move)(snapshot_t snap, snapshot_t org) \
M_INLINE void \
M_F(name, _init_move)(snapshot_t snap, snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(org); \
M_ASSERT(snap != NULL && snap != org); \
@@ -250,8 +259,8 @@ M_BEGIN_PROTECTED_CODE
,) /* IF_METHOD (INIT_MOVE) */ \
\
M_IF_METHOD(MOVE, oplist)( \
static inline void \
M_C(name, _move)(snapshot_t snap, \
M_INLINE void \
M_F(name, _move)(snapshot_t snap, \
snapshot_t org) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
@@ -266,8 +275,8 @@ M_BEGIN_PROTECTED_CODE
} \
,) /* IF_METHOD (MOVE) */ \
\
static inline type * \
M_C(name, _write)(snapshot_t snap) \
M_INLINE type * \
M_F(name, _write)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \
@@ -285,8 +294,8 @@ M_BEGIN_PROTECTED_CODE
return &snap->data[M_SNAPSH0T_SPSC_W(nextFlags)].x; \
} \
\
static inline type const * \
M_C(name, _read)(snapshot_t snap) \
M_INLINE type const * \
M_F(name, _read)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \
@@ -310,8 +319,8 @@ M_BEGIN_PROTECTED_CODE
} \
\
/* Non const due to use of atomic_load */ \
static inline bool \
M_C(name, _updated_p)(snapshot_t snap) \
M_INLINE bool \
M_F(name, _updated_p)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
unsigned char flags = atomic_load (&snap->flags); \
@@ -319,8 +328,8 @@ M_BEGIN_PROTECTED_CODE
} \
\
/* Non const due to use of atomic_load */ \
static inline type * \
M_C(name, _get_write_buffer)(snapshot_t snap) \
M_INLINE type * \
M_F(name, _get_write_buffer)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
unsigned char flags = atomic_load(&snap->flags); \
@@ -328,8 +337,8 @@ M_BEGIN_PROTECTED_CODE
} \
\
/* Non const due to use of atomic_load */ \
static inline type const * \
M_C(name, _get_read_buffer)(snapshot_t snap) \
M_INLINE type const * \
M_F(name, _get_read_buffer)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPSC_CONTRACT(snap); \
unsigned char flags = atomic_load(&snap->flags); \
@@ -337,8 +346,7 @@ M_BEGIN_PROTECTED_CODE
} \
/******************************** INTERNAL SPMC **********************************/
/********************************** INTERNAL *********************************/
#define M_SNAPSH0T_SPMC_INT_FLAG(w, n) ( ((w) << 1) | (n) )
#define M_SNAPSH0T_SPMC_INT_FLAG_W(f) ((f) >> 1)
@@ -374,7 +382,7 @@ typedef struct m_snapsh0t_mrsw_s {
} while (0)
/* Initialize m_snapsh0t_mrsw_ct for n readers (constructor) */
static inline void
M_INLINE void
m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n)
{
M_ASSERT (s != NULL);
@@ -384,7 +392,7 @@ m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n)
// Initialize the counters to zero (no reader use it)
atomic_uint *ptr = M_MEMORY_REALLOC (atomic_uint, NULL, n);
if (M_UNLIKELY (ptr == NULL)) {
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
M_MEMORY_FULL(sizeof (atomic_uint) * n);
return;
}
@@ -407,7 +415,7 @@ m_snapsh0t_mrsw_init(m_snapsh0t_mrsw_ct s, size_t n)
}
/* Clear m_snapsh0t_mrsw_ct (destructor) */
static inline void
M_INLINE void
m_snapsh0t_mrsw_clear(m_snapsh0t_mrsw_ct s)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -418,7 +426,7 @@ m_snapsh0t_mrsw_clear(m_snapsh0t_mrsw_ct s)
}
/* Return the current index that is written in the buffer */
static inline unsigned int
M_INLINE unsigned int
m_snapsh0t_mrsw_get_write_idx(m_snapsh0t_mrsw_ct s)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -426,7 +434,7 @@ m_snapsh0t_mrsw_get_write_idx(m_snapsh0t_mrsw_ct s)
}
/* Return the number of readers */
static inline unsigned int
M_INLINE unsigned int
m_snapsh0t_mrsw_size(m_snapsh0t_mrsw_ct s)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -435,7 +443,7 @@ m_snapsh0t_mrsw_size(m_snapsh0t_mrsw_ct s)
/* Give the current index that is written to the readers,
and return new available index for the writer thread */
static inline unsigned int
M_INLINE unsigned int
m_snapsh0t_mrsw_write_idx(m_snapsh0t_mrsw_ct s, unsigned int idx)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -477,7 +485,7 @@ m_snapsh0t_mrsw_write_idx(m_snapsh0t_mrsw_ct s, unsigned int idx)
}
/* Perform a swap of the current write buffer and return a new one */
static inline unsigned int
M_INLINE unsigned int
m_snapsh0t_mrsw_write(m_snapsh0t_mrsw_ct s)
{
s->currentWrite = m_snapsh0t_mrsw_write_idx(s, s->currentWrite);
@@ -486,7 +494,7 @@ m_snapsh0t_mrsw_write(m_snapsh0t_mrsw_ct s)
}
/* Start writing to the write buffer and return its index */
static inline unsigned int
M_INLINE unsigned int
m_snapsh0t_mrsw_write_start(m_snapsh0t_mrsw_ct s)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -501,7 +509,7 @@ m_snapsh0t_mrsw_write_start(m_snapsh0t_mrsw_ct s)
}
/* End writing to the given write buffer */
static inline void
M_INLINE void
m_snapsh0t_mrsw_write_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -523,7 +531,7 @@ m_snapsh0t_mrsw_write_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
}
/* Start reading the latest written buffer and return the index to it */
static inline unsigned int
M_INLINE unsigned int
m_snapsh0t_mrsw_read_start(m_snapsh0t_mrsw_ct s)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -566,7 +574,7 @@ m_snapsh0t_mrsw_read_start(m_snapsh0t_mrsw_ct s)
}
/* End the reading the given buffer */
static inline void
M_INLINE void
m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
{
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
@@ -582,6 +590,9 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
M_SNAPSH0T_SPMC_INT_CONTRACT(s);
}
/********************************** INTERNAL *********************************/
/* Contract of a SPMC snapshot.
Nothing notable as it can be accessed concurrently */
#define M_SNAPSH0T_SPMC_CONTRACT(snap) do { \
@@ -603,33 +614,40 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
/* Expand the type and the functions of a SPMC snapshot */
#define M_SNAPSH0T_SPMC_DEF_P3(name, type, oplist, snapshot_t) \
M_SNAPSH0T_SPMC_DEF_TYPE(name, type, oplist, snapshot_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_SNAPSH0T_SPMC_DEF_CORE(name, type, oplist, snapshot_t) \
/* Define the type */
#define M_SNAPSH0T_SPMC_DEF_TYPE(name, type, oplist, snapshot_t) \
\
/* Create an aligned type to avoid false sharing between threads */ \
typedef struct M_C(name, _aligned_type_s) { \
typedef struct M_F(name, _aligned_type_s) { \
type x; \
M_CACHELINE_ALIGN(align, type); \
} M_C(name, _aligned_type_ct); \
} M_F(name, _aligned_type_ct); \
\
typedef struct M_C(name, _s) { \
M_C(name, _aligned_type_ct) *data; \
typedef struct M_F(name, _s) { \
M_F(name, _aligned_type_ct) *data; \
m_snapsh0t_mrsw_ct core; \
} snapshot_t[1]; \
\
/* Define internal types for oplist */ \
typedef snapshot_t M_C(name, _ct); \
typedef type M_C(name, _subtype_ct); \
typedef snapshot_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
/* Define the core functions */
#define M_SNAPSH0T_SPMC_DEF_CORE(name, type, oplist, snapshot_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(snapshot_t snap, size_t nReader) \
M_INLINE void \
M_F(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_C(name, _aligned_type_ct), \
snap->data = M_CALL_REALLOC(oplist, M_F(name, _aligned_type_ct), \
NULL, nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER); \
if (M_UNLIKELY (snap->data == NULL)) { \
M_MEMORY_FULL(sizeof(M_C(name, _aligned_type_ct)) * \
if (M_UNLIKELY_NOMEM (snap->data == NULL)) { \
M_MEMORY_FULL(sizeof(M_F(name, _aligned_type_ct)) * \
(nReader+M_SNAPSH0T_SPMC_EXTRA_BUFFER)); \
return; \
} \
@@ -640,8 +658,8 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
M_SNAPSH0T_SPMC_CONTRACT(snap); \
} \
\
static inline void \
M_C(name, _clear)(snapshot_t snap) \
M_INLINE void \
M_F(name, _clear)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
size_t nReader = m_snapsh0t_mrsw_size(snap->core); \
@@ -652,29 +670,29 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
m_snapsh0t_mrsw_clear(snap->core); \
} \
\
static inline type * \
M_C(name, _write)(snapshot_t snap) \
M_INLINE type * \
M_F(name, _write)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
const unsigned int idx = m_snapsh0t_mrsw_write(snap->core); \
return &snap->data[idx].x; \
} \
\
static inline type const * \
M_C(name, _read_start)(snapshot_t snap) \
M_INLINE type const * \
M_F(name, _read_start)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
const unsigned int idx = m_snapsh0t_mrsw_read_start(snap->core); \
return M_CONST_CAST(type, &snap->data[idx].x); \
} \
\
static inline void \
M_C(name, _read_end)(snapshot_t snap, type const *old) \
M_INLINE void \
M_F(name, _read_end)(snapshot_t snap, type const *old) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
M_ASSERT (old != NULL); \
const M_C(name, _aligned_type_ct) *oldx; \
oldx = M_CTYPE_FROM_FIELD(M_C(name, _aligned_type_ct), old, type, x); \
const M_F(name, _aligned_type_ct) *oldx; \
oldx = M_CTYPE_FROM_FIELD(M_F(name, _aligned_type_ct), old, type, x); \
M_ASSERT (oldx >= snap->data); \
M_ASSERT (oldx < snap->data + snap->core->n_reader + M_SNAPSH0T_SPMC_EXTRA_BUFFER); \
M_ASSERT(snap->core->n_reader +M_SNAPSH0T_SPMC_EXTRA_BUFFER < UINT_MAX); \
@@ -682,8 +700,8 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
m_snapsh0t_mrsw_read_end(snap->core, idx); \
} \
\
static inline type * \
M_C(name, _get_write_buffer)(snapshot_t snap) \
M_INLINE type * \
M_F(name, _get_write_buffer)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap); \
const unsigned int idx = m_snapsh0t_mrsw_get_write_idx(snap->core); \
@@ -691,11 +709,8 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
} \
\
//FIXME: Evaluate the needs for the methods _set_, _init_set.
/******************************** INTERNAL MPMC **********************************/
/********************************** INTERNAL *********************************/
// MPMC is built upon SPMC
@@ -712,48 +727,54 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
/* Expand the type and the functions of a MPMC snapshot */
#define M_SNAPSH0T_MPMC_DEF_P3(name, type, oplist, snapshot_t) \
M_SNAPSH0T_SPMC_DEF_P1((M_F(name, _mrsw), type, oplist, M_F(name, _mrsw_pct))) \
M_SNAPSH0T_MPMC_DEF_TYPE(name, type, oplist, snapshot_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_SNAPSH0T_MPMC_DEF_CORE(name, type, oplist, snapshot_t) \
/* Define the types */
#define M_SNAPSH0T_MPMC_DEF_TYPE(name, type, oplist, snapshot_t) \
\
M_SNAPSH0T_SPMC_DEF_P1((M_C(name, _mrsw), type, oplist, M_C(name, _mrsw_pct))) \
\
typedef struct M_C(name, _s) { \
M_C(name, _mrsw_pct) core; \
typedef struct M_F(name, _s) { \
M_F(name, _mrsw_pct) core; \
} snapshot_t[1]; \
\
/* Define internal types for oplist */ \
typedef snapshot_t M_C(name, _ct); \
typedef type M_C(name, _subtype_ct); \
typedef snapshot_t M_F(name, _ct); \
typedef type M_F(name, _subtype_ct); \
/* Define the core functions */
#define M_SNAPSH0T_MPMC_DEF_CORE(name, type, oplist, snapshot_t) \
\
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
\
static inline void \
M_C(name, _init)(snapshot_t snap, size_t nReader, size_t nWriter) \
M_INLINE void \
M_F(name, _init)(snapshot_t snap, size_t nReader, size_t nWriter) \
{ \
M_C(name, _mrsw_init)(snap->core, nReader + nWriter -1 ); \
M_F(name, _mrsw_init)(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); \
} \
\
static inline void \
M_C(name, _clear)(snapshot_t snap) \
M_INLINE void \
M_F(name, _clear)(snapshot_t snap) \
{ \
M_C(name, _mrsw_clear)(snap->core); \
M_F(name, _mrsw_clear)(snap->core); \
} \
\
static inline type * \
M_C(name, _write_start)(snapshot_t snap) \
M_INLINE type * \
M_F(name, _write_start)(snapshot_t snap) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap->core); \
const unsigned int idx = m_snapsh0t_mrsw_write_start(snap->core->core); \
return &snap->core->data[idx].x; \
} \
\
static inline void \
M_C(name, _write_end)(snapshot_t snap, type *old) \
M_INLINE void \
M_F(name, _write_end)(snapshot_t snap, type *old) \
{ \
M_SNAPSH0T_SPMC_CONTRACT(snap->core); \
const M_C(name, _mrsw_aligned_type_ct) *oldx; \
oldx = M_CTYPE_FROM_FIELD(M_C(name, _mrsw_aligned_type_ct), old, type, x); \
const M_F(name, _mrsw_aligned_type_ct) *oldx; \
oldx = M_CTYPE_FROM_FIELD(M_F(name, _mrsw_aligned_type_ct), old, type, x); \
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); \
@@ -761,16 +782,16 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
m_snapsh0t_mrsw_write_end(snap->core->core, idx); \
} \
\
static inline type const * \
M_C(name, _read_start)(snapshot_t snap) \
M_INLINE type const * \
M_F(name, _read_start)(snapshot_t snap) \
{ \
return M_C(name, _mrsw_read_start)(snap->core); \
return M_F(name, _mrsw_read_start)(snap->core); \
} \
\
static inline void \
M_C(name, _read_end)(snapshot_t snap, type const *old) \
M_INLINE void \
M_F(name, _read_end)(snapshot_t snap, type const *old) \
{ \
M_C(name, _mrsw_read_end)(snap->core, old); \
M_F(name, _mrsw_read_end)(snap->core, old); \
} \
\
@@ -778,6 +799,8 @@ m_snapsh0t_mrsw_read_end(m_snapsh0t_mrsw_ct s, unsigned int idx)
M_END_PROTECTED_CODE
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define SNAPSHOT_SPSC_DEF M_SNAPSHOT_SPSC_DEF
#define SNAPSHOT_SPSC_DEF_AS M_SNAPSHOT_SPSC_DEF_AS
+312 -217
View File
File diff suppressed because it is too large. Load diff
+748
View File
@@ -0,0 +1,748 @@
/*
* M*LIB - Thin Mutex & Thread wrapper
*
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_MUTEX_H
#define MSTARLIB_MUTEX_H
/* Auto-detect the thread backend to use if the user has not override it */
#ifndef M_USE_THREAD_BACKEND
# if defined(INC_FREERTOS_H)
# define M_USE_THREAD_BACKEND 4
# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
&& !defined(__STDC_NO_THREADS__)
# define M_USE_THREAD_BACKEND 1
# elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
# define M_USE_THREAD_BACKEND 2
# else
# define M_USE_THREAD_BACKEND 3
# endif
#endif
/****************************** C11 version ********************************/
#if M_USE_THREAD_BACKEND == 1
#include <threads.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
/* Define a mutex type based on C11 definition */
typedef mtx_t m_mutex_t[1];
/* Define a condition variable type based on C11 definition */
typedef cnd_t m_cond_t[1];
/* Define a thread type based on C11 definition */
typedef thrd_t m_thread_t[1];
/* Initialize the mutex (constructor) */
M_INLINE void m_mutex_init(m_mutex_t m)
{
int rc = mtx_init(m, mtx_plain);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (rc == thrd_success, "mutex");
}
/* Clear the mutex (destructor) */
M_INLINE void m_mutex_clear(m_mutex_t m)
{
mtx_destroy(m);
}
/* Lock the mutex */
M_INLINE void m_mutex_lock(m_mutex_t m)
{
mtx_lock(m);
}
/* Unlock the mutex */
M_INLINE void m_mutex_unlock(m_mutex_t m)
{
mtx_unlock(m);
}
/* Initialize the condition variable (constructor) */
M_INLINE void m_cond_init(m_cond_t c)
{
int rc = cnd_init(c);
// Abort program in case of initialization failure
// There is really nothing else to do if the object cannot be constructed
M_ASSERT_INIT (rc == thrd_success, "conditional variable");
}
/* Clear the condition variable (destructor) */
M_INLINE void m_cond_clear(m_cond_t c)
{
cnd_destroy(c);
}
/* Signal the condition variable to at least one waiting thread */
M_INLINE void m_cond_signal(m_cond_t c)
{
cnd_signal(c);
}
/* Signal the condition variable to all waiting threads */
M_INLINE void m_cond_broadcast(m_cond_t c)
{
cnd_broadcast(c);
}
/* Wait for signaling the condition variable by another thread */
M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m)
{
cnd_wait(c, m);
}
/* Create the thread (constructor) and start it */
M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
{
int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
// Abort program in case of initialization failure
M_ASSERT_INIT (rc == thrd_success, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
M_INLINE void m_thread_join(m_thread_t t)
{
int rc = thrd_join(*t, NULL);
M_ASSERT (rc == thrd_success);
// Avoid warning about variable unused.
(void) rc;
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
M_INLINE void m_thread_yield(void)
{
thrd_yield();
}
/* Sleep the thread for at least usec microseconds.
Return true if the sleep was successful (or we cannot know) */
M_INLINE bool m_thread_sleep(unsigned long long usec)
{
struct timespec tv;
tv.tv_sec = (long) (usec / 1000000ULL);
tv.tv_nsec = (long) ((usec % 1000000ULL) * 1000UL);
int retval = thrd_sleep(&tv, NULL);
return retval == 0;
}
// a helper structure for m_once_call
typedef once_flag m_once_t[1];
// Initial value for m_once_t
#define M_ONCE_INIT_VALUE { ONCE_FLAG_INIT }
// Call the function exactly once
M_INLINE void m_once_call(m_once_t o, void (*func)(void))
{
call_once(o,func);
}
// Attribute to use to allocate a global variable to a thread.
#define M_THREAD_ATTR _Thread_local
M_END_PROTECTED_CODE
/****************************** WIN32 version ******************************/
#elif M_USE_THREAD_BACKEND == 2
/* CLANG provides some useless and wrong warnings:
* - _WIN32_WINNT starts with '_' which is reserved by the standard
* as per the MSVC compiler, it is needed to be defined by the user
* to define which version of windows it want to be compatible with.
* - windows.h may be different than the case used by the file sytem
* there is however no normalized case.
*
* So, theses warnings have to be ignored and are disabled.
*
* We cannot add theses warnings in M_BEGIN_PROTECTED_CODE
* as they need to be disabled **BEFORE** including any system header
* and m-core includes some system headers.
* So we need to disable them explictly here.
*/
#if defined(__clang__) && __clang_major__ >= 4
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"")
_Pragma("clang diagnostic ignored \"-Wnonportable-system-include-path\"")
#endif
/* CriticalSection & ConditionVariable are available from Windows Vista */
#ifndef WINVER
#define WINVER _WIN32_WINNT_VISTA
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif
/* Include system headers */
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
#if defined(__clang__) && __clang_major__ >= 4
_Pragma("clang diagnostic pop")
#endif
M_BEGIN_PROTECTED_CODE
/* Define a thread type based on WINDOWS definition */
typedef HANDLE m_thread_t[1];
/* Define a mutex type based on WINDOWS definition */
typedef CRITICAL_SECTION m_mutex_t[1];
/* Define a condition variable type based on WINDOWS definition */
typedef CONDITION_VARIABLE m_cond_t[1];
/* Initialize a mutex (Constructor)*/
M_INLINE void m_mutex_init(m_mutex_t m)
{
InitializeCriticalSection(m);
}
/* Clear a mutex (destructor) */
M_INLINE void m_mutex_clear(m_mutex_t m)
{
DeleteCriticalSection(m);
}
/* Lock a mutex */
M_INLINE void m_mutex_lock(m_mutex_t m)
{
EnterCriticalSection(m);
}
/* Unlock a mutex */
M_INLINE void m_mutex_unlock(m_mutex_t m)
{
LeaveCriticalSection(m);
}
/* Initialize a condition variable (constructor) */
M_INLINE void m_cond_init(m_cond_t c)
{
InitializeConditionVariable(c);
}
/* Clear a condition variable (destructor) */
M_INLINE void m_cond_clear(m_cond_t c)
{
(void) c; // There is no destructor for this object.
}
/* Signal a condition variable to at least one waiting thread */
M_INLINE void m_cond_signal(m_cond_t c)
{
WakeConditionVariable(c);
}
/* Signal a condition variable to all waiting threads */
M_INLINE void m_cond_broadcast(m_cond_t c)
{
WakeAllConditionVariable(c);
}
/* Wait for a condition variable */
M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m)
{
SleepConditionVariableCS(c, m, INFINITE);
}
/* Create a thread (constructor) and start it */
M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (uintptr_t) func, arg, 0, NULL);
M_ASSERT_INIT (*t != NULL, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
M_INLINE void m_thread_join(m_thread_t t)
{
DWORD dwWaitResult = WaitForSingleObject(*t, INFINITE);
(void) dwWaitResult;
M_ASSERT (dwWaitResult == WAIT_OBJECT_0);
CloseHandle(*t);
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
M_INLINE void m_thread_yield(void)
{
Sleep(0);
}
/* Sleep the thread for at least usec microseconds
Return true if the sleep was successful */
M_INLINE bool m_thread_sleep(unsigned long long usec)
{
LARGE_INTEGER ft;
M_ASSERT (usec <= LLONG_MAX);
ft.QuadPart = -(10LL*(long long) usec);
HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
M_ASSERT_INIT (hd != NULL, "timer");
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
CloseHandle(hd);
return dwWaitResult == WAIT_OBJECT_0;
}
typedef INIT_ONCE m_once_t[1];
#define M_ONCE_INIT_VALUE { INIT_ONCE_STATIC_INIT }
M_INLINE BOOL CALLBACK m_once_callback( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
{
void (*func)(void);
(void) InitOnce;
(void) lpContext;
func = (void (*)(void))(uintptr_t) Parameter;
(*func)();
return TRUE;
}
M_INLINE void m_once_call(m_once_t o, void (*func)(void))
{
InitOnceExecuteOnce(o, m_once_callback, (void*)(intptr_t)func, NULL);
}
#if defined(_MSC_VER)
// Attribute to use to allocate a global variable to a thread (MSVC def).
# define M_THREAD_ATTR __declspec( thread )
#else
// Attribute to use to allocate a global variable to a thread (GCC def).
# define M_THREAD_ATTR __thread
#endif
M_END_PROTECTED_CODE
/**************************** PTHREAD version ******************************/
#elif M_USE_THREAD_BACKEND == 3
#include <pthread.h>
#ifdef _POSIX_PRIORITY_SCHEDULING
#include <sched.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
/* Define a mutex type based on PTHREAD definition */
typedef pthread_mutex_t m_mutex_t[1];
/* Define a condition variable type based on PTHREAD definition */
typedef pthread_cond_t m_cond_t[1];
/* Define a thread type based on PTHREAD definition */
typedef pthread_t m_thread_t[1];
/* Initialize the mutex (constructor) */
M_INLINE void m_mutex_init(m_mutex_t m)
{
int _rc = pthread_mutex_init(m, NULL);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (_rc == 0, "mutex");
}
/* Clear the mutex (destructor) */
M_INLINE void m_mutex_clear(m_mutex_t m)
{
pthread_mutex_destroy(m);
}
/* Lock the mutex */
M_INLINE void m_mutex_lock(m_mutex_t m)
{
pthread_mutex_lock(m);
}
/* Unlock the mutex */
M_INLINE void m_mutex_unlock(m_mutex_t m)
{
pthread_mutex_unlock(m);
}
/* Lazy lock initialization */
#define M_MUTEXI_INIT_VALUE { PTHREAD_MUTEX_INITIALIZER }
/* Internal function compatible with lazy lock */
M_INLINE void m_mutexi_lazy_lock(m_mutex_t m)
{
pthread_mutex_lock(m);
}
/* Initialize the condition variable (constructor) */
M_INLINE void m_cond_init(m_cond_t c)
{
int _rc = pthread_cond_init(c, NULL);
// Abort program in case of initialization failure
// There is really nothing else to do if a mutex cannot be constructed
M_ASSERT_INIT (_rc == 0, "conditional variable");
}
/* Clear the condition variable (destructor) */
M_INLINE void m_cond_clear(m_cond_t c)
{
pthread_cond_destroy(c);
}
/* Signal a condition variable to at least a waiting thread */
M_INLINE void m_cond_signal(m_cond_t c)
{
pthread_cond_signal(c);
}
/* Signal a condition variable to all waiting threads */
M_INLINE void m_cond_broadcast(m_cond_t c)
{
pthread_cond_broadcast(c);
}
/* Waiting for a condition variable */
M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m)
{
pthread_cond_wait(c, m);
}
/* Create a thread (constructor) and start it */
M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
{
int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
M_ASSERT_INIT (_rc == 0, "thread");
}
/* Wait for the thread to terminate and destroy it (destructor) */
M_INLINE void m_thread_join(m_thread_t t)
{
int _rc = pthread_join(*t, NULL);
(void)_rc; // Avoid warning about variable unused.
M_ASSERT (_rc == 0);
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
M_INLINE void m_thread_yield(void)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield();
#endif
}
/* Sleep for at least usec microseconds
Return true if the sleep was successful */
M_INLINE bool m_thread_sleep(unsigned long long usec)
{
struct timeval tv;
/* We don't want to use usleep or nanosleep so that
we remain compatible with strict C99 build */
tv.tv_sec = (time_t) (usec / 1000000ULL);
tv.tv_usec = (suseconds_t) (usec % 1000000ULL);
int retval = select(1, NULL, NULL, NULL, &tv);
return retval == 0;
}
typedef pthread_once_t m_once_t[1];
#define M_ONCE_INIT_VALUE { PTHREAD_ONCE_INIT }
M_INLINE void m_once_call(m_once_t o, void (*func)(void))
{
pthread_once(o,func);
}
#if defined(__GNUC__)
# define M_THREAD_ATTR __thread
#else
# define M_THREAD_ATTR /* Not supported */
#endif
M_END_PROTECTED_CODE
/****************************** FreeRTOS version ********************************/
#elif M_USE_THREAD_BACKEND == 4
#include <stdatomic.h>
#include <semphr.h>
#include <task.h>
#include "m-core.h"
M_BEGIN_PROTECTED_CODE
/* Default value for the stack */
#ifndef M_USE_TASK_STACK_SIZE
#define M_USE_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
#endif
/* Default value for the priority tasks */
#ifndef M_USE_TASK_PRIORITY
#define M_USE_TASK_PRIORITY ( tskIDLE_PRIORITY )
#endif
/* Define a mutex type based on FreeRTOS definition */
typedef struct m_mutex_s {
SemaphoreHandle_t handle;
StaticSemaphore_t MutexBuffer;
} m_mutex_t[1];
/* Define a thread type based on FreeRTOS definition */
typedef struct m_cond_s {
SemaphoreHandle_t handle;
StaticSemaphore_t SemBuffer;
unsigned int NumThreadWaiting;
} m_cond_t[1];
/* Define a thread type based on FreeRTOS definition */
typedef struct m_thread_s {
SemaphoreHandle_t SemHandle;
StaticSemaphore_t SemBuffer;
TaskHandle_t TaskHandle;
StaticTask_t TaskBuffer;
void (*EntryPoint)(void *);
void* ArgsEntryPoint;
StackType_t* StackBuffer;
} m_thread_t[1];
/* Initialize the mutex (constructor) */
M_INLINE void m_mutex_init(m_mutex_t m)
{
/* Create a mutex semaphore without using any dynamic allocation */
m->handle = xSemaphoreCreateMutexStatic(&m->MutexBuffer);
// It cannot fail, so we won't use M_ASSERT_INIT
M_ASSERT(m->handle);
}
/* Clear the mutex (destructor) */
M_INLINE void m_mutex_clear(m_mutex_t m)
{
vSemaphoreDelete(m->handle);
}
/* Lock the mutex */
M_INLINE void m_mutex_lock(m_mutex_t m)
{
xSemaphoreTake(m->handle, portMAX_DELAY);
}
/* Unlock the mutex */
M_INLINE void m_mutex_unlock(m_mutex_t m)
{
xSemaphoreGive(m->handle);
}
/* Initialize the condition variable (constructor) */
M_INLINE void m_cond_init(m_cond_t c)
{
c->NumThreadWaiting = 0;
// Create a semaphore to implement the conditional variable
// Initial value is 0 and valid range is <= 0
c->handle = xSemaphoreCreateCountingStatic( INT_MAX, 0, &c->SemBuffer );
// It cannot fail, so we won't use M_ASSERT_INIT
M_ASSERT(c->handle);
}
/* Clear the condition variable (destructor) */
M_INLINE void m_cond_clear(m_cond_t c)
{
vSemaphoreDelete(c->handle);
}
/* Signal the condition variable to at least one waiting thread */
M_INLINE void m_cond_signal(m_cond_t c)
{
// This function is called within the mutex lock
// NumThreadWaiting doesn't need to be atomic
if (c->NumThreadWaiting > 0) {
// Wakeup one thread by posting on the semaphore
xSemaphoreGive(c->handle);
} // Otherwise there is no waiting thread, so nothing to signal
}
/* Signal the condition variable to all waiting threads */
M_INLINE void m_cond_broadcast(m_cond_t c)
{
// This function is called within the mutex lock
// NumThreadWaiting doesn't need to be atomic
if (c->NumThreadWaiting > 0) {
// Wakeup all thread by posting on the semaphore
// as many times as there are waiting threads
for(unsigned i = 0; i < c->NumThreadWaiting; i++) {
xSemaphoreGive(c->handle);
}
} // Otherwise there is no waiting thread, so nothing to signal
}
/* Wait for signaling the condition variable by another thread */
M_INLINE void m_cond_wait(m_cond_t c, m_mutex_t m)
{
// This function is called within the mutex lock
// Increment the number of waiting thread
c->NumThreadWaiting ++;
m_mutex_unlock(m);
// Wait for post in the semaphore
xSemaphoreTake(c->handle, portMAX_DELAY);
m_mutex_lock(m);
c->NumThreadWaiting --;
}
M_INLINE void m_thr3ad_wrapper( void *args)
{
struct m_thread_s *thread_ptr = args;
thread_ptr->EntryPoint(thread_ptr->ArgsEntryPoint);
// Give back the semaphore.
xSemaphoreGive(thread_ptr->SemHandle);
// Wait for destruction
while (true) { vTaskSuspend(NULL); }
}
/* Create the thread (constructor) and start it */
M_INLINE void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
{
// Create a semaphore to implement the final wait
t->SemHandle = xSemaphoreCreateCountingStatic( 1, 0, &t->SemBuffer );
M_ASSERT(t->SemHandle);
// Save the argument to the thread
t->EntryPoint = func;
t->ArgsEntryPoint = arg;
// Allocate the stack
t->StackBuffer = pvPortMalloc( sizeof (StackType_t) * M_USE_TASK_STACK_SIZE);
M_ASSERT_INIT(t->StackBuffer, "STACK");
// Create the task without using any dynamic allocation
t->TaskHandle = xTaskCreateStatic(m_thr3ad_wrapper, "M*LIB", M_USE_TASK_STACK_SIZE, (void*) t, M_USE_TASK_PRIORITY, t->StackBuffer, &t->TaskBuffer);
// It cannot fail, so we won't use M_ASSERT_INIT
M_ASSERT(t->TaskHandle);
}
/* Wait for the thread to terminate and destroy it (destructor) */
M_INLINE void m_thread_join(m_thread_t t)
{
xSemaphoreTake(t->SemHandle, portMAX_DELAY);
vTaskDelete(t->TaskHandle);
vPortFree(t->StackBuffer);
vSemaphoreDelete(t->SemHandle);
t->TaskHandle = 0;
t->StackBuffer = 0;
t->SemHandle = 0;
}
/* The thread has nothing meaningfull to do.
Inform the OS to let other threads be scheduled */
M_INLINE void m_thread_yield(void)
{
taskYIELD();
}
/* Sleep the thread for at least usec microseconds.
Return true if the sleep was successful */
M_INLINE bool m_thread_sleep(unsigned long long usec)
{
TickType_t delay = (TickType_t) (usec / portTICK_PERIOD_MS / 1000ULL);
vTaskDelay(delay);
return true;
}
// a helper structure for m_once_call
typedef struct {
atomic_int count;
} m_once_t[1];
// Initial value for m_once_t
#define M_ONCE_INIT_VALUE { { ATOMIC_VAR_INIT(0) } }
// Call the function exactly once
M_INLINE void m_once_call(m_once_t o, void (*func)(void))
{
if (atomic_load(&o->count) != 2) {
int n = 0;
if (atomic_compare_exchange_strong( &o->count, &n, 1)) {
// First thread success
func();
atomic_store(&o->count, 2);
}
// Wait for function call (FIXME: priority inversion possible?)
while (atomic_load(&o->count) != 2) { m_thread_yield(); }
} // Already called. Nothing to do
}
// Attribute to use to allocate a global variable to a thread.
#define M_THREAD_ATTR __thread
M_END_PROTECTED_CODE
/******************************** INVALID VALUE **********************************/
#else
# error Value of M_USE_THREAD_BACKEND is incorrect. Please see the documentation for valid usage.
#endif
// TODO: Obsolete M_LOCK macro.
/* M_LOCK macro. Allow simple locking encapsulation.
USAGE:
static M_LOCK_DECL(name);
int f(int n) {
M_LOCK(name) {
// Exclusive access
}
}
*/
/* NOTE: Either using direct support by the OS (WIN32/PTHREAD)
or using C11's ONCE mechanism */
#ifdef M_MUTEXI_INIT_VALUE
# define M_LOCK_DECL(name) m_mutex_t name = M_MUTEXI_INIT_VALUE
# define M_LOCK(name) \
M_LOCKI_DO(name, M_C(local_cont_, __LINE__), m_mutexi_lazy_lock, m_mutex_unlock)
#else
# define M_LOCK_DECL(name) \
m_mutex_t name; \
static void M_C(m_mutex_init_, name)(void) { \
m_mutex_init(name); \
} \
m_once_t M_C(m_once_, name) = M_ONCE_INIT_VALUE
# define M_LOCKI_BY_ONCE(name) \
(m_once_call(M_C(m_once_, name), M_C(m_mutex_init_, name)), \
m_mutex_lock(name), (void) 0 )
# define M_LOCK(name) \
M_LOCKI_DO(name, M_C(local_cont_, __LINE__), M_LOCKI_BY_ONCE, m_mutex_unlock)
#endif
#define M_LOCKI_DO(name, cont, lock_func, unlock_func) \
for(bool cont = true \
; cont && (lock_func (name), true); \
(unlock_func (name), cont = false))
#endif
+575 -455
View File
File diff suppressed because it is too large. Load diff
+578
View File
@@ -0,0 +1,578 @@
/*
* M*LIB - try / catch mechanism for M*LIB
*
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_TRY_H
#define MSTARLIB_TRY_H
#include "m-core.h"
#include "m-thread.h"
/*
* Select mechanism to use for support of RAII and exception,
* 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).
* The user can override the desired mechanism.
*/
#ifndef M_USE_TRY_MECHANISM
# if defined(__has_extension)
# if __has_extension(blocks)
# define M_TRY_CLANG_BLOCKS
# endif
# endif
# if defined(__cplusplus)
# define M_USE_TRY_MECHANISM 1
# elif defined(M_TRY_CLANG_BLOCKS)
# define M_USE_TRY_MECHANISM 2
# elif defined(__GNUC__) && !defined(__clang__)
# define M_USE_TRY_MECHANISM 3
# else
# define M_USE_TRY_MECHANISM 4
# endif
#endif
/*
* Start a protected section of code 'name' where all exceptions are catched
* by the associated CATCH section.
*/
#define M_TRY(name) \
M_TRY_B( M_C(m_try_bool_, name), M_C(m_try_buf_, name), name)
/*
* Catch an exception associated to the TRY block 'name' that matches the given error_code
* If error_code is 0, it catches all error codes.
* error code shall be a constant positive integer.
*/
#define M_CATCH(name, error_code) M_CATCH_B(name, error_code)
/*
* Throw an exception to the upper try block
* error_code shall be the first argument.
* Other arguments are integers or pointers stored in the exception.
* error code shall be a constant positive integer.
* There is no genericity of the exception data structure itself.
*/
#define M_THROW(...) do { \
M_STATIC_ASSERT(M_RET_ARG1 (__VA_ARGS__) != 0, \
M_LIB_NOT_A_CONSTANT_NON_NULL_INTEGER, \
"The error code shall be a non null positive constant"); \
M_STATIC_ASSERT(M_NARGS (__VA_ARGS__) <= 1+M_USE_MAX_CONTEXT, \
M_LIB_TOO_MANY_ARGUMENTS, \
"There are too many arguments for an exception."); \
M_IF_NARGS_EQ1(__VA_ARGS__)(M_THROW_1, M_THROW_N)(__VA_ARGS__); \
} while (0)
/*
* Size of the context data that are stored in an exception data structure.
*/
#ifndef M_USE_MAX_CONTEXT
#define M_USE_MAX_CONTEXT 10
#endif
/*
* The exception itself.
*
* It is POD data where every fields can be used by the user.
* It has been decided to have only one exception data structure
* to simplify error code and because :
* - using generic types is much harder in C to do (still possible)
* - it will make exceptions more usable for errors which should not
* be handled by exceptions.
*
* For C++, we need to encapsulate it in a template,
* so that it can be a unique type for each error code,
* which is needed for the catch mechanism.
* We all need to override the operator -> since the C++
* throw the type and catch the type, whereas the C back-end
* throw the type and catch a pointer to the type:
* within the catch block you are supposed to use the arrow
* operator to test the content of the exception.
*/
#if M_USE_TRY_MECHANISM == 1
namespace m_lib {
template <unsigned int N>
#endif
struct m_exception_s {
unsigned error_code; // Error code
unsigned short line; // Line number where the error was detected
unsigned short num; // Number of entries in 'context' table
const char *filename; // filename where the error was detected
intptr_t context[M_USE_MAX_CONTEXT]; // Specific context of the exception
#ifdef __cplusplus
m_exception_s<N> *operator->() { return this; }
#endif
};
#if M_USE_TRY_MECHANISM == 1
}
#endif
// Typical Error codes (TODO: add more classic?)
#define M_ERROR_MEMORY 1
#define M_ERROR_ACCESS 2
#define M_ERROR_BUSY 3
/*
* Define all global needed by the try mechanism with a
* thread attribute. It needs to be defined once in all the program
*/
#define M_TRY_DEF_ONCE() M_TRY_DEF_ONCE_B()
/*
* Re-throw the last exception
* It shall be done in a CATCH block.
*/
#define M_RETHROW() m_rethrow()
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/*
* Define the C++ back-end.
* It is fully different from C back-end as it reuses the classic try of the C++.
* Surprisingly it has more constraints than the C one.
* error_code shall be a positive, constant integer.
* the catch all block shall always be the last block.
* at least catch block is mandatory for each try block.
* Note that theses constraints are meaningless in real code,
* and simply good behavior.
* Notice also that you won't have any access to the exception for a catch all error.
*/
#if M_USE_TRY_MECHANISM == 1
// Define the CATCH block. If error_code is 0, it shall catch all errors.
// NOTE: It will even catch non M*LIB errors.
#define M_CATCH_B(name, error_code) \
M_IF(M_BOOL(error_code)) \
(catch (m_lib::m_exception_s<error_code> &name), catch (...))
// No global to define in C++
#define M_TRY_DEF_ONCE_B() /* Nothing to do */
// Reuse the try keyword of the C++
#define M_TRY_B(cont, buf, exception) \
try
// Reuse the throw keyword of the C++
// by throwing the type m_lib::m_exception_s<error_code>
#define M_THROW_1(error_code) \
throw m_lib::m_exception_s<error_code>{ error_code, __LINE__, 0, __FILE__, { 0 } }
// Reuse the throw keyword of the C++
// by throwing the type m_lib::m_exception_s<error_code>
#define M_THROW_N(error_code, ...) \
throw m_lib::m_exception_s<error_code>{ error_code, __LINE__, \
M_NARGS(__VA_ARGS__), __FILE__, { __VA_ARGS__ } }
// Nothing to inject for a pre initialization of a M*LIB object
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) /* Nothing to do */
// Code to inject for a post initialization of a M*LIB object
// We create a C++ object with a destructor that will call the CLEAR operator of the M*LIB object
// by using a lambda function.
// If the CLEAR operator is called naturally, we disable the destructor of the C++ object.
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
for(m_lib::m_regclear M_C(m_try_regclear_, name){[&](void) { M_CALL_CLEAR(oplist, name); } } \
; cont ; M_C(m_try_regclear_, name).disable() )
// M_DEFER Injection / pre initialization
#define M_DEFER_TRY_INJECT_PRE_B(cont, ...) /* Nothing to do */
// M_DEFER Injection / post initialization
// Register the stack frame and tests for the longjmp.
// In which case call the 'clear' operations (...), unstack the error list and rethrow the error.
#define M_DEFER_TRY_INJECT_POST_B(cont, ...) \
for(m_lib::m_regclear M_C(m_try_regclear_, cont){[&](void) { __VA_ARGS__; } } \
; cont ; M_C(m_try_regclear_, cont).disable() )
// Definition of the C++ object wrapper
// The registered function is called by the destructor,
// except if the disable function has been called.
#include <functional>
namespace m_lib {
class m_regclear {
std::function<void(void)> function;
bool done;
public:
inline m_regclear(const std::function<void(void)> &f) : function{f}, done{false} { }
inline void disable(void) { done = true; }
inline ~m_regclear() { if (done == false) { function(); done = true; } }
};
}
// Rethrow is simply throw without any argument
#define m_rethrow() throw
/*****************************************************************************/
/* The C back-end.
* It is fully different from the C++ back-end and is based on setjmp/lonjmp
* (classic implementation).
* 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.
*/
#else
#if (M_USE_TRY_MECHANISM == 3)
// Use of builtin setjmp / longjmp for GCC
// There are at least twice faster at worst, and reduce stack consumption
// See https://gcc.gnu.org/onlinedocs/gcc/Nonlocal-Gotos.html
// CLANG doesn't support these builtins officialy (https://groups.google.com/g/llvm-dev/c/9QgfdW23K8M)
#define m_try_setjmp(x) __builtin_setjmp(x)
#define m_try_longjmp(x,v) __builtin_longjmp(x, v)
typedef intptr_t m_try_jmp_buf[5];
#define m_try_jmp_buf m_try_jmp_buf
#else
// C compliant setjmp
#include <setjmp.h>
#define m_try_setjmp(x) setjmp(x)
#define m_try_longjmp(x,v) longjmp(x, v)
#define m_try_jmp_buf jmp_buf
#endif
// Define the CATCH block associated to the 'name' TRY to catch the exception
// associated to 'error_code' and provide 'name' as a pointer to the exception
// if the exception matches the error code.
// If error code is 0, it matches all errors.
#define M_CATCH_B(name, error_code) \
else if (m_catch( M_C(m_try_buf_, name), (error_code), &name))
// Define the operator to define nested functions (GCC) or blocks (CLANG)
#if M_USE_TRY_MECHANISM == 2
# define M_TRY_FUNC_OPERATOR ^
#else
# define M_TRY_FUNC_OPERATOR *
#endif
// Define the linked structure used to identify what is present in the C stack.
// We create for each M_TRY and each M_LET a new node in the stack that represents
// 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_CATCHED,
M_STATE_CLEAR_JMPBUF, M_STATE_CLEAR_CB } kind;
struct m_try_s *next;
union {
m_try_jmp_buf buf;
struct { void (M_TRY_FUNC_OPERATOR func)(void*); void *data; } clear;
} data;
} m_try_t[1];
// Define the TRY block.
// Classic usage of the for trick to push destructor on the exit path.
#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))
// 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
#define M_THROW_N(error_code, ...) \
m_throw( &(const struct m_exception_s) { error_code, __LINE__, M_NARGS(__VA_ARGS__), __FILE__, \
{ __VA_ARGS__ } } )
// Copy an exception to another.
M_INLINE void
m_exception_set(struct m_exception_s *out, const struct m_exception_s *in)
{
if (in != out) {
memcpy(out, in, sizeof *out);
}
}
// The global thread attribute variables and functions.
extern M_THREAD_ATTR struct m_try_s *m_global_error_list;
extern M_THREAD_ATTR struct m_exception_s m_global_exception;
extern M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void m_throw(const struct m_exception_s *exception);
// Macro to add once in one source file to define theses global:
#define M_TRY_DEF_ONCE_B() \
M_THREAD_ATTR struct m_try_s *m_global_error_list; \
M_THREAD_ATTR struct m_exception_s m_global_exception; \
\
/* Throw the given exception \
This function should be rarely called. */ \
M_ATTR_NO_RETURN M_ATTR_COLD_FUNCTION void \
m_throw(const struct m_exception_s *exception) \
{ \
/* 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 */ \
if (e->kind == M_STATE_CLEAR_CB) { \
e->data.clear.func(e->data.clear.data); \
} \
else { \
/* A JUMP command has been registered. \
* Either due to the M_TRY block or \
* because of the jump to the CLEAR operator of the object to clear. */ \
M_ASSERT(e->kind == M_STATE_TRY || e->kind == M_STATE_CLEAR_JMPBUF); \
/* If the exception is already m_global_exception, it won't be copied */ \
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); \
} \
/* Next stack frame */ \
e = e->next; \
} \
/* No exception found. \
Display the information and halt program . */ \
M_RAISE_FATAL("Exception '%u' raised by (%s:%d) is not catched. Program aborted.\n", \
exception->error_code, exception->filename, exception->line); \
}
// Rethrow the error
M_INLINE void
m_rethrow(void)
{
M_ASSERT(m_global_error_list != NULL);
m_throw(&m_global_exception);
}
// Catch the error code associated to the TRY block state
// and provide a pointer to the exception (which is a global).
M_INLINE bool
m_catch(m_try_t state, unsigned error_code, const struct m_exception_s **exception)
{
M_ASSERT(m_global_error_list == state);
M_ASSERT(state->kind == M_STATE_EXCEPTION_IN_PROGRESS);
*exception = &m_global_exception;
if (error_code != 0 && m_global_exception.error_code != error_code)
return false;
// The exception has been catched.
state->kind = M_STATE_EXCEPTION_CATCHED;
// Unstack the try block, so that next throw command in the CATCH block
// will reach the upper TRY block.
m_global_error_list = state->next;
return true;
}
// Initialize the state to a TRY state.
M_INLINE void
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.
}
#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
m_try_clear(m_try_t state)
{
// Even if there is a CATCH block and an unstack of the exception
// m_global_error_list won't be changed.
m_global_error_list = state->next;
if (M_UNLIKELY (state->kind == M_STATE_EXCEPTION_IN_PROGRESS)) {
// There was no catch for this error.
// Forward it to the upper level.
m_rethrow();
}
}
// Implement the M_LET injection macros, so that the CLEAR operator is called on exception
// Helper functions
// Each mechanisme provide 3 helper functions:
// * pre: which is called before the constructor
// * post: which is called after the constructor
// * final: which is called before the destructor.
// 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.
// However we register the position in the stack frame now so that in case of partial initialization
// of the object (if the INIT operator of the object calls other INIT operators of composed fields),
// since partial initialization will be unstacked naturally by the composing object.
M_INLINE bool
m_try_cb_pre(m_try_t state)
{
state->kind = M_STATE_CLEAR_CB;
state->next = m_global_error_list;
return true;
}
// We register the function to call of the initialized object.
M_INLINE bool
m_try_cb_post(m_try_t state, void (M_TRY_FUNC_OPERATOR func)(void*), void *data)
{
state->data.clear.func = func;
state->data.clear.data = data;
m_global_error_list = state;
return true;
}
// The object will be cleared.
// We can pop the stack frame of the errors.
M_INLINE void
m_try_cb_final(m_try_t state)
{
m_global_error_list = state->next;
}
// Pre initialization function. Save the stack frame for a longjmp
M_INLINE bool
m_try_jump_pre(m_try_t state)
{
state->kind = M_STATE_CLEAR_JMPBUF;
state->next = m_global_error_list;
return true;
}
// Post initialization function. Register the stack frame for a longjmp
M_INLINE void
m_try_jump_post(m_try_t state)
{
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.
M_INLINE void
m_try_jump_final(m_try_t state)
{
m_global_error_list = state->next;
}
// Implement the M_LET injection macros, so that the CLEAR operator is called on exception
//
#if M_USE_TRY_MECHANISM == 1
# error M*LIB: Internal error. C++ back-end requested within C implementation.
#elif M_USE_TRY_MECHANISM == 2
// Use of CLANG blocks
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
for(m_try_t M_C(m_try_state_, name); cont && \
m_try_cb_pre(M_C(m_try_state_, name) ); )
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
for(m_try_cb_post(M_C(m_try_state_, name), \
^ void (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); }, \
(void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) )
#elif M_USE_TRY_MECHANISM == 3
// Use of GCC nested functions.
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
for(m_try_t M_C(m_try_state_, name); cont && \
m_try_cb_pre(M_C(m_try_state_, name) ); )
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
for(m_try_cb_post(M_C(m_try_state_, name), \
__extension__ ({ __extension__ void _callback (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); } _callback; }), \
(void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) )
#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.
// It uses setjmp/longjmp, and as such, is much slower than
// other implementations.
// M_LET Injection / pre initialization
// Initialize the stack frame.
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
for(m_try_t M_C(m_try_state_, name); cont && \
m_try_jump_pre(M_C(m_try_state_, name)); )
// M_LET Injection / post initialization
// 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))
#else
# error M*LIB: Invalid value for M_USE_TRY_MECHANISM [1..4]
#endif
// M_DEFER Injection / pre initialization
// Initialize the stack frame.
#define M_DEFER_TRY_INJECT_PRE_B(cont, ...) \
for(m_try_t M_C(m_try_state_, cont); cont && \
m_try_jump_pre(M_C(m_try_state_, cont)); )
// M_DEFER Injection / post initialization
// 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))
#endif /* cplusplus */
/*****************************************************************************/
// Macro injection for M_LET.
// If the oplist defined NOCLEAR property, we won't register this variable for clear on exception
#undef M_LET_TRY_INJECT_PRE
#define M_LET_TRY_INJECT_PRE(cont, oplist, name) \
M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_EAT, M_LET_TRY_INJECT_PRE_B) \
(cont, oplist, name)
#undef M_LET_TRY_INJECT_POST
#define M_LET_TRY_INJECT_POST(cont, oplist, name) \
M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_EAT, M_LET_TRY_INJECT_POST_B) \
(cont, oplist, name)
// Macro injection for M_DEFER.
#undef M_DEFER_TRY_INJECT_PRE
#define M_DEFER_TRY_INJECT_PRE(cont, ...) M_DEFER_TRY_INJECT_PRE_B(cont, __VA_ARGS__)
#undef M_DEFER_TRY_INJECT_POST
#define M_DEFER_TRY_INJECT_POST(cont, ...) M_DEFER_TRY_INJECT_POST_B(cont, __VA_ARGS__)
// 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))
#endif
+88 -82
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - TUPLE module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -27,13 +27,11 @@
#include "m-core.h"
/********************** External interface *************************/
/* Define the tuple type and functions.
USAGE:
TUPLE_DEF2(name, [(field1, type1[, oplist1]), (field2, type2[, oplist2]), ...] ) */
#define M_TUPLE_DEF2(name, ...) \
M_TUPLE_DEF2_AS(name, M_C(name,_t), __VA_ARGS__)
M_TUPLE_DEF2_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define the tuple type and functions
@@ -81,7 +79,9 @@ namespace m_lib {
#endif
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/* Contract of a tuple. Nothing notable */
#define M_TUPL3_CONTRACT(tup) do { \
@@ -151,7 +151,7 @@ namespace m_lib {
M_TUPL3_IF_ALL(RESET, __VA_ARGS__)(M_TUPL3_DEFINE_RESET(name, __VA_ARGS__),)
/* Provide order for _cmp_order */
#define M_TUPL3_ORDER_CONVERT(name, x) M_C(name, M_C(M_TUPL3_ORDER_CONVERT_, x))
#define M_TUPL3_ORDER_CONVERT(name, x) M_F(name, M_C(M_TUPL3_ORDER_CONVERT_, x))
#define M_TUPL3_ORDER_CONVERT_ASC(x) M_C3(_,x,_value)
#define M_TUPL3_ORDER_CONVERT_DSC(x) M_C3(_,x,_value)*-1
@@ -201,14 +201,14 @@ namespace m_lib {
/* Define the type of a tuple */
#define M_TUPL3_DEFINE_TYPE(name, name_t, ...) \
typedef struct M_C(name, _s) { \
typedef struct M_F(name, _s) { \
M_MAP(M_TUPL3_DEFINE_RECUR_TYPE_ELE , __VA_ARGS__) \
} name_t[1]; \
\
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
/* Define internal type for oplist */ \
typedef name_t M_C(name, _ct); \
typedef name_t M_F(name, _ct); \
/* Save constant as the number of arguments (internal) */ \
typedef enum { \
M_C3(m_tupl3_, name, _num_args) = M_NARGS(__VA_ARGS__) \
@@ -225,9 +225,9 @@ namespace m_lib {
/* Define the basic enumerate, identifying a parameter */
#define M_TUPL3_DEFINE_ENUM(name, ...) \
typedef enum { \
M_C(name, _first_one_val), \
M_F(name, _first_one_val), \
M_MAP2_C(M_TUPL3_DEFINE_ENUM_ELE , name, __VA_ARGS__) \
} M_C(name,_field_e);
} M_F(name,_field_e);
#define M_TUPL3_DEFINE_ENUM_ELE(name, a) \
M_C4(name, _, M_TUPL3_GET_FIELD a, _value)
@@ -242,41 +242,43 @@ namespace m_lib {
/* Define the INIT method calling the INIT method for all params */
#define M_TUPL3_DEFINE_INIT(name, ...) \
static inline void M_C(name, _init)(M_C(name,_ct) my) { \
M_MAP(M_TUPL3_DEFINE_INIT_FUNC , __VA_ARGS__) \
M_INLINE void M_F(name, _init)(M_F(name,_ct) my) { \
M_MAP(M_TUPL3_DEFINE_INIT_FUNC , __VA_ARGS__) {} \
}
#define M_TUPL3_DEFINE_INIT_FUNC(a) \
M_TUPL3_CALL_INIT(a, my -> M_TUPL3_GET_FIELD a );
M_CHAIN_OBJ(M_TUPL3_GET_FIELD a, M_TUPL3_GET_OPLIST a, my -> M_TUPL3_GET_FIELD a)
/* Define the INIT_SET method calling the INIT_SET method for all params */
#define M_TUPL3_DEFINE_INIT_SET(name, ...) \
static inline void M_C(name, _init_set)(M_C(name,_ct) my , M_C(name,_ct) const org) { \
M_INLINE void M_F(name, _init_set)(M_F(name,_ct) my , M_F(name,_ct) const org) { \
M_TUPL3_CONTRACT(org); \
M_MAP(M_TUPL3_DEFINE_INIT_SET_FUNC , __VA_ARGS__) \
M_MAP(M_TUPL3_DEFINE_INIT_SET_FUNC , __VA_ARGS__) {} \
}
#define M_TUPL3_DEFINE_INIT_SET_FUNC(a) \
M_TUPL3_CALL_INIT_SET(a, my -> M_TUPL3_GET_FIELD a , org -> M_TUPL3_GET_FIELD a );
M_CHAIN_OBJ(M_TUPL3_GET_FIELD a, M_TUPL3_GET_OPLIST a, \
my -> M_TUPL3_GET_FIELD a , org -> M_TUPL3_GET_FIELD a )
/* Define the INIT_WITH method calling the INIT_SET method for all params. */
#define M_TUPL3_DEFINE_INIT_SET2(name, ...) \
static inline void M_C(name, _init_emplace)(M_C(name,_ct) my \
M_INLINE void M_F(name, _init_emplace)(M_F(name,_ct) my \
M_MAP(M_TUPL3_DEFINE_INIT_SET2_PROTO, __VA_ARGS__) \
) { \
M_MAP(M_TUPL3_DEFINE_INIT_SET2_FUNC , __VA_ARGS__) \
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_TUPL3_CALL_INIT_SET(a, my -> M_TUPL3_GET_FIELD a , M_TUPL3_GET_FIELD 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 )
/* Define the SET method calling the SET method for all params. */
#define M_TUPL3_DEFINE_SET(name, ...) \
static inline void M_C(name, _set)(M_C(name,_ct) my , \
M_C(name,_ct) const org) { \
M_INLINE void M_F(name, _set)(M_F(name,_ct) my , \
M_F(name,_ct) const org) { \
M_TUPL3_CONTRACT(my); \
M_TUPL3_CONTRACT(org); \
M_MAP(M_TUPL3_DEFINE_SET_FUNC , __VA_ARGS__) \
@@ -288,7 +290,7 @@ namespace m_lib {
/* Define the SET_WITH method calling the SET method for all params. */
#define M_TUPL3_DEFINE_SET2(name, ...) \
static inline void M_C(name, _emplace)(M_C(name,_ct) my \
M_INLINE void M_F(name, _emplace)(M_F(name,_ct) my \
M_MAP(M_TUPL3_DEFINE_SET2_PROTO, __VA_ARGS__) \
) { \
M_TUPL3_CONTRACT(my); \
@@ -303,7 +305,7 @@ namespace m_lib {
/* Define the CLEAR method calling the CLEAR method for all params. */
#define M_TUPL3_DEFINE_CLEAR(name, ...) \
static inline void M_C(name, _clear)(M_C(name,_ct) my) { \
M_INLINE void M_F(name, _clear)(M_F(name,_ct) my) { \
M_TUPL3_CONTRACT(my); \
M_MAP(M_TUPL3_DEFINE_CLEAR_FUNC , __VA_ARGS__) \
}
@@ -317,19 +319,19 @@ namespace m_lib {
M_MAP3(M_TUPL3_DEFINE_GETTER_FIELD_PROTO, name, __VA_ARGS__)
#define M_TUPL3_DEFINE_GETTER_FIELD_PROTO(name, num, a) \
static inline M_TUPL3_GET_TYPE a * M_C3(name, _get_at_, M_TUPL3_GET_FIELD a) \
(M_C(name,_ct) my) { \
M_INLINE M_TUPL3_GET_TYPE a * M_C3(name, _get_at_, M_TUPL3_GET_FIELD a) \
(M_F(name,_ct) my) { \
M_TUPL3_CONTRACT(my); \
return &(my->M_TUPL3_GET_FIELD a); \
} \
static inline M_TUPL3_GET_TYPE a const * M_C3(name, _cget_at_, M_TUPL3_GET_FIELD a) \
(M_C(name,_ct) const my) { \
M_INLINE M_TUPL3_GET_TYPE a const * M_C3(name, _cget_at_, M_TUPL3_GET_FIELD a) \
(M_F(name,_ct) const my) { \
M_TUPL3_CONTRACT(my); \
return &(my->M_TUPL3_GET_FIELD a); \
} \
/* Same but uses numerical index for accessing the field (internal) */ \
static inline M_TUPL3_GET_TYPE a * M_C4(m_tupl3_, name, _get_at_, num) \
(M_C(name,_ct) my) { \
M_INLINE M_TUPL3_GET_TYPE a * M_C4(m_tupl3_, name, _get_at_, num) \
(M_F(name,_ct) my) { \
return &(my->M_TUPL3_GET_FIELD a); \
} \
@@ -339,8 +341,8 @@ namespace m_lib {
M_MAP2(M_TUPL3_DEFINE_SETTER_FIELD_PROTO, name, __VA_ARGS__)
#define M_TUPL3_DEFINE_SETTER_FIELD_PROTO(name, a) \
static inline void M_C3(name, _set_, M_TUPL3_GET_FIELD a) \
(M_C(name,_ct) my, M_TUPL3_GET_TYPE a const M_TUPL3_GET_FIELD 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_TUPL3_CONTRACT(my); \
M_TUPL3_CALL_SET(a, my ->M_TUPL3_GET_FIELD a, M_TUPL3_GET_FIELD a); \
}
@@ -348,8 +350,8 @@ namespace m_lib {
/* Define the CMP method by calling CMP methods for all params. */
#define M_TUPL3_DEFINE_CMP(name, ...) \
static inline int M_C(name, _cmp)(M_C(name,_ct) const e1 , \
M_C(name,_ct) const e2) { \
M_INLINE int M_F(name, _cmp)(M_F(name,_ct) const e1 , \
M_F(name,_ct) const e2) { \
int i; \
M_TUPL3_CONTRACT(e1); \
M_TUPL3_CONTRACT(e2); \
@@ -369,8 +371,8 @@ namespace m_lib {
FIXME: All oplists shall define the CMP operator or at least one?
*/
#define M_TUPL3_DEFINE_CMP_ORDER(name, ...) \
static inline int M_C(name, _cmp_order)(M_C(name,_ct) const e1 , \
M_C(name,_ct) const e2, \
M_INLINE int M_F(name, _cmp_order)(M_F(name,_ct) const e1 , \
M_F(name,_ct) const e2, \
const int order[]) { \
int i, r; \
M_TUPL3_CONTRACT(e1); \
@@ -380,7 +382,7 @@ namespace m_lib {
switch (i) { \
case 0: return 0; \
M_MAP2(M_TUPL3_DEFINE_CMP_ORDER_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(0); \
default: M_ASSUME(0); \
} \
} \
}
@@ -403,8 +405,8 @@ namespace m_lib {
)
#define M_TUPL3_DEFINE_CMP_FIELD_FUNC(name, field, func_cmp, oplist) \
static inline int M_C3(name, _cmp_, field)(M_C(name,_ct) const e1 , \
M_C(name,_ct) const e2) { \
M_INLINE int M_C3(name, _cmp_, field)(M_F(name,_ct) const e1 , \
M_F(name,_ct) const e2) { \
M_TUPL3_CONTRACT(e1); \
M_TUPL3_CONTRACT(e2); \
return M_APPLY_API(func_cmp, oplist, e1 -> field , e2 -> field ); \
@@ -413,8 +415,8 @@ namespace m_lib {
/* Define a EQUAL method by calling the EQUAL methods for all params */
#define M_TUPL3_DEFINE_EQUAL(name, ...) \
static inline bool M_C(name, _equal_p)(M_C(name,_ct) const e1 , \
M_C(name,_ct) const e2) { \
M_INLINE bool M_F(name, _equal_p)(M_F(name,_ct) const e1 , \
M_F(name,_ct) const e2) { \
bool b; \
M_TUPL3_CONTRACT(e1); \
M_TUPL3_CONTRACT(e2); \
@@ -431,7 +433,7 @@ namespace m_lib {
/* Define a HASH method by calling the HASH methods for all params */
#define M_TUPL3_DEFINE_HASH(name, ...) \
static inline size_t M_C(name, _hash)(M_C(name,_ct) const e1) { \
M_INLINE size_t M_F(name, _hash)(M_F(name,_ct) const e1) { \
M_TUPL3_CONTRACT(e1); \
M_HASH_DECL(hash); \
M_MAP(M_TUPL3_DEFINE_HASH_FUNC_P0, __VA_ARGS__) \
@@ -446,8 +448,8 @@ namespace m_lib {
/* Define a GET_STR method by calling the GET_STR methods for all params */
#define M_TUPL3_DEFINE_GET_STR(name, ...) \
static inline void M_C(name, _get_str)(m_string_t str, \
M_C(name,_ct) const el, \
M_INLINE void M_F(name, _get_str)(m_string_t str, \
M_F(name,_ct) const el, \
bool append) { \
bool comma = false; \
M_TUPL3_CONTRACT(el); \
@@ -465,8 +467,8 @@ namespace m_lib {
/* Define a OUT_STR method by calling the OUT_STR methods for all params */
#define M_TUPL3_DEFINE_OUT_STR(name, ...) \
static inline void M_C(name, _out_str)(FILE *f, \
M_C(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); \
@@ -483,7 +485,7 @@ namespace m_lib {
/* Define a IN_STR method by calling the IN_STR methods for all params */
#define M_TUPL3_DEFINE_IN_STR(name, ...) \
static inline bool M_C(name, _in_str)(M_C(name,_ct) el, FILE *f) { \
M_INLINE bool M_F(name, _in_str)(M_F(name,_ct) el, FILE *f) { \
bool comma = false; \
M_TUPL3_CONTRACT(el); \
M_ASSERT (f != NULL); \
@@ -506,7 +508,7 @@ namespace m_lib {
/* Define a PARSE_STR method by calling the PARSE_STR methods for all params */
#define M_TUPL3_DEFINE_PARSE_STR(name, ...) \
static inline bool M_C(name, _parse_str)(M_C(name,_ct) el, \
M_INLINE bool M_F(name, _parse_str)(M_F(name,_ct) el, \
const char str[], \
const char **endptr) { \
M_TUPL3_CONTRACT(el); \
@@ -540,9 +542,9 @@ namespace m_lib {
/* Define a OUT_SERIAL method by calling the OUT_SERIAL methods for all params */
#define M_TUPL3_DEFINE_OUT_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, \
M_C(name,_ct) const el) { \
M_INLINE m_serial_return_code_t \
M_F(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__); \
@@ -567,8 +569,8 @@ namespace m_lib {
/* Define a IN_SERIAL method by calling the IN_SERIAL methods for all params */
#define M_TUPL3_DEFINE_IN_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(M_C(name,_ct) el, m_serial_read_t f) { \
M_INLINE m_serial_return_code_t \
M_F(name, _in_serial)(M_F(name,_ct) el, m_serial_read_t f) { \
M_TUPL3_CONTRACT(el); \
M_ASSERT (f != NULL && f->m_interface != NULL); \
int index = -1; \
@@ -584,7 +586,7 @@ namespace m_lib {
M_ASSERT (index >= 0 && index < field_max); \
switch (1+index) { \
M_MAP2(M_TUPL3_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(0); \
default: M_ASSUME(0); \
} \
ret = (ret == M_SERIAL_OK_DONE) ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL; \
} \
@@ -598,9 +600,10 @@ namespace m_lib {
break; \
/* Define a INIT_MOVE method by calling the INIT_MOVE methods for all params */
/* Define a INIT_MOVE method by calling the INIT_MOVE methods for all params
INIT_MOVE cannot fail and cannot throw any exception */
#define M_TUPL3_DEFINE_INIT_MOVE(name, ...) \
static inline void M_C(name, _init_move)(M_C(name,_ct) el, M_C(name,_ct) org) { \
M_INLINE void M_F(name, _init_move)(M_F(name,_ct) el, M_F(name,_ct) org) { \
M_TUPL3_CONTRACT(el); \
M_MAP(M_TUPL3_DEFINE_INIT_MOVE_FUNC , __VA_ARGS__) \
}
@@ -611,7 +614,7 @@ namespace m_lib {
/* Define a MOVE method by calling the MOVE methods for all params */
#define M_TUPL3_DEFINE_MOVE(name, ...) \
static inline void M_C(name, _move)(M_C(name,_ct) el, M_C(name,_ct) org) { \
M_INLINE void M_F(name, _move)(M_F(name,_ct) el, M_F(name,_ct) org) { \
M_TUPL3_CONTRACT(el); \
M_MAP(M_TUPL3_DEFINE_MOVE_FUNC , __VA_ARGS__) \
}
@@ -622,7 +625,7 @@ namespace m_lib {
/* Define a SWAP method by calling the SWAP methods for all params */
#define M_TUPL3_DEFINE_SWAP(name, ...) \
static inline void M_C(name, _swap)(M_C(name,_ct) el1, M_C(name,_ct) el2) { \
M_INLINE void M_F(name, _swap)(M_F(name,_ct) el1, M_F(name,_ct) el2) { \
M_TUPL3_CONTRACT(el1); \
M_TUPL3_CONTRACT(el2); \
M_MAP(M_TUPL3_DEFINE_SWAP_FUNC , __VA_ARGS__) \
@@ -634,7 +637,7 @@ namespace m_lib {
/* Define a RESET method by calling the RESET methods for all params */
#define M_TUPL3_DEFINE_RESET(name, ...) \
static inline void M_C(name, _reset)(M_C(name,_ct) el1) { \
M_INLINE void M_F(name, _reset)(M_F(name,_ct) el1) { \
M_TUPL3_CONTRACT(el1); \
M_MAP(M_TUPL3_DEFINE_RESET_FUNC , __VA_ARGS__) \
} \
@@ -643,6 +646,8 @@ namespace m_lib {
M_TUPL3_CALL_RESET(a, el1 -> M_TUPL3_GET_FIELD a);
/********************************** INTERNAL *********************************/
/* INIT_WITH macro enabling recursive INIT_WITH initialization
tuple = { int, m_string_t, array<m_string_t> }
USAGE:
@@ -669,7 +674,7 @@ namespace m_lib {
M_IF(M_NOTEQUAL( M_NARGS oplist_arglist, M_NARGS (__VA_ARGS__))) \
(M_TUPL3_INIT_WITH_P1_FUNC, M_TUPL3_INIT_WITH_P1_MACRO)(name, oplist_arglist, dest, __VA_ARGS__)
#define M_TUPL3_INIT_WITH_P1_FUNC(name, oplist_arglist, dest, ...) \
M_C(name, _init_emplace)(dest, __VA_ARGS__)
M_F(name, _init_emplace)(dest, __VA_ARGS__)
#define M_TUPL3_INIT_WITH_P1_MACRO(name, oplist_arglist, dest, ...) \
( M_STATIC_ASSERT( M_NARGS oplist_arglist == M_C3(m_tupl3_, name, _num_args), M_LIB_DIMENSION_ERROR, "The number of oplists given to TUPLE_OPLIST don't match the number of oplists used to create the tuple." ), \
M_STATIC_ASSERT( M_NARGS(__VA_ARGS__) == M_C3(m_tupl3_, name, _num_args), M_LIB_DIMENSION_ERROR, "Missing / Too many arguments for tuple"), \
@@ -696,6 +701,8 @@ namespace m_lib {
M_TEST_METHOD_P(method, op)
/********************************** INTERNAL *********************************/
/* Macros for testing for the presence of a method in all the params */
#define M_TUPL3_IF_ALL(method, ...) \
M_IF(M_REDUCE2(M_TUPL3_TEST_METHOD_P, M_AND, method, __VA_ARGS__))
@@ -717,30 +724,27 @@ namespace m_lib {
/* Define the TUPLE oplist */
#define M_TUPL3_OPLIST_P3(name, ...) \
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_C(name,_init)),), \
INIT_SET(M_C(name, _init_set)), \
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \
INIT_SET(M_F(name, _init_set)), \
INIT_WITH(API_1(M_TUPL3_INIT_WITH)), \
SET(M_C(name,_set)), \
CLEAR(M_C(name, _clear)), \
SET(M_F(name,_set)), \
CLEAR(M_F(name, _clear)), \
NAME(name), \
TYPE(M_C(name,_ct)), \
TYPE(M_F(name,_ct)), \
OPLIST( (__VA_ARGS__) ), \
M_IF_METHOD_ALL(CMP, __VA_ARGS__)(CMP(M_C(name, _cmp)),), \
M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_C(name, _hash)),), \
M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_C(name, _equal_p)),), \
M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(M_C(name, _get_str)),), \
M_IF_METHOD_ALL(PARSE_STR, __VA_ARGS__)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD_ALL(IN_SERIAL, __VA_ARGS__)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_C(name, _init_move)),), \
M_IF_METHOD_ALL(MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_C(name, _swap)),), \
M_IF_METHOD_ALL(RESET, __VA_ARGS__)(RESET(M_C(name, _reset)),), \
M_IF_METHOD(NEW, M_RET_ARG1(__VA_ARGS__,))(NEW(M_DELAY2(M_GET_NEW) M_RET_ARG1(__VA_ARGS__,)),), \
M_IF_METHOD(REALLOC, M_RET_ARG1(__VA_ARGS__,))(REALLOC(M_DELAY2(M_GET_REALLOC) M_RET_ARG1(__VA_ARGS__,)),), \
M_IF_METHOD(DEL, M_RET_ARG1(__VA_ARGS__,))(DEL(M_DELAY2(M_GET_DEL) M_RET_ARG1(__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(M_F(name, _get_str)),), \
M_IF_METHOD_ALL(PARSE_STR, __VA_ARGS__)(PARSE_STR(M_F(name, _parse_str)),), \
M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(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(M_F(name, _in_serial)),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(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(M_F(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \
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__))) ) ) \
)
@@ -748,6 +752,8 @@ namespace m_lib {
#define M_TUPL3_OPLIST_SUBTYPE(name, num) \
M_C4(name, _type_, num, _ct)
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define TUPLE_DEF2 M_TUPLE_DEF2
#define TUPLE_DEF2_AS M_TUPLE_DEF2_AS
+138 -133
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - VARIANT module
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -27,14 +27,11 @@
#include "m-core.h"
/********************** External interface *************************/
/* Define the variant type and functions.
USAGE:
VARIANT_DEF2(name, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */
#define M_VARIANT_DEF2(name, ...) \
M_VARIANT_DEF2_AS(name, M_C(name,_t), __VA_ARGS__)
M_VARIANT_DEF2_AS(name, M_F(name,_t), __VA_ARGS__)
/* Define the variant type and functions
@@ -55,13 +52,15 @@
M_VAR1ANT_OPLIST_P1((__VA_ARGS__ )))
/********************************** INTERNAL ************************************/
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/* Contract of a variant. */
#define M_VAR1ANT_CONTRACT(name, my) do { \
M_ASSERT(my != NULL); \
M_ASSERT(my->type >= M_C(name, _EMPTY)); \
M_ASSERT(my->type <= (enum M_C(name, _enum)) M_C(name, _MAX_TYPE)); \
M_ASSERT(my->type >= M_F(name, _EMPTY)); \
M_ASSERT(my->type <= (enum M_F(name, _enum)) M_F(name, _MAX_TYPE)); \
} while (0)
/* Inject the oplist within the list of arguments */
@@ -182,25 +181,25 @@
/* Define the type */
#define M_VAR1ANT_DEFINE_TYPE(name, name_t, ...) \
/* Define enum of all types of the variant */ \
enum M_C(name, _enum) { M_C(name, _EMPTY) \
enum M_F(name, _enum) { M_F(name, _EMPTY) \
M_MAP2(M_VAR1ANT_DEFINE_UNION_ELE, name, __VA_ARGS__) \
}; \
/* Define enum equal to the number of types of the variant */ \
enum M_C(name, _enum_max) { \
M_C(name, _MAX_TYPE) = M_NARGS(__VA_ARGS__) \
enum M_F(name, _enum_max) { \
M_F(name, _MAX_TYPE) = M_NARGS(__VA_ARGS__) \
}; \
/* Define the variant */ \
typedef struct M_C(name, _s) { \
enum M_C(name, _enum) type; \
typedef struct M_F(name, _s) { \
enum M_F(name, _enum) type; \
union { \
M_MAP(M_VAR1ANT_DEFINE_TYPE_ELE , __VA_ARGS__) \
} value; \
} name_t[1]; \
\
typedef struct M_C(name, _s) *M_C(name, _ptr); \
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
/* Define internal type for oplist */ \
typedef name_t M_C(name, _ct);
typedef name_t M_F(name, _ct);
#define M_VAR1ANT_DEFINE_UNION_ELE(name, a) \
, M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value)
@@ -220,21 +219,21 @@
/* Define the INIT function. Init the variant to empty */
#define M_VAR1ANT_DEFINE_INIT(name, ...) \
static inline void M_C(name, _init)(M_C(name,_ct) my) { \
my->type = M_C(name, _EMPTY); \
M_INLINE void M_F(name, _init)(M_F(name,_ct) my) { \
my->type = M_F(name, _EMPTY); \
}
/* Define the INIT_SET function. */
#define M_VAR1ANT_DEFINE_INIT_SET(name, ...) \
static inline void M_C(name, _init_set)(M_C(name,_ct) my , \
M_C(name,_ct) const org) { \
M_INLINE void M_F(name, _init_set)(M_F(name,_ct) my , \
M_F(name,_ct) const org) { \
M_VAR1ANT_CONTRACT(name, org); \
my->type = org->type; \
switch (org->type) { \
case M_C(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_INIT_SET_FUNC, name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
case M_F(name, _EMPTY): /* fallthrough */ \
default: M_ASSUME(org->type == M_F(name, _EMPTY)); break; \
} \
}
@@ -247,20 +246,20 @@
/* Define the SET function. */
#define M_VAR1ANT_DEFINE_SET(name, ...) \
static inline void M_C(name, _set)(M_C(name,_ct) my , \
M_C(name,_ct) const org) { \
M_INLINE void M_F(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_C(name, _clear)(my); \
M_C(name, _init_set)(my, org); \
M_F(name, _clear)(my); \
M_F(name, _init_set)(my, org); \
} else { \
/* Same type: optimize the set */ \
switch (org->type) { \
case M_C(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_SET_FUNC, name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
case M_F(name, _EMPTY): /* fallthrough */ \
default: M_ASSUME(org->type == M_F(name, _EMPTY)); break; \
} \
} \
}
@@ -274,14 +273,14 @@
/* Define the CLEAR function. */
#define M_VAR1ANT_DEFINE_CLEAR(name, ...) \
static inline void M_C(name, _clear)(M_C(name,_ct) my) { \
M_INLINE void M_F(name, _clear)(M_F(name,_ct) my) { \
M_VAR1ANT_CONTRACT(name, my); \
switch (my->type) { \
case M_C(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_CLEAR_FUNC, name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
case M_F(name, _EMPTY): /* fallthrough */ \
default: M_ASSUME(my->type == M_F(name, _EMPTY)); break; \
} \
my->type = M_C(name, _EMPTY); \
my->type = M_F(name, _EMPTY); \
}
#define M_VAR1ANT_DEFINE_CLEAR_FUNC(name, a) \
@@ -292,20 +291,20 @@
/* Define the TEST_P function. */
#define M_VAR1ANT_DEFINE_TEST_P(name, ...) \
static inline bool M_C(name, _empty_p)(M_C(name,_ct) const my) { \
M_INLINE bool M_F(name, _empty_p)(M_F(name,_ct) const my) { \
M_VAR1ANT_CONTRACT(name, my); \
return my->type == M_C(name, _EMPTY); \
return my->type == M_F(name, _EMPTY); \
} \
static inline enum M_C(name, _enum) \
M_C(name, _type)(M_C(name,_ct) my) { \
M_INLINE enum M_F(name, _enum) \
M_F(name, _type)(M_F(name,_ct) my) { \
M_VAR1ANT_CONTRACT(name, my); \
return my->type; \
} \
M_MAP2(M_VAR1ANT_DEFINE_TEST_FUNC, name, __VA_ARGS__)
#define M_VAR1ANT_DEFINE_TEST_FUNC(name, a) \
static inline bool \
M_C4(name, _, M_VAR1ANT_GET_FIELD a, _p)(M_C(name,_ct) const my) { \
M_INLINE bool \
M_C4(name, _, M_VAR1ANT_GET_FIELD a, _p)(M_F(name,_ct) const my) { \
M_VAR1ANT_CONTRACT(name, my); \
return my->type == M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
}
@@ -316,8 +315,8 @@
M_MAP2(M_VAR1ANT_DEFINE_INIT_FIELD_FUNC, name, __VA_ARGS__)
#define M_VAR1ANT_DEFINE_INIT_FIELD_FUNC(name, a) \
static inline void \
M_C3(name, _init_, M_VAR1ANT_GET_FIELD a)(M_C(name,_ct) my) { \
M_INLINE void \
M_C3(name, _init_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my) { \
/* 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); \
@@ -329,8 +328,8 @@
M_MAP2(M_VAR1ANT_DEFINE_INIT_SETTER_FIELD_FUNC, name, __VA_ARGS__)
#define M_VAR1ANT_DEFINE_INIT_SETTER_FIELD_FUNC(name, a) \
static inline void \
M_C3(name, _init_set_, M_VAR1ANT_GET_FIELD a)(M_C(name,_ct) my, \
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 ) { \
my->type = M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value); \
M_VAR1ANT_CALL_INIT_SET(a, my -> value. M_VAR1ANT_GET_FIELD a, \
@@ -343,15 +342,15 @@
M_MAP2(M_VAR1ANT_DEFINE_SETTER_FIELD_FUNC, name, __VA_ARGS__)
#define M_VAR1ANT_DEFINE_SETTER_FIELD_FUNC(name, a) \
static inline void \
M_C3(name, _set_, M_VAR1ANT_GET_FIELD a)(M_C(name,_ct) my, \
M_INLINE void \
M_C3(name, _set_, M_VAR1ANT_GET_FIELD a)(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_C(name, _clear)(my); \
M_F(name, _clear)(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, \
@@ -365,8 +364,8 @@
M_MAP2(M_VAR1ANT_DEFINE_GETTER_FIELD_FUNC, name, __VA_ARGS__)
#define M_VAR1ANT_DEFINE_GETTER_FIELD_FUNC(name, a) \
static inline M_VAR1ANT_GET_TYPE a * \
M_C3(name, _get_, M_VAR1ANT_GET_FIELD a)(M_C(name,_ct) my) { \
M_INLINE M_VAR1ANT_GET_TYPE a * \
M_C3(name, _get_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my) { \
M_VAR1ANT_CONTRACT(name, my); \
if (my->type != M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) ) { \
return NULL; \
@@ -374,8 +373,8 @@
return &my -> value . M_VAR1ANT_GET_FIELD a; \
} \
\
static inline M_VAR1ANT_GET_TYPE a const * \
M_C3(name, _cget_, M_VAR1ANT_GET_FIELD a)(M_C(name,_ct) const my) { \
M_INLINE M_VAR1ANT_GET_TYPE a const * \
M_C3(name, _cget_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) const my) { \
M_VAR1ANT_CONTRACT(name, my); \
if (my->type != M_C4(name, _, M_VAR1ANT_GET_FIELD a, _value) ) { \
return NULL; \
@@ -394,11 +393,11 @@
#define M_VAR1ANT_MAP3_ALT_ID(a, b) a b
#define M_VAR1ANT_DEFINE_EMPLACE_FUNC(name, num, a) \
M_EMPLACE_QUEUE_DEF( (name, M_VAR1ANT_GET_FIELD a), M_C(name,_ct), M_C3(name, _init_emplace_, M_VAR1ANT_GET_FIELD a), M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_DEFINE_INIT_EMPLACE_DEF) \
M_EMPLACE_QUEUE_DEF( (name, M_VAR1ANT_GET_FIELD a), M_C(name,_ct), M_C3(name, _emplace_, M_VAR1ANT_GET_FIELD a), M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_DEFINE_EMPLACE_DEF)
M_EMPLACE_QUEUE_DEF( (name, M_VAR1ANT_GET_FIELD a), M_F(name,_ct), M_C3(name, _init_emplace_, M_VAR1ANT_GET_FIELD a), M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_DEFINE_INIT_EMPLACE_DEF) \
M_EMPLACE_QUEUE_DEF( (name, M_VAR1ANT_GET_FIELD a), M_F(name,_ct), M_C3(name, _emplace_, M_VAR1ANT_GET_FIELD a), M_VAR1ANT_GET_OPLIST a, M_VAR1ANT_DEFINE_EMPLACE_DEF)
#define M_VAR1ANT_DEFINE_INIT_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
static inline void \
M_INLINE void \
function_name(name_t my \
M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \
{ \
@@ -407,7 +406,7 @@ static inline void \
} \
#define M_VAR1ANT_DEFINE_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
static inline void \
M_INLINE void \
function_name(name_t my \
M_EMPLACE_LIST_TYPE_VAR(ab, exp_emplace_type) ) \
{ \
@@ -419,16 +418,16 @@ static inline void \
/* Define the EQUAL_P function. */
#define M_VAR1ANT_DEFINE_EQUAL(name, ...) \
static inline bool M_C(name, _equal_p)(M_C(name,_ct) const e1 , \
M_C(name,_ct) const e2) { \
M_INLINE bool M_F(name, _equal_p)(M_F(name,_ct) const e1 , \
M_F(name,_ct) const e2) { \
bool b; \
M_VAR1ANT_CONTRACT(name, e1); \
M_VAR1ANT_CONTRACT(name, e2); \
if (e1->type != e2->type) return false; \
switch (e1->type) { \
case M_C(name, _EMPTY): break; \
case M_F(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_EQUAL_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
return true; \
}
@@ -443,14 +442,14 @@ static inline void \
/* Define the HASH function. */
#define M_VAR1ANT_DEFINE_HASH(name, ...) \
static inline size_t M_C(name, _hash)(M_C(name,_ct) const e1) { \
M_INLINE size_t M_F(name, _hash)(M_F(name,_ct) const e1) { \
M_VAR1ANT_CONTRACT(name, e1); \
M_HASH_DECL(hash); \
M_HASH_UP (hash, (unsigned int) (e1 -> type)); \
switch (e1->type) { \
case M_C(name, _EMPTY): break; \
case M_F(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_HASH_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
return M_HASH_FINAL (hash); \
}
@@ -463,16 +462,16 @@ static inline void \
/* Define the INIT_MOVE function. */
#define M_VAR1ANT_DEFINE_INIT_MOVE(name, ...) \
static inline void \
M_C(name, _init_move)(M_C(name,_ct) el, M_C(name,_ct) org) { \
M_INLINE void \
M_F(name, _init_move)(M_F(name,_ct) el, M_F(name,_ct) org) { \
M_VAR1ANT_CONTRACT(name, org); \
el -> type = org -> type; \
switch (el->type) { \
case M_C(name, _EMPTY): break; \
case M_F(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_INIT_MOVE_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
org -> type = M_C(name, _EMPTY); \
org -> type = M_F(name, _EMPTY); \
}
#define M_VAR1ANT_DEFINE_INIT_MOVE_FUNC(name, a) \
@@ -487,12 +486,12 @@ static inline void \
It can be optimized if both types are the same.
*/
#define M_VAR1ANT_DEFINE_MOVE(name, ...) \
static inline void \
M_C(name, _move)(M_C(name,_ct) el, M_C(name,_ct) org) { \
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_C(name, _clear)(el); \
M_C(name, _init_move)(el , org); \
M_F(name, _clear)(el); \
M_F(name, _init_move)(el , org); \
}
@@ -501,11 +500,11 @@ static inline void \
M_MAP2(M_VAR1ANT_DEFINE_MOVER_FUNC, name, __VA_ARGS__)
#define M_VAR1ANT_DEFINE_MOVER_FUNC(name, a) \
static inline void \
M_C3(name, _move_, M_VAR1ANT_GET_FIELD a)(M_C(name,_ct) my, \
M_INLINE void \
M_C3(name, _move_, M_VAR1ANT_GET_FIELD a)(M_F(name,_ct) my, \
M_VAR1ANT_GET_TYPE a M_VAR1ANT_GET_FIELD a ) { \
M_VAR1ANT_CONTRACT(name, my); \
M_C(name, _clear)(my); \
M_F(name, _clear)(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, \
@@ -515,29 +514,29 @@ static inline void \
/* Define the SWAP function */
#define M_VAR1ANT_DEFINE_SWAP(name, ...) \
static inline void \
M_C(name, _swap)(M_C(name,_ct) el1, M_C(name,_ct) el2) { \
M_INLINE void \
M_F(name, _swap)(M_F(name,_ct) el1, M_F(name,_ct) el2) { \
M_VAR1ANT_CONTRACT(name, el1); \
M_VAR1ANT_CONTRACT(name, el2); \
if (el1->type == el2->type) { \
switch (el1->type) { \
case M_C(name, _EMPTY): break; \
case M_F(name, _EMPTY): break; \
M_MAP2(M_VAR1ANT_DEFINE_INIT_SWAP_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
} else { \
M_C(name,_ct) tmp; \
M_F(name,_ct) tmp; \
M_VAR1ANT_IF_ALL(INIT_MOVE, __VA_ARGS__) \
( /* NOTE: Slow implementation */ \
M_C(name, _init_move)(tmp, el1); \
M_C(name, _init_move)(el1, el2); \
M_C(name, _init_move)(el2, tmp); \
M_F(name, _init_move)(tmp, el1); \
M_F(name, _init_move)(el1, el2); \
M_F(name, _init_move)(el2, tmp); \
, \
/* NOTE: Very slow implementation */ \
M_C(name, _init_set)(tmp, el1); \
M_C(name, _set)(el1, el2); \
M_C(name, _set)(el2, tmp); \
M_C(name, _clear)(tmp); \
M_F(name, _init_set)(tmp, el1); \
M_F(name, _set)(el1, el2); \
M_F(name, _set)(el2, tmp); \
M_F(name, _clear)(tmp); \
) \
} \
}
@@ -551,17 +550,17 @@ static inline void \
/* Define the GET_STR function */
#define M_VAR1ANT_DEFINE_GET_STR(name, ...) \
static inline void M_C(name, _get_str)(m_string_t str, \
M_C(name,_ct) const el, \
M_INLINE void M_F(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 *); \
func = append ? m_string_cat_cstr : m_string_set_cstr; \
switch (el->type) { \
case M_C(name, _EMPTY): func(str, "@EMPTY@"); break; \
case M_F(name, _EMPTY): func(str, "@EMPTY@"); break; \
M_MAP2(M_VAR1ANT_DEFINE_GET_STR_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
m_string_push_back (str, '@'); \
}
@@ -575,7 +574,7 @@ static inline void \
/* Define the PARSE_STR function */
#define M_VAR1ANT_DEFINE_PARSE_STR(name, ...) \
static inline bool M_C(name, _parse_str)(M_C(name,_ct) el, \
M_INLINE bool M_F(name, _parse_str)(M_F(name,_ct) el, \
const char str[], \
const char **endp) { \
M_VAR1ANT_CONTRACT(name, el); \
@@ -584,7 +583,7 @@ static inline void \
char variantTypeBuf[M_USE_IDENTIFIER_ALLOC+1]; \
int c = *str++; \
unsigned int i = 0; \
M_C(name, _reset)(el); \
M_F(name, _reset)(el); \
if (c != '@') goto exit; \
/* First read the name of the type */ \
c = *str++; \
@@ -597,7 +596,7 @@ static inline void \
M_ASSERT(i < sizeof(variantTypeBuf)); \
/* In function of the type */ \
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
el->type = M_C(name, _EMPTY); \
el->type = M_F(name, _EMPTY); \
} \
M_MAP2(M_VAR1ANT_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \
else goto exit; \
@@ -618,14 +617,14 @@ static inline void \
/* Define the OUT_STR function */
#define M_VAR1ANT_DEFINE_OUT_STR(name, ...) \
static inline void M_C(name, _out_str)(FILE *f, \
M_C(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) { \
case M_C(name, _EMPTY): fprintf(f, "@EMPTY@"); break; \
case M_F(name, _EMPTY): fprintf(f, "@EMPTY@"); break; \
M_MAP2(M_VAR1ANT_DEFINE_OUT_STR_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
fputc ('@', f); \
}
@@ -639,12 +638,12 @@ static inline void \
/* Define the IN_STR function */
#define M_VAR1ANT_DEFINE_IN_STR(name, ...) \
static inline bool M_C(name, _in_str)(M_C(name,_ct) el, \
M_INLINE bool M_F(name, _in_str)(M_F(name,_ct) el, \
FILE *f) { \
M_VAR1ANT_CONTRACT(name, el); \
M_ASSERT (f != NULL); \
M_ASSERT (f != NULL); \
char variantTypeBuf[M_USE_IDENTIFIER_ALLOC+1]; \
M_C(name, _reset)(el); \
M_F(name, _reset)(el); \
if (fgetc(f) != '@') return false; \
/* First read the name of the type */ \
bool b = true; \
@@ -659,7 +658,7 @@ static inline void \
M_ASSERT(i < sizeof(variantTypeBuf)); \
/* In function of the type */ \
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
el->type = M_C(name, _EMPTY); \
el->type = M_F(name, _EMPTY); \
} \
M_MAP2(M_VAR1ANT_DEFINE_IN_STR_FUNC , name, __VA_ARGS__) \
else { b = false; } \
@@ -681,9 +680,9 @@ static inline void \
/* Define the OUT_SERIAL function */
#define M_VAR1ANT_DEFINE_OUT_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _out_serial)(m_serial_write_t f, \
M_C(name,_ct) const el) { \
M_INLINE m_serial_return_code_t \
M_F(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[] = \
@@ -692,11 +691,11 @@ static inline void \
m_serial_local_t local; \
m_serial_return_code_t ret; \
switch (el->type) { \
case M_C(name, _EMPTY): \
case M_F(name, _EMPTY): \
return f->m_interface->write_variant_start(local, f, field_name, field_max, -1); \
break; \
M_MAP2(M_VAR1ANT_DEFINE_OUT_SERIAL_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
ret |= f->m_interface->write_variant_end(local, f); \
return ret & M_SERIAL_FAIL; \
@@ -712,8 +711,8 @@ static inline void \
/* Define the IN_SERIAL function */
#define M_VAR1ANT_DEFINE_IN_SERIAL(name, ...) \
static inline m_serial_return_code_t \
M_C(name, _in_serial)(M_C(name,_ct) el, \
M_INLINE m_serial_return_code_t \
M_F(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__); \
@@ -723,14 +722,14 @@ static inline void \
m_serial_local_t local; \
m_serial_return_code_t ret; \
int id = -1; \
M_C(name, _reset)(el); \
M_F(name, _reset)(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); \
el->type = (enum M_C(name, _enum))(id+1); \
el->type = (enum M_F(name, _enum))(id+1); \
switch (id+1) { \
M_MAP2(M_VAR1ANT_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \
default: M_ASSERT(false); break; \
default: M_ASSUME(false); break; \
} \
if (ret == M_SERIAL_OK_DONE) \
ret = f->m_interface->read_variant_end(local, f); \
@@ -746,13 +745,16 @@ static inline void \
/* Define the RESET function */
#define M_VAR1ANT_DEFINE_RESET_FUNC(name, ...) \
static inline void M_C(name, _reset)(M_C(name,_ct) my) \
M_INLINE void M_F(name, _reset)(M_F(name,_ct) my) \
{ \
M_VAR1ANT_CONTRACT(name, my); \
M_C(name, _clear)(my); \
M_C(name, _init)(my); \
M_F(name, _clear)(my); \
M_F(name, _init)(my); \
} \
/********************************** INTERNAL *********************************/
/* deferred evaluation of the oplist */
#define M_VAR1ANT_OPLIST_P1(arg) M_VAR1ANT_OPLIST_P2 arg
@@ -766,30 +768,30 @@ static inline void \
/* Define the oplist */
#define M_VAR1ANT_OPLIST_P3(name, ...) \
(INIT(M_C(name,_init)), \
INIT_SET(M_C(name, _init_set)), \
SET(M_C(name,_set)), \
CLEAR(M_C(name, _clear)), \
RESET(M_C(name, _reset)), \
(INIT(M_F(name,_init)), \
INIT_SET(M_F(name, _init_set)), \
SET(M_F(name,_set)), \
CLEAR(M_F(name, _clear)), \
RESET(M_F(name, _reset)), \
NAME(name), \
TYPE(M_C(name,_ct)), \
EMPTY_P(M_C(name,_empty_p)), \
M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_C(name, _hash)),), \
M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_C(name, _equal_p)),), \
M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(M_C(name, _get_str)),), \
M_IF_METHOD2_ALL(PARSE_STR, INIT, __VA_ARGS__)(PARSE_STR(M_C(name, _parse_str)),), \
M_IF_METHOD2_ALL(IN_STR, INIT, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_C(name, _out_str)),), \
M_IF_METHOD2_ALL(IN_SERIAL, INIT, __VA_ARGS__)(IN_SERIAL(M_C(name, _in_serial)),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(M_C(name, _out_serial)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_C(name, _init_move)),), \
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_C(name, _swap)),), \
M_IF_METHOD(NEW, M_RET_ARG1(__VA_ARGS__,))(NEW(M_DELAY2(M_GET_NEW) M_RET_ARG1(__VA_ARGS__,)),), \
M_IF_METHOD(REALLOC, M_RET_ARG1(__VA_ARGS__,))(REALLOC(M_DELAY2(M_GET_REALLOC) M_RET_ARG1(__VA_ARGS__,)),), \
M_IF_METHOD(DEL, M_RET_ARG1(__VA_ARGS__,))(DEL(M_DELAY2(M_GET_DEL) M_RET_ARG1(__VA_ARGS__,)),), \
TYPE(M_F(name,_ct)), \
EMPTY_P(M_F(name,_empty_p)), \
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(M_F(name, _get_str)),), \
M_IF_METHOD2_ALL(PARSE_STR, INIT, __VA_ARGS__)(PARSE_STR(M_F(name, _parse_str)),), \
M_IF_METHOD2_ALL(IN_STR, INIT, __VA_ARGS__)(IN_STR(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(M_F(name, _in_serial)),), \
M_IF_METHOD_ALL(OUT_SERIAL, __VA_ARGS__)(OUT_SERIAL(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(M_F(name, _move)),), \
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_F(name, _swap)),), \
)
/********************************** INTERNAL *********************************/
/* Macros for testing for method presence */
#define M_VAR1ANT_TEST_METHOD_P2(method, f, t, op) \
M_TEST_METHOD_P(method, op)
@@ -805,6 +807,9 @@ static inline void \
#define M_VAR1ANT_IF_ALL2(method1, method2, ...) \
M_IF(M_REDUCE2(M_VAR1ANT_TEST_METHOD2_P, M_AND, (method1, method2), __VA_ARGS__))
/********************************** INTERNAL *********************************/
#if M_USE_SMALL_NAME
#define VARIANT_DEF2 M_VARIANT_DEF2
#define VARIANT_DEF2_AS M_VARIANT_DEF2_AS
+144 -18
View File
@@ -1,7 +1,7 @@
/*
* M*LIB / WORKER - Extra worker interface
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -38,11 +38,11 @@
#include "m-atomic.h"
#include "m-buffer.h"
#include "m-mutex.h"
#include "m-thread.h"
/* Include needed system header for detection of how many cores are available in the system */
#if defined(_WIN32)
# include <windows.h>
# include <sysinfoapi.h>
#elif (defined(__APPLE__) && defined(__MACH__)) \
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
@@ -174,8 +174,132 @@ typedef struct m_worker_sync_s {
struct m_worker_s *worker; // Reference to the pool of workers
} m_worker_sync_t[1];
/* Return the number of cores available in the system */
static inline int
/* Extend m_worker_spawn by defining a specialization function
with the given arguments.
Generate the needed encapsulation for the user.
USAGE: name, oplists of arguments */
#define M_WORKER_SPAWN_DEF2(name, ...) \
M_BEGIN_PROTECTED_CODE \
M_WORK3R_SPAWN_EXTEND_P1( (name, M_MAP_C(M_WORK3R_SPAWN_EXTEND_P0, __VA_ARGS__) ) ) \
M_END_PROTECTED_CODE
/* Output a valid oplist with the given type.
input is (fieldname, type) or (fieldname, type, oplist)
Output shall be : M_OPEXTEND(M_GLOBAL_OPLIST_OR_DEF(type_or_oplist)(), TYPE(type)) / M_OPEXTEND(oplist, TYPE(type))
*/
#define M_WORK3R_SPAWN_EXTEND_P0(...) M_BY_NARGS(M_WORK3R_SPAWN_EXTEND_P0, M_ID __VA_ARGS__) __VA_ARGS__
#define M_WORK3R_SPAWN_EXTEND_P0__2(field, type) M_OPEXTEND(M_GLOBAL_OPLIST_OR_DEF(type)(), TYPE(type))
#define M_WORK3R_SPAWN_EXTEND_P0__3(field, type, oplist) M_IF_OPLIST(oplist)(M_WORK3R_SPAWN_EXTEND_P0__3_OK, M_WORK3R_SPAWN_EXTEND_P0__3_KO)(field, type, oplist)
#define M_WORK3R_SPAWN_EXTEND_P0__3_OK(field, type, oplist) M_OPEXTEND(oplist, TYPE(type))
#define M_WORK3R_SPAWN_EXTEND_P0__3_KO(field, type, oplist) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(M_WORKER_SPAWN_EXTEND): the argument is not a valid oplist: " M_MAP(M_AS_STR, oplist))
/* Deferred evaluation for the definition,
so that all arguments are evaluated before further expansion */
#define M_WORK3R_SPAWN_EXTEND_P1(arg) M_ID( M_WORK3R_SPAWN_EXTEND_P2 arg )
/* Validate the oplist before going further */
#define M_WORK3R_SPAWN_EXTEND_P2(name, ...) \
M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__)) \
(M_WORK3R_SPAWN_EXTEND_P3, M_WORK3R_SPAWN_EXTEND_FAILURE)(name, __VA_ARGS__)
/* Stop processing with a compilation failure */
#define M_WORK3R_SPAWN_EXTEND_FAILURE(name, ...) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, \
"(M_WORKER_SPAWN_EXTEND): at least one of the given argument is not a valid oplist: " \
M_MAP(M_AS_STR, __VA_ARGS__))
/* Define the extension of spawn */
#define M_WORK3R_SPAWN_EXTEND_P3(name, ...) \
M_WORK3R_SPAWN_EXTEND_DEF_TYPE(name, __VA_ARGS__) \
M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK(name, __VA_ARGS__) \
M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE(name, __VA_ARGS__) \
/* Define the type */
#define M_WORK3R_SPAWN_EXTEND_DEF_TYPE(name, ...) \
typedef void (*M_C3(m_worker_,name, _callback_ct))(M_MAP_C(M_WORK3R_SPAWN_EXTEND_DEF_TYPE_TYPE, __VA_ARGS__)); \
struct M_C3(m_worker_, name, _s){ \
M_C3(m_worker_, name, _callback_ct) callback; \
M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_TYPE_FIELD, data, __VA_ARGS__) \
};
#define M_WORK3R_SPAWN_EXTEND_DEF_TYPE_FIELD(data, num, oplist) \
M_GET_TYPE oplist M_C(field, num);
#define M_WORK3R_SPAWN_EXTEND_DEF_TYPE_TYPE(oplist) \
M_GET_TYPE oplist
/* Define the callback */
#define M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK(name, ...) \
M_INLINE void \
M_C3(m_work3r_, name, _clear)(struct M_C3(m_worker_, name, _s) *p) \
{ \
M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_CLEAR, data, __VA_ARGS__) \
/* TODO: Overload */ \
M_MEMORY_DEL(p); \
} \
\
M_INLINE void \
M_C3(m_work3r_, name, _callback)(void *data) \
{ \
struct M_C3(m_worker_, name, _s) *p = (struct M_C3(m_worker_, name, _s) *) data; \
(*p->callback)( \
M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_FIELD, data, __VA_ARGS__) \
); \
M_C3(m_work3r_, name, _clear)(p); \
}
#define M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_FIELD(data, num, oplist) \
p->M_C(field, num)
#define M_WORK3R_SPAWN_EXTEND_DEF_CALLBACK_CLEAR(data, num, oplist) \
M_CALL_CLEAR(oplist, p->M_C(field, num)) ;
/* Define the emplace like spawn method */
#define M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE(name, ...) \
M_INLINE void \
M_C(m_worker_spawn_, name)(m_worker_sync_t block, M_C3(m_worker_, name, _callback_ct) callback, \
M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD, data, __VA_ARGS__) \
) \
{ \
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)); \
if (M_UNLIKELY_NOMEM(p == NULL)) { \
M_MEMORY_FULL(sizeof (struct M_C3(m_worker_, name, _s))); \
} \
p->callback = callback; \
M_MAP3(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_COPY, 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) { \
atomic_fetch_add (&block->num_spawn, 1); \
return; \
} \
/* No worker available now. Call the function ourself */ \
/* But before clear the allocated data */ \
M_C3(m_work3r_, name, _clear)(p); \
} \
/* No worker available. Call the function ourself */ \
(*callback) ( \
M_MAP3_C(M_WORK3R_SPAWN_EXTEND_DEF_EMPLACE_FIELD_ALONE, data, __VA_ARGS__) \
); \
}
#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) \
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) \
M_C(param, num)
/* Return the number of CPU cores available in the system.
Works for WINDOWS, MACOS, *BSD, LINUX.
*/
M_INLINE int
m_work3r_get_cpu_count(void)
{
#if defined(_WIN32)
@@ -193,6 +317,8 @@ m_work3r_get_cpu_count(void)
return M_MAX(1, count);
#elif defined (_SC_NPROCESSORS_ONLN)
return (int) sysconf(_SC_NPROCESSORS_ONLN);
#elif defined (_SC_NPROCESSORS_CONF)
return (int) sysconf(_SC_NPROCESSORS_CONF);
#else
return 1;
#endif
@@ -206,7 +332,7 @@ m_work3r_get_cpu_count(void)
#endif
/* Execute the registered work order **synchronously** */
static inline void
M_INLINE void
m_work3r_exec(m_work3r_order_ct *w)
{
M_ASSERT (w!= NULL && w->block != NULL);
@@ -230,7 +356,7 @@ m_work3r_exec(m_work3r_order_ct *w)
/* The worker thread main loop*/
static inline void
M_INLINE void
m_work3r_thread(void *arg)
{
// Get back the given argument
@@ -271,7 +397,7 @@ m_work3r_thread(void *arg)
@resetFunc: function to reset the state of a worker between work orders (or NULL if none)
@clearFunc: function to clear the state of a worker before terminaning (or NULL if none)
*/
static inline void
M_INLINE void
m_worker_init(m_worker_t g, int numWorker, unsigned int extraQueue, void (*resetFunc)(void), void (*clearFunc)(void))
{
M_ASSERT (numWorker >= -1);
@@ -284,7 +410,7 @@ m_worker_init(m_worker_t g, int numWorker, unsigned int extraQueue, void (*reset
M_ASSERT(numWorker >= 0);
size_t numWorker_st = (size_t) numWorker;
g->worker = M_MEMORY_REALLOC(m_work3r_thread_ct, NULL, numWorker_st);
if (g->worker == NULL) {
if (M_UNLIKELY_NOMEM (g->worker == NULL)) {
M_MEMORY_FULL(sizeof (m_work3r_thread_ct) * numWorker_st);
return;
}
@@ -311,7 +437,7 @@ m_worker_init(m_worker_t g, int numWorker, unsigned int extraQueue, void (*reset
#define m_worker_init(...) m_worker_init(M_DEFAULT_ARGS(5, (0, 0, NULL, NULL), __VA_ARGS__))
/* Clear of the worker module (destructor) */
static inline void
M_INLINE void
m_worker_clear(m_worker_t g)
{
M_ASSERT (m_work3r_queue_empty_p (g->queue_g));
@@ -336,7 +462,7 @@ m_worker_clear(m_worker_t g)
/* Start a new collaboration between workers of pool 'g'
by defining the synchronization point 'block' */
static inline void
M_INLINE void
m_worker_start(m_worker_sync_t block, m_worker_t g)
{
atomic_init (&block->num_spawn, 0);
@@ -349,7 +475,7 @@ m_worker_start(m_worker_sync_t block, m_worker_t g)
The synchronization point is defined a 'block'
The work order if composed of the function 'func' and its 'data'
*/
static inline void
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 };
@@ -367,7 +493,7 @@ m_worker_spawn(m_worker_sync_t block, void (*func)(void *data), void *data)
#if M_USE_WORKER_CLANG_BLOCK
/* Spawn or not the given work order to workers,
or do it ourself if no worker is available */
static inline void
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 };
@@ -386,7 +512,7 @@ m_work3r_spawn_block(m_worker_sync_t block, void (^func)(void *data), void *data
#if M_USE_WORKER_CPP_FUNCTION
/* Spawn or not the given work order to workers,
or do it ourself if no worker is available */
static inline void
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 };
@@ -403,7 +529,7 @@ m_work3r_spawn_function(m_worker_sync_t block, std::function<void(void *data)> f
#endif
/* Test if all work orders of the given synchronization point are finished */
static inline bool
M_INLINE bool
m_worker_sync_p(m_worker_sync_t block)
{
/* If the number of spawns is greated than the number
@@ -413,7 +539,7 @@ m_worker_sync_p(m_worker_sync_t block)
}
/* Wait for all work orders of the given synchronization point to be finished */
static inline void
M_INLINE void
m_worker_sync(m_worker_sync_t block)
{
M_WORK3R_DEBUG ("Waiting for thread terminasion.\n");
@@ -428,7 +554,7 @@ m_worker_sync(m_worker_sync_t block)
}
/* Flush any work order in the queue ourself if some remains.*/
static inline void
M_INLINE void
m_worker_flush(m_worker_t g)
{
m_work3r_order_ct w;
@@ -440,7 +566,7 @@ m_worker_flush(m_worker_t g)
/* Return the number of workers */
static inline size_t
M_INLINE size_t
m_worker_count(m_worker_t g)
{
return g->numWorker_g + 1;
+2 -2
View File
@@ -1,4 +1,4 @@
REM Copyright (c) 2017-2022, Patrick Pelissier
REM Copyright (c) 2017-2023, Patrick Pelissier
REM All rights reserved.
REM
REM Redistribution and use in source and binary forms, with or without
@@ -66,7 +66,7 @@ for %%f in (test-*.c) do (
REM Enable warnings and basic optimization
REM Inform M*LIB to use Annex K by defining __STDC_WANT_LIB_EXT1__
echo Compiling %%f with %compiler%
%compiler% /I.. /O2 /W3 /std:c++14 /Zc:__cplusplus /Zc:preprocessor /D__STDC_WANT_LIB_EXT1__ test.cpp > %%f.log 2>&1 || (
%compiler% /I.. /O2 /W3 /std:c++14 /Zc:__cplusplus /Zc:preprocessor /D__STDC_WANT_LIB_EXT1__ /EHsc test.cpp > %%f.log 2>&1 || (
echo *** BUILD ERROR for %%f *** >> %%f.log
type %%f.log
EXIT /B 1
+37 -27
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2022, Patrick Pelissier
# Copyright (c) 2017-2023, Patrick Pelissier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -43,16 +43,16 @@ all:
#####################################################################################################
.POSIX:
.PHONY: scan-build sanitize check clean *.log coverage synthesis
.PHONY: scan-build sanitize check clean *.log coverage synthesis.synt
.SUFFIXES:
.SUFFIXES: .c .exe .log .depend .gcov .analyzer .cpp .fuzz
.SECONDARY:
.c.exe:
$(CC) $(CFLAGS) $(CPPFLAGS) -g $(LDFLAGS) $< -o $@
$(CC) $(CFLAGS) $(CPPFLAGS) -g $< -o $@ $(LDFLAGS)
.cpp.exe:
$(CXX) $(WCFLAGS_CPP) -Wno-sign-conversion -Wno-inline $(XCFLAGS) $(CPPFLAGS) -g $(LDFLAGS) $< -o $@
$(CXX) $(WCFLAGS_CPP) -Wno-sign-conversion -Wno-inline $(XCFLAGS) $(CPPFLAGS) -g $< -o $@ $(LDFLAGS)
.c.depend:
$(CC) $(CPPFLAGS) -MM -MT `echo $< |$(SED) 's/\.c/\.exe/g'` $< > $@
@@ -68,9 +68,9 @@ all:
awk ' printit { print } /END_COVERAGE/ { printit=1}' $< > $<.end.c
$(CC) $(CPPFLAGS) -E -DNDEBUG -DWITHIN_COVERAGE $< |grep -v '^#' |awk ' /END_COVERAGE/ { printit=0} printit { print } /START_COVERAGE/ { printit=1 }' |perl -p -e "s/;(?![\"'])/;\n/g ; s/}(?![\"'])/}\n/g ; s/{(?![\"'])/{\n/g" > $<.middle.c
cat $<.first.c $<.middle.c $<.end.c > $<.c
$(CC) $(CPPFLAGS) $(LDFLAGS) -ftest-coverage -fprofile-arcs -O -g $<.c -o $<.exe
$(CC) $(CPPFLAGS) -DBUILD_COVERAGE -ftest-coverage -fprofile-arcs -O0 -g $<.c -o $<.exe $(LDFLAGS)
$(LOG_COMPILER) ./$<.exe
LANG=C gcov $<.gcno > `basename $< .c`.synt
LANG=C gcov -b $<.gcno > `basename $< .c`.synt
cat `basename $< .c`.synt
mv $<.c.gcov `basename $< .c`.gcov
$(RM) $<.first.c $<.end.c $<.middle.c $<.c $<.exe
@@ -102,7 +102,7 @@ exe: depend
@echo All executable generated
check: depend
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.log/g'` `ls fail-*.c|$(SED) 's/\.c/\.log/g'` CC="$(CC)" CFLAGS="$(CFLAGS)"
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.log/g'` `ls fail-*.c|$(SED) 's/\.c/\.log.err/g'` CC="$(CC)" CFLAGS="$(CFLAGS)"
@echo All tests passed
check-stl: depend
@@ -110,7 +110,7 @@ check-stl: depend
@echo All comparison against C++ STL passed
clean:
$(RM) *.exe *.s *~ *.o *.log a*.dat
$(RM) *.exe *.s *~ *.o *.log *.log.err a*.dat
$(RM) *.gcda *.gcno *.gcov all.info test.tar *.synt clang_output_* *.c.c *.end.c *.first.c *.middle.c *.analyzer
$(RMDIR) coverage coverage-dir mlib-report
$(RMDIR) fuzzer-testsuite fuzzer-findings
@@ -126,17 +126,17 @@ distclean: clean
# TODO: should grep failure message?
FAIL_BASE=
FAIL_SEQ=
fail-generic.log:
fail-generic.log.err:
for i in `seq 1 $(FAIL_SEQ)` ; do \
if $(CC) $(CFLAGS) $(CPPFLAGS) -g -c $(FAIL_BASE).c -o tmp.o -DTEST=$$i 2> $(FAIL_BASE)-$$i.log ; then echo "$(FAIL_BASE) -- TEST $$i : ERROR (compilation succeeded)" ; false ; elif grep -q "M_LIB_" $(FAIL_BASE)-$$i.log ; then echo "$(FAIL_BASE) -- TEST $$i : OK" ; else echo "$(FAIL_BASE) -- TEST $$i : WARNING (Not a M*LIB detected error)" ; fi \
done
fail-no-oplist.log:
@$(MAKE) fail-generic.log FAIL_BASE=fail-no-oplist FAIL_SEQ=44
fail-chain-oplist.log:
@$(MAKE) fail-generic.log FAIL_BASE=fail-chain-oplist FAIL_SEQ=24
fail-incompatible.log:
@$(MAKE) fail-generic.log FAIL_BASE=fail-incompatible FAIL_SEQ=24
fail-no-oplist.log.err:
@$(MAKE) fail-generic.log.err FAIL_BASE=fail-no-oplist FAIL_SEQ=44
fail-chain-oplist.log.err:
@$(MAKE) fail-generic.log.err FAIL_BASE=fail-chain-oplist FAIL_SEQ=24
fail-incompatible.log.err:
@$(MAKE) fail-generic.log.err FAIL_BASE=fail-incompatible FAIL_SEQ=24
#####################################################################################################
@@ -158,6 +158,9 @@ gene-bitset:
gene-dict:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mdict.c && less tgen-mdict.s
gene-map:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mmap.c && less tgen-mmap.s
gene-queue:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-queue.c && less tgen-queue.s
@@ -167,14 +170,19 @@ gene-string:
gene-serial:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S -std=c11 tgen-mserial.c && less tgen-mserial.s
gene-try:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S -std=c11 tgen-try.c && less tgen-try.s
gene-tuple:
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S -std=c11 tgen-tuple.c && less tgen-tuple.s
#####################################################################################################
# Coverage
#####################################################################################################
coverage:
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.gcov/g'` CC="cc -std=c11"
$(MAKE) synthesis.synt check-synthesis
@$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.gcov/g'` CC="cc -std=c11"
@$(MAKE) synthesis.synt
SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-ARRAY test-marray.c.c test-marray.synt \
@@ -182,7 +190,7 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-BBPTREE test-mbptree.c test-mbptree.synt \
M-BUFFER test-mbuffer.c.c test-mbuffer.synt \
M-CONCURRENT test-mconcurrent.c.c test-mconcurrent.synt \
M-CORE test-mcore.c.c test-mcore.synt \
M-CORE ../m-core.h test-mcore.synt \
M-DEQUE test-mdeque.c.c test-mdeque.synt \
M-DICT test-mdict.c.c test-mdict.synt \
M-FUNCOBJ test-mfuncobj.c.c test-mfuncobj.synt \
@@ -191,7 +199,6 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
M-I-SHARED test-mishared.c.c test-mishared.synt \
M-LIST test-mlist.c.c test-mlist.synt \
M-MEMPOOL test-mmempool.c.c test-mmempool.synt \
M-MUTEX ../m-mutex.h test-mmutex.synt \
M-PRIOQUEUE test-mprioqueue.c.c test-mprioqueue.synt \
M-RBTREE test-mrbtree.c.c test-mrbtree.synt \
M-SERIAL-BIN ../m-serial-bin.h test-mserial-bin.synt \
@@ -200,25 +207,28 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.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-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
synthesis.synt:
@printf "+-------------------------------+-------------------------------+\n"
@printf "+------------------+------------------+------------------+------------------+\n"
@printf "| Functions | Instruction Cov. | Decision Cov. | MCDC Cov. |\n"
@printf "+------------------+------------------+------------------+------------------+\n"
@for i in $(SYNTHESIS_DATA) ; do \
if test "$$j" = "" ; then j=$$i ; continue ; fi; \
if test "$$k" = "" ; then k=$$i ; continue ; fi; \
printf "|\t%16.16s\t|\t%16.16s\t|\n" "$$j" "`grep -v Creating $$i|grep $$k -F -A1 |tail -1|cut -f2 -d':'`"; \
printf "| %16.16s | %16.16s | %16.16s | %16.16s |\n" "$$j" \
"`grep -v Creating $$i|grep $$k -F -A1 |tail -1|cut -f2 -d':'`" \
"`grep -v Creating $$i|grep $$k -F -A2 |tail -1|cut -f2 -d':'`" \
"`grep -v Creating $$i|grep $$k -F -A3 |tail -1|cut -f2 -d':'`"; \
j=""; \
k=""; \
done > synthesis.synt
@cat synthesis.synt
@printf "+-------------------------------+-------------------------------+\n"
# Compare the computed coverage against the reference coverage (synthesis.ref), allowing only increase of coverage
check-synthesis: synthesis.synt
@paste synthesis.ref synthesis.synt |awk '($$2 != $$9) {err=1} { n1=$$4*$$6/100 ; n2=$$11*$$13/100; if (n1 > n2) { printf "ERROR for %s: new coverage (%d) is lower than reference (%d)\n",$$2,n2,n1 ; err=2 } } END {exit err}'
@printf "+------------------+------------------+------------------+------------------+\n"
#####################################################################################################
@@ -246,7 +256,7 @@ sanitize:
awk ' printit { print } /END_COVERAGE/ { printit=1}' $< > $<.end.c
$(CC) $(CPPFLAGS) -E -DNDEBUG -DWITHIN_COVERAGE $< |grep -v '^#' |awk ' /END_COVERAGE/ { printit=0} printit { print } /START_COVERAGE/ { printit=1 }' |perl -p -e "s/;(?![\"'])/;\n/g ; s/}(?![\"'])/}\n/g ; s/{(?![\"'])/{\n/g" > $<.middle.c
cat $<.first.c $<.middle.c $<.end.c > $<.c
$(CC) $(CPPFLAGS) $(LDFLAGS) -fanalyzer -Wall -W -O2 -g $<.c -o $<.exe 2> `basename $< .c`.analyzer
$(CC) $(CPPFLAGS) -fanalyzer -Wall -W -Wno-analyzer-malloc-leak -O2 -g $<.c -o $<.exe 2> `basename $< .c`.analyzer $(LDFLAGS)
$(RM) $<.first.c $<.end.c $<.middle.c $<.c $<.exe
analyze:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022-2022, Patrick Pelissier
* Copyright (c) 2021-2023-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022 Patrick Pelissier
* Copyright (c) 2021-2023 Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022-2022, Patrick Pelissier
* Copyright (c) 2021-2023-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Patrick Pelissier
* Copyright (c) 2021-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+101 -45
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -21,6 +21,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
/* Coverage helper header.
Computing coverage for a generic macro expansing library is not trivial
as there is no tool to do it normally.
@@ -40,55 +42,103 @@
It'll contain the macro expanded & annotated.
*/
#if defined(WITHIN_COVERAGE) || defined (BUILD_COVERAGE)
/* For coverage build, expand the typical macros assert, avoid expanding then
to not disturb coverage measurement */
#undef M_ASSERT
#define M_ASSERT(...) (void)0
#undef M_ASSERT_SLOW
#define M_ASSERT_SLOW(...) (void)0
#undef M_ASSERT_INIT
#define M_ASSERT_INIT(...) (void)0
#undef M_ASSERT_INDEX
#define M_ASSERT_INDEX(...) (void)0
#undef M_ASSUME
#define M_ASSUME(...) (void)0
#undef M_STATIC_ASSERT
#define M_STATIC_ASSERT(...) (void)0
#undef M_ARRA4_CONTRACT
#define M_ARRA4_CONTRACT(...) (void)0
#undef M_B1TSET_CONTRACT
#define M_B1TSET_CONTRACT(...) (void)0
#undef M_BPTR33_NODE_CONTRACT
#define M_BPTR33_NODE_CONTRACT(...) (void)0
#undef M_BPTR33_CONTRACT
#define M_BPTR33_CONTRACT(...) (void)0
#undef M_BUFF3R_CONTRACT
#define M_BUFF3R_CONTRACT(...) (void)0
#undef M_BUFF3R_PROTECTED_CONTRACT
#define M_BUFF3R_PROTECTED_CONTRACT(...) (void)0
#undef M_QU3UE_MPMC_CONTRACT
#define M_QU3UE_MPMC_CONTRACT(...) (void)0
#undef M_QU3UE_MPMC_CONTRACT
#define M_QU3UE_MPMC_CONTRACT(...) (void)0
#undef M_QU3UE_SPSC_CONTRACT
#define M_QU3UE_SPSC_CONTRACT(...) (void)0
#undef M_C0NCURRENT_CONTRACT
#define M_C0NCURRENT_CONTRACT(...) (void)0
#undef M_D3QU3_CONTRACT
#define M_D3QU3_CONTRACT(...) (void)0
#undef M_D1CT_CONTRACT
#define M_D1CT_CONTRACT(...) (void)0
#undef M_D1CT_OA_CONTRACT
#define M_D1CT_OA_CONTRACT(...) (void)0
#undef M_GEN1NT_CONTRACT
#define M_GEN1NT_CONTRACT(...) (void)0
#undef M_IL1ST_CONTRACT
#define M_IL1ST_CONTRACT(...) (void)0
#undef M_IL1ST_NODE_CONTRACT
#define M_IL1ST_NODE_CONTRACT(...) (void)0
#undef M_L1ST_CONTRACT
#define M_L1ST_CONTRACT(...) (void)0
#undef M_L1ST_DUAL_PUSH_CONTRACT
#define M_L1ST_DUAL_PUSH_CONTRACT(...) (void)0
#undef M_M3MPOOL_CONTRACT
#define M_M3MPOOL_CONTRACT(...) (void)0
#undef M_RBTR33_CONTRACT
#define M_RBTR33_CONTRACT(...) (void)0
#undef M_RBTR33_CONTRACT_NODE
#define M_RBTR33_CONTRACT_NODE(...) (void)0
#undef M_SHAR3D_CONTRACT
#define M_SHAR3D_CONTRACT(...) (void)0
#undef M_SHAR3D_RESOURCE_CONTRACT
#define M_SHAR3D_RESOURCE_CONTRACT(...) (void)0
#undef M_SNAPSH0T_SPSC_FLAGS_CONTRACT
#define M_SNAPSH0T_SPSC_FLAGS_CONTRACT(...) (void)0
#undef M_SNAPSH0T_SPSC_CONTRACT
#define M_SNAPSH0T_SPSC_CONTRACT(...) (void)0
#undef M_SNAPSH0T_SPMC_INT_CONTRACT
#define M_SNAPSH0T_SPMC_INT_CONTRACT(...) (void)0
#undef M_SNAPSH0T_SPMC_CONTRACT
#define M_SNAPSH0T_SPMC_CONTRACT(...) (void)0
#undef M_STR1NG_CONTRACT
#define M_STR1NG_CONTRACT(...) (void)0
#undef M_BOUNDED_STR1NG_CONTRACT
#define M_BOUNDED_STR1NG_CONTRACT(...) (void)0
#undef M_TUPL3_CONTRACT
#define M_TUPL3_CONTRACT(...) (void)0
#undef M_VAR1ANT_CONTRACT
#define M_VAR1ANT_CONTRACT(...) (void)0
#undef M_TR33_NODE_CONTRACT
#define M_TR33_NODE_CONTRACT(...) (void)0
#undef M_TR33_CONTRACT
#define M_TR33_CONTRACT(...) (void)0
#undef M_TR33_IT_CONTRACT
#define M_TR33_IT_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
#endif
#ifdef WITHIN_COVERAGE
/* For coverage build, do not expand the
typical macros assert, NULL, etc. */
/* For coverage build, do not expand other likes NULL, etc. */
#undef assert
#undef M_ASSERT
#undef M_ASSERT_SLOW
#undef M_ASSERT_INIT
#undef M_ASSERT_INDEX
#undef M_STATIC_FAILURE
#undef M_STATIC_ASSERT
#undef M_ASSUME
#undef NULL
#undef M_LIKELY
#undef M_UNLIKELY
#undef M_MEMORY_FULL
#undef M_SWAP
#undef M_ARRA4_CONTRACT
#undef M_B1TSET_CONTRACT
#undef M_BPTR33_NODE_CONTRACT
#undef M_BPTR33_NODE_CONTRACT
#undef M_BPTR33_CONTRACT
#undef M_BUFF3R_CONTRACT
#undef M_BUFF3R_PROTECTED_CONTRACT
#undef M_QU3UE_MPMC_CONTRACT
#undef M_QU3UE_MPMC_CONTRACT
#undef M_QU3UE_SPSC_CONTRACT
#undef M_QU3UE_SPSC_CONTRACT
#undef M_C0NCURRENT_CONTRACT
#undef M_D3QU3_CONTRACT
#undef M_D1CT_CONTRACT
#undef M_D1CT_OA_CONTRACT
#undef M_GEN1NT_CONTRACT
#undef M_IL1ST_CONTRACT
#undef M_IL1ST_NODE_CONTRACT
#undef M_L1ST_CONTRACT
#undef M_L1ST_DUAL_PUSH_CONTRACT
#undef M_M3MPOOL_CONTRACT
#undef M_RBTR33_CONTRACT
#undef M_RBTR33_CONTRACT_NODE
#undef M_SHAR3D_CONTRACT
#undef M_SHAR3D_RESOURCE_CONTRACT
#undef M_SNAPSH0T_SPSC_FLAGS_CONTRACT
#undef M_SNAPSH0T_SPSC_CONTRACT
#undef M_SNAPSH0T_SPMC_INT_CONTRACT
#undef M_SNAPSH0T_SPMC_CONTRACT
#undef M_STR1NG_CONTRACT
#undef M_BOUNDED_STR1NG_CONTRACT
#undef M_TUPL3_CONTRACT
#undef M_VAR1ANT_CONTRACT
#undef NDEBUG
#undef atomic_init
#undef atomic_load
@@ -101,9 +151,15 @@
#undef M_ASSIGN_CAST
#undef M_CHECK_DEFAULT_TYPE
#undef M_CHECK_SAME
#undef M_TR33_NODE_CONTRACT
#undef M_TR33_CONTRACT
#undef M_TR33_IT_CONTRACT
#undef isspace
#elif defined(BUILD_COVERAGE)
// Overload assert to avoid polluting final branch coverage
// No need to provide debug outputs. Just abort in case of error.
#undef assert
static void assert(int cond)
{
if (!cond) abort();
}
#else
/* For normal builds, empty macros which does nothing */
#define START_COVERAGE
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -724,5 +724,6 @@ int main(void)
test_insert();
test_string_utf8();
test_fo();
testobj_final_check();
exit(0);
}
+81 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -245,6 +245,67 @@ static void test_mpz(void)
assert (array_mpz_equal_p (array1, array2));
fclose(f);
f = m_core_fopen ("a-marray.dat", "wt");
if (!f) abort();
fprintf(f, "array");
fclose (f);
f = m_core_fopen ("a-marray.dat", "rt");
if (!f) abort();
b = array_mpz_in_str (array2, f);
assert (b == false);
fclose(f);
f = m_core_fopen ("a-marray.dat", "wt");
if (!f) abort();
fprintf(f, "[");
fclose (f);
f = m_core_fopen ("a-marray.dat", "rt");
if (!f) abort();
b = array_mpz_in_str (array2, f);
assert (b == false);
fclose(f);
f = m_core_fopen ("a-marray.dat", "wt");
if (!f) abort();
fprintf(f, "[17");
fclose (f);
f = m_core_fopen ("a-marray.dat", "rt");
if (!f) abort();
b = array_mpz_in_str (array2, f);
assert (b == false);
fclose(f);
f = m_core_fopen ("a-marray.dat", "wt");
if (!f) abort();
fprintf(f, "[17,");
fclose (f);
f = m_core_fopen ("a-marray.dat", "rt");
if (!f) abort();
b = array_mpz_in_str (array2, f);
assert (b == false);
fclose(f);
f = m_core_fopen ("a-marray.dat", "wt");
if (!f) abort();
fprintf(f, "[17,18");
fclose (f);
f = m_core_fopen ("a-marray.dat", "rt");
if (!f) abort();
b = array_mpz_in_str (array2, f);
assert (b == false);
fclose(f);
f = m_core_fopen ("a-marray.dat", "wt");
if (!f) abort();
fprintf(f, "[]");
fclose (f);
f = m_core_fopen ("a-marray.dat", "rt");
if (!f) abort();
b = array_mpz_in_str (array2, f);
assert (b == true);
fclose(f);
assert(array_mpz_empty_p(array2));
array_mpz_reset(array1);
M_LET(str, STRING_OPLIST) {
@@ -277,6 +338,20 @@ static void test_mpz(void)
assert(b);
assert(strcmp(sp, "") == 0);
assert(array_mpz_equal_p(array1, array2));
array_mpz_push_back(array1, z);
assert(!array_mpz_equal_p(array1, array2));
testobj_set_ui (z, 43);
array_mpz_push_back(array2, z);
assert(!array_mpz_equal_p(array1, array2));
b = array_mpz_parse_str(array2, "[", &sp);
assert(!b);
b = array_mpz_parse_str(array2, "[17,", &sp);
assert(!b);
b = array_mpz_parse_str(array2, "[17,18", &sp);
assert(!b);
// NOTE: Not the same order than list...
}
@@ -400,6 +475,10 @@ static void test_d(void)
array_uint_init_set (a2, a1);
assert (array_uint_equal_p (a2, a1));
array_uint_push_back(a2, 890);
assert (!array_uint_equal_p (a2, a1));
array_uint_push_back(a1, 891);
assert (!array_uint_equal_p (a2, a1));
size_t h = array_uint_hash(a2);
assert (h != 0);
@@ -535,5 +614,6 @@ int main(void)
test_str();
test_double();
test_cplusplus();
testobj_final_check();
exit(0);
}
+5 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -21,8 +21,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include "m-string.h"
#include "coverage.h"
#include "m-bitset.h"
static void test1(void)
@@ -379,6 +380,8 @@ static void test_clz(void)
}
M_LET(s1, bitset_t) {
bitset_reset(s1);
assert(bitset_ctz(s1) == 0);
bitset_set_str(s1, "[00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]");
assert(bitset_ctz(s1) == 116);
bitset_set_str(s1, "[10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]");
+33 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -333,6 +333,16 @@ static void test4(void)
assert(btree_size(b1) == 0);
assert (btree_equal_p(b1, b2));
btree_set_at(b1, 0, 1);
btree_set_at(b2, 0, 2);
assert (!btree_equal_p(b1, b2));
btree_set_at(b1, 0, 1);
btree_set_at(b2, 1, 1);
bool b = btree_erase(b2, 0);
assert( b );
assert (!btree_equal_p(b1, b2));
btree_clear(b1);
btree_clear(b2);
@@ -400,7 +410,8 @@ static void test5(void)
}
assert (i == 600);
btree_erase (b, 500);
bool r = btree_erase (b, 500);
assert(r);
i = 501;
for(btree_it_from(it, b, 500); !btree_it_until_p(it, 600); btree_next(it)) {
const btree_itref_t *item = btree_cref(it);
@@ -413,6 +424,25 @@ static void test5(void)
btree_it_from(it, b, 1000);
assert (btree_end_p(it));
assert (btree_it_until_p(it, 1001));
// Remove all even integers
for(i = 0 ; i < 1000; i+=2) {
r = btree_erase(b, i);
assert(r || i == 500);
}
for(i = 0; i < 100; i++) {
for(int j = i ; j < 100; j ++) {
int k = i;
for(btree_it_from(it, b, i); !btree_it_until_p(it, j); btree_next(it)) {
const btree_itref_t *item = btree_cref(it);
int expect = k/2*2+1;
assert (*item->key_ptr == expect);
assert (*item->value_ptr == 1000*expect);
k+=2;
}
}
}
btree_clear(b);
}
@@ -705,5 +735,6 @@ int main(void)
test_multiset();
test_double();
test_emplace();
testobj_final_check();
exit(0);
}
+19 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -187,8 +187,18 @@ static void test_global(void)
assert(j == i);
}
assert (buffer_uint_empty_p(g_buffB));
buffer_uint_clear(g_buffB);
buffer_uint_set(g_buffB, g_buff);
buffer_uint_reset(g_buff);
assert (buffer_uint_empty_p(g_buff));
assert (buffer_uint_size(g_buff) == 0);
buffer_uint_set(g_buff, g_buffB);
assert (!buffer_uint_empty_p(g_buff));
assert (!buffer_uint_full_p(g_buff));
assert (buffer_uint_size(g_buff) == 5);
buffer_uint_clear(g_buffB);
buffer_uint_clear(g_buff);
}
@@ -558,7 +568,12 @@ static void test_spsc(void)
b = squeue_uint_pop(&j, q);
assert(b);
assert(j == 65536);
do {
l = 189;
b = squeue_uint_push_move(q, &l);
} while (b);
assert( squeue_uint_size(q) == 256);
squeue_uint_clear(q);
}
@@ -661,5 +676,6 @@ int main(void)
test_double1();
test_double2();
test_double3();
testobj_final_check();
exit(0);
}
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* M*LIB - Test for Concurrent memory pool allocator
*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,7 +24,7 @@
*/
#include "m-c-mempool.h"
#include "m-mutex.h"
#include "m-thread.h"
C_MEMPOOL_DEF(lf_mempool, int)
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2022, Patrick Pelissier
* Copyright (c) 2017-2023, Patrick Pelissier
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -201,7 +201,7 @@ static void test_rp_basic(void)
{
rpdict1_t dict;
rpdict1_init(dict);
int z;
int z = 0;
rpdict1_set_at(dict, 2, 3);
rpdict1_get_copy(&z, dict, 2);
assert (z == 3);
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more