Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a96845fac | ||
|
|
b742881ebc | ||
|
|
f7f5db61d3 | ||
|
|
32b00eb433 | ||
|
|
796f2619e5 | ||
|
|
268192138a | ||
|
|
0671a2ea41 | ||
|
|
07dce84e51 | ||
|
|
5f4303624f | ||
|
|
19e61467d5 | ||
|
|
1454bcebe4 | ||
|
|
d48f2ff84d | ||
|
|
269636b4d8 | ||
|
|
bf68340106 | ||
|
|
5dba526e29 | ||
|
|
469239adcd | ||
|
|
589abda0c2 | ||
|
|
8f141896ba | ||
|
|
a0cbec4329 | ||
|
|
df66db562d | ||
|
|
1829a70601 | ||
|
|
e9b25f2414 | ||
|
|
c9bb739f24 | ||
|
|
e049b83a9e | ||
|
|
31ac3999a1 | ||
|
|
a0818419ab | ||
|
|
496f885a84 | ||
|
|
d5d9a4bd5c | ||
|
|
ca9571a110 | ||
|
|
4497214b9c | ||
|
|
ccb1658afd | ||
|
|
eaf2783f1a | ||
|
|
5705819814 | ||
|
|
91f5f513c4 | ||
|
|
3dec4e2cbe | ||
|
|
e70766beaa | ||
|
|
d7639f0ae0 | ||
|
|
c63ce84e45 | ||
|
|
a043c379c0 | ||
|
|
92bb380117 | ||
|
|
6bb7a184db | ||
|
|
fa9163be9d | ||
|
|
0bade1563c | ||
|
|
1278d2f8a0 | ||
|
|
53af2aae68 | ||
|
|
17ded6fcc8 | ||
|
|
19ce371533 | ||
|
|
0bd1bcb68e | ||
|
|
b50813713a | ||
|
|
2628536d20 | ||
|
|
ecfa4a9152 | ||
|
|
dafe4bcec5 | ||
|
|
8544a7f289 | ||
|
|
aa18915d61 | ||
|
|
df4bc0a56c | ||
|
|
b6046e5e23 | ||
|
|
b487bfe5ad | ||
|
|
408272c810 | ||
|
|
6a9ee17eca | ||
|
|
05c5a94634 | ||
|
|
b1b0927e9f | ||
|
|
37770365a1 | ||
|
|
82bdcc685a | ||
|
|
26fa733f70 | ||
|
|
cd2fc05573 | ||
|
|
33430d4359 | ||
|
|
dc95b49f05 | ||
|
|
3ec24cfa40 | ||
|
|
12b099dcf8 | ||
|
|
b885d6136c | ||
|
|
b51c4bea2b | ||
|
|
302c5eb300 | ||
|
|
6d2900a2bd | ||
|
|
573e09c56e | ||
|
|
0a39a17d36 | ||
|
|
a913d0947c | ||
|
|
abbd8ad6ce | ||
|
|
a09c9fe1c3 | ||
|
|
219c62ff5d | ||
|
|
272a326375 | ||
|
|
eca79f2c80 | ||
|
|
2feda4059f | ||
|
|
de0ef67cf2 | ||
|
|
95762a058c | ||
|
|
acd8671017 | ||
|
|
a6c1688f23 | ||
|
|
f6ed549ce5 | ||
|
|
8639fdbd98 | ||
|
|
693b09387f | ||
|
|
4052d3794b | ||
|
|
044e9c3bf7 | ||
|
|
45a2a1afb2 | ||
|
|
b44e4e09dd | ||
|
|
5158059ec4 | ||
|
|
7279011f0a | ||
|
|
79369e9b05 | ||
|
|
58a7a7c214 | ||
|
|
e3e94ca238 | ||
|
|
ae81271b2a | ||
|
|
d9c3724cff | ||
|
|
1ff9848384 | ||
|
|
83e7f2a641 | ||
|
|
6580601969 | ||
|
|
ad023bb09d | ||
|
|
b5df21f9c5 | ||
|
|
d2dccbf514 | ||
|
|
a8075cbb05 | ||
|
|
1322afc4c9 | ||
|
|
0f885fb158 | ||
|
|
25a2eb0875 | ||
|
|
6bcd51f10f | ||
|
|
9c1cb9c835 | ||
|
|
19cb329ae6 | ||
|
|
8099e6a94f | ||
|
|
ce6cc080d6 | ||
|
|
a27b0b4014 | ||
|
|
e74f80cc59 | ||
|
|
bafd307247 | ||
|
|
6bed3ac697 | ||
|
|
b8a9073fc9 | ||
|
|
da3e71c676 | ||
|
|
4ea83e129e | ||
|
|
fc34fa80b5 | ||
|
|
4f608ec266 | ||
|
|
ccaa54d2a5 | ||
|
|
095bf7070d | ||
|
|
676b12d5d2 | ||
|
|
f858aa2cb5 | ||
|
|
9d77448a25 | ||
|
|
8babc33b91 | ||
|
|
352d7cf725 | ||
|
|
a6993ab758 | ||
|
|
48260ac2a6 | ||
|
|
e4afb024b9 | ||
|
|
63008af30f | ||
|
|
1828aca65e | ||
|
|
9354e9717d | ||
|
|
100c0f22fe | ||
|
|
ff778528f8 | ||
|
|
8f1a1df617 | ||
|
|
28faf815d7 | ||
|
|
fe3b703d0a | ||
|
|
0aa6a9f0cf | ||
|
|
67832ac918 | ||
|
|
669a3297c7 | ||
|
|
25d4829d7b | ||
|
|
ebab05a789 | ||
|
|
727df5b7bf | ||
|
|
aebe1308fc | ||
|
|
1d163a396e | ||
|
|
7534aa0d61 | ||
|
|
6976d4e23d | ||
|
|
6b7954e141 | ||
|
|
c27d76a83b | ||
|
|
0e3081bfc6 | ||
|
|
d754e9a15f | ||
|
|
4e4e21793c | ||
|
|
c515da9f37 | ||
|
|
961b93bd4c | ||
|
|
39a719c3f0 | ||
|
|
8b643b4878 | ||
|
|
876c394fba | ||
|
|
81439c9437 | ||
|
|
191ef7d09e | ||
|
|
bd4131dd8f | ||
|
|
cfe8293c64 | ||
|
|
ea2715aa75 | ||
|
|
f69cec6820 | ||
|
|
085f371693 | ||
|
|
16a0a5c1a3 | ||
|
|
3953f81884 | ||
|
|
8365888e6e | ||
|
|
c3f6e3c5f0 | ||
|
|
4cafe4dce1 | ||
|
|
9f8b97ae46 | ||
|
|
030bea9c11 | ||
|
|
e33894f1b3 | ||
|
|
66f5e6c67d | ||
|
|
78c6dce068 | ||
|
|
0798480ad5 | ||
|
|
e8322215fd | ||
|
|
42d3042c15 | ||
|
|
b7214ad11d | ||
|
|
08b4bc9052 | ||
|
|
d813171e8a | ||
|
|
d1094999f5 | ||
|
|
c8354aa6ed | ||
|
|
ab3aa33fac | ||
|
|
01f2f376e2 | ||
|
|
535e00366a | ||
|
|
42111ec22a | ||
|
|
ef0e849616 | ||
|
|
9c516c2055 | ||
|
|
db48b985c3 | ||
|
|
888796ca06 | ||
|
|
144ae9c5ed | ||
|
|
6223c93b53 | ||
|
|
30f7656822 | ||
|
|
8209a0c32a | ||
|
|
92e6957320 | ||
|
|
b3c9f6ee47 | ||
|
|
ab8bbad3f4 | ||
|
|
9047376b7f | ||
|
|
dfed8dbc50 | ||
|
|
2d3c25cd85 | ||
|
|
bfa948dc7a | ||
|
|
94b6d1b9fd | ||
|
|
0ddd5ddc6d | ||
|
|
cba93d5bb4 | ||
|
|
d02de75487 | ||
|
|
2f57766249 | ||
|
|
3ff01e37eb | ||
|
|
5683df0f22 | ||
|
|
11dc7afc06 | ||
|
|
9e2ae3f101 | ||
|
|
c35aa9c891 | ||
|
|
2fc31d8c12 | ||
|
|
4f8d0a7156 | ||
|
|
aaf5bbd6a0 | ||
|
|
2f76652f2b | ||
|
|
942cf32519 | ||
|
|
c1c9cb9cd3 | ||
|
|
76bfb48685 | ||
|
|
afff412f7a | ||
|
|
e48dcdeecc | ||
|
|
12fae5c36c | ||
|
|
3f9129c7ed | ||
|
|
b918f61f4f | ||
|
|
93b69d0f8b | ||
|
|
b3440a2edd | ||
|
|
4140df5bfc | ||
|
|
fbab093d52 | ||
|
|
f57a0e0bf8 | ||
|
|
925c06beb8 | ||
|
|
8e2f67b794 | ||
|
|
f176d01e49 | ||
|
|
2b99bbe956 | ||
|
|
700bfc5341 | ||
|
|
4062525821 | ||
|
|
800a1e5c36 | ||
|
|
1838f675a7 | ||
|
|
bf6c1341b3 | ||
|
|
55b745b916 | ||
|
|
5cb0d9214b | ||
|
|
8f99b8e617 | ||
|
|
d2ce71a90a | ||
|
|
8562986eea | ||
|
|
ee89858bf0 | ||
|
|
085d7157c0 | ||
|
|
91f3174a36 | ||
|
|
56f51c59af | ||
|
|
df0c95ca46 | ||
|
|
482ec38e62 | ||
|
|
82743ca72c | ||
|
|
312821454c | ||
|
|
c953e87660 | ||
|
|
8846707c8b | ||
|
|
bf7d4eee55 | ||
|
|
d4dc85f89a | ||
|
|
130c32e7d4 | ||
|
|
2bcaba3bc0 | ||
|
|
443c805c22 | ||
|
|
414c87bb69 | ||
|
|
e9ca506d93 | ||
|
|
8e58f7d637 | ||
|
|
254beaf3c7 | ||
|
|
27735eb224 | ||
|
|
0afc81bd54 | ||
|
|
39e67eebdc | ||
|
|
840226c7fd | ||
|
|
e421851525 | ||
|
|
f70b52c9e4 | ||
|
|
40249ce5bc | ||
|
|
97ff413f01 | ||
|
|
8e4ac99a35 | ||
|
|
4366e8eeab | ||
|
|
4e5e498bb2 | ||
|
|
870a6f4b57 | ||
|
|
a2ae209992 |
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -38,8 +38,13 @@ depend
|
||||
other/
|
||||
a*.dat
|
||||
config
|
||||
config.link
|
||||
*.log
|
||||
.vscode
|
||||
.vs
|
||||
tmp-serial.*
|
||||
*~
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
*.synt
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,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:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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:
|
||||
|
||||
@@ -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
|
||||
@@ -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?
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 71 KiB |
@@ -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}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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]");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||