Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0d3e93d9d | ||
|
|
e77ff2451b | ||
|
|
142c224f84 | ||
|
|
29e8d9199d | ||
|
|
7128f5add2 | ||
|
|
1b7bcda4e1 | ||
|
|
03b6146ef0 | ||
|
|
e99735d2b6 | ||
|
|
7263629c55 | ||
|
|
605ce9b938 | ||
|
|
049d8b6ca6 | ||
|
|
90d3dda830 | ||
|
|
2022a66db3 | ||
|
|
78c786070e | ||
|
|
c5c72007af | ||
|
|
c60d59cb7a | ||
|
|
00111e3e3c | ||
|
|
5cc2ac17d9 | ||
|
|
d37ea84ab5 | ||
|
|
5e335bc8bd | ||
|
|
65765763ae | ||
|
|
90f248fbe8 | ||
|
|
a39cf23697 | ||
|
|
261776762c | ||
|
|
f207f9441c | ||
|
|
ffed880e0a | ||
|
|
a79b67cea2 | ||
|
|
bc3c8b5bcd | ||
|
|
3489bb42bc | ||
|
|
7fe36f7bd7 | ||
|
|
82fd4c94db | ||
|
|
4fc48b490e | ||
|
|
750e5e6d95 | ||
|
|
cb564128e6 | ||
|
|
047fc986f2 | ||
|
|
aca800ef8a | ||
|
|
dde090d4b0 | ||
|
|
87183573ce | ||
|
|
baddcc3855 | ||
|
|
ba531774a0 | ||
|
|
708d17a4d2 | ||
|
|
7d802c03e5 | ||
|
|
787e1e9965 | ||
|
|
625691d279 | ||
|
|
4f0148dc77 | ||
|
|
c78f2b992f | ||
|
|
9857db0d15 | ||
|
|
8b71064643 | ||
|
|
bc39034f6d | ||
|
|
e6a9b8fc8e | ||
|
|
3db43dc2db | ||
|
|
d384d5ad6c | ||
|
|
7b2004caad | ||
|
|
6754f88861 | ||
|
|
9393809dfd | ||
|
|
79ecacb6c0 | ||
|
|
853acbad4b | ||
|
|
e158eac10a | ||
|
|
48ca2a2384 | ||
|
|
d9d3d3cbe3 | ||
|
|
6b4f10bf45 | ||
|
|
3b647b982f | ||
|
|
00b875ea53 | ||
|
|
545fc4d93c | ||
|
|
c41342739a | ||
|
|
ed99c5aaa3 | ||
|
|
08842811e8 | ||
|
|
3ea063d1e0 | ||
|
|
a905f12915 | ||
|
|
5d8548b92b | ||
|
|
99b63e9c77 | ||
|
|
4002ef5aff | ||
|
|
d31ccfe8a4 | ||
|
|
c4ab76a330 | ||
|
|
84d4277be3 | ||
|
|
0a99814ce2 | ||
|
|
11d695cb57 | ||
|
|
13e2d6b473 | ||
|
|
e8137e4218 | ||
|
|
fe5df08d6e | ||
|
|
f77017f35f | ||
|
|
ce93c00a7b | ||
|
|
b8f44cc219 | ||
|
|
2c921fdefe | ||
|
|
20eb95ba45 | ||
|
|
7a6239912e | ||
|
|
9067311788 | ||
|
|
23b2e07132 | ||
|
|
c8f2c0e06e | ||
|
|
72c4eb80a2 | ||
|
|
1f18e788e6 | ||
|
|
f855e24b56 | ||
|
|
84d1b7ad50 | ||
|
|
9f82649aa8 | ||
|
|
791f586c7e | ||
|
|
8d3b0cc94b | ||
|
|
70d1dfeb8b | ||
|
|
9a4c6ba0d5 | ||
|
|
ef2dbf89ee | ||
|
|
d30b776fb0 | ||
|
|
6408b28513 | ||
|
|
32b3e21564 | ||
|
|
1ef28bedcb | ||
|
|
1356bb746e | ||
|
|
23240a44d5 | ||
|
|
589c023622 | ||
|
|
047b6c5a58 | ||
|
|
951425e963 | ||
|
|
e69cbfcab3 | ||
|
|
37f3cd6a72 | ||
|
|
ec7b5dd120 | ||
|
|
d50a78171f | ||
|
|
b422aec2cd | ||
|
|
7ba0e03b4f | ||
|
|
c535766c22 | ||
|
|
289a3fdc71 | ||
|
|
7f0b6208a4 | ||
|
|
60a10eb799 | ||
|
|
5321085856 | ||
|
|
03be814a4d | ||
|
|
6c3f7683a5 | ||
|
|
06d6a90a21 | ||
|
|
747da27489 | ||
|
|
2869345920 | ||
|
|
6b8938cf76 | ||
|
|
8e7eae5751 | ||
|
|
1882148a7c | ||
|
|
e46746ce01 | ||
|
|
32917e2cfa | ||
|
|
65a243fc13 | ||
|
|
1e011cb851 | ||
|
|
48f585a4ee | ||
|
|
bc10b616ae | ||
|
|
851f08131d | ||
|
|
e294f4557e | ||
|
|
9e5775fc16 | ||
|
|
8eb95d0cdc | ||
|
|
42f807d64b | ||
|
|
d1bb97ac52 | ||
|
|
f502b16b1d | ||
|
|
51e5764258 | ||
|
|
ec2281c29a | ||
|
|
acfe1cb7e8 | ||
|
|
594aebd903 | ||
|
|
8bca63ac08 | ||
|
|
c02ec4f94c | ||
|
|
797ac1f0da | ||
|
|
058d3202ff | ||
|
|
62fcdac9cb | ||
|
|
b9131353b6 | ||
|
|
a34deb2f3b | ||
|
|
9ef4b5c93f | ||
|
|
417b0295be | ||
|
|
d11409bcc6 | ||
|
|
928b81f454 | ||
|
|
05a68157bc | ||
|
|
b6d5b5462b | ||
|
|
1c77082a29 | ||
|
|
363c55668d | ||
|
|
7ed85fb97a | ||
|
|
585ea1293d | ||
|
|
448072555f | ||
|
|
4701d5919b | ||
|
|
0997f59665 | ||
|
|
dcf7a94728 | ||
|
|
12ba563416 | ||
|
|
f2fffb9219 | ||
|
|
3f03615ed2 | ||
|
|
bc6bda057c | ||
|
|
aeebe42bfb | ||
|
|
5c4c93b62f | ||
|
|
c3ddd48d38 | ||
|
|
98f3c40ac5 | ||
|
|
ada1b9861f | ||
|
|
50cc8689bb | ||
|
|
83da861a9c | ||
|
|
1cb1d16e77 | ||
|
|
b525723084 | ||
|
|
4accdc994d | ||
|
|
ad9bee0eec | ||
|
|
6c7ff1c723 | ||
|
|
3b2aad1b2e | ||
|
|
5222fabf02 | ||
|
|
44abead174 | ||
|
|
492d5f0f9a | ||
|
|
9bc5ac3df8 | ||
|
|
5c726d63f4 | ||
|
|
2cd0a8db74 | ||
|
|
0fa0445fa9 | ||
|
|
1e171f9cc0 | ||
|
|
8d16ea4bf4 | ||
|
|
a0c2ead91d | ||
|
|
b2875f6ba0 | ||
|
|
edb4ccf63e | ||
|
|
481009d18b | ||
|
|
6153383dab | ||
|
|
74fed41818 | ||
|
|
2d5de1b9ea | ||
|
|
e23691c743 | ||
|
|
aaac236db6 | ||
|
|
ccccedc2d5 | ||
|
|
446afb04c7 | ||
|
|
87cd964329 | ||
|
|
80a344e46d | ||
|
|
f2eb5014ac | ||
|
|
d6c72061ce | ||
|
|
7c26fd25c9 | ||
|
|
10e12d2b5c | ||
|
|
b9070f89d9 | ||
|
|
46d41a8882 | ||
|
|
b5dc3481d4 | ||
|
|
c18cd81dde | ||
|
|
eaf5a26157 | ||
|
|
547c67b676 | ||
|
|
c89d6ef0ce | ||
|
|
a54041c340 | ||
|
|
352e0972cd | ||
|
|
91ebd7cc9f | ||
|
|
d8854c276b | ||
|
|
cc61840ae8 | ||
|
|
67a7a4762a | ||
|
|
c28597014f | ||
|
|
d00f29bf26 | ||
|
|
1432fbabca | ||
|
|
f3b6ee0190 | ||
|
|
229d21ef72 | ||
|
|
00f1b1fc4f | ||
|
|
277d8b37af | ||
|
|
7b83761ef4 | ||
|
|
a96e9c7e11 | ||
|
|
866721d067 | ||
|
|
b356c29c89 | ||
|
|
a2f7cb7670 | ||
|
|
08887c1df0 | ||
|
|
8b0a9f2da9 | ||
|
|
e6c958c0df | ||
|
|
01ad221972 | ||
|
|
c269a36e8b | ||
|
|
95fcd054a3 | ||
|
|
7760f16260 | ||
|
|
6d016d732b | ||
|
|
b9aebf875e | ||
|
|
1eb0e8730d | ||
|
|
7eb26a29f1 | ||
|
|
0ef051e6d2 | ||
|
|
0d641f0d17 | ||
|
|
ce6a7f6cb3 | ||
|
|
07b4c1a9f4 | ||
|
|
4d3a82583d | ||
|
|
2835d53ef1 | ||
|
|
620ecadec1 | ||
|
|
dc803f23bb | ||
|
|
a6d11fe897 | ||
|
|
78b05690e7 | ||
|
|
958a79ad73 | ||
|
|
ead25c905a | ||
|
|
97f80450ca | ||
|
|
8e62537604 | ||
|
|
7813182e2b | ||
|
|
b199fef674 | ||
|
|
e27c06c227 | ||
|
|
41b04f34e2 | ||
|
|
b091f84f90 | ||
|
|
05ae9f382b | ||
|
|
a36efcd888 | ||
|
|
af2fe9ccf9 | ||
|
|
c4a5727c2a | ||
|
|
31ca762c62 | ||
|
|
30bda70319 | ||
|
|
7fa9d375b8 | ||
|
|
91ae3fd4f2 | ||
|
|
b00d6a9655 | ||
|
|
013aa58524 | ||
|
|
8ce7e0483f | ||
|
|
582c413cd2 | ||
|
|
22e16e983b | ||
|
|
7c0c16260a | ||
|
|
e496fbd152 | ||
|
|
e025de91ab | ||
|
|
69e2fc432f | ||
|
|
78ba83ce42 | ||
|
|
6d9b61c119 | ||
|
|
61cfa5cedd | ||
|
|
0507d94306 | ||
|
|
7e8919080b | ||
|
|
8406efcaab | ||
|
|
2d87eb1e6a | ||
|
|
4685aac454 | ||
|
|
9863a8325c | ||
|
|
b9514cbe72 | ||
|
|
7b085d18fc | ||
|
|
91e73520f3 | ||
|
|
aebf5c83ba | ||
|
|
42ff47f511 | ||
|
|
746bdf144b | ||
|
|
4e8f5f7b51 | ||
|
|
50d89f71cb | ||
|
|
d70ef53283 | ||
|
|
1b6182c1cd | ||
|
|
73b50c852a | ||
|
|
063011d8e9 | ||
|
|
0f3bf6ab82 | ||
|
|
475836e851 | ||
|
|
325ca30c61 | ||
|
|
72e872c768 | ||
|
|
c2a837b30b | ||
|
|
08822ac7d5 | ||
|
|
7f9007ca1a | ||
|
|
f067cb334c | ||
|
|
60ee253fa7 | ||
|
|
4642e0b506 | ||
|
|
bd29680b62 | ||
|
|
c337462af6 | ||
|
|
c990277855 | ||
|
|
8870a1c2f9 | ||
|
|
60f1454550 | ||
|
|
38eb1c26c6 | ||
|
|
af050d0100 | ||
|
|
14f3ae5cea | ||
|
|
f4ed750d39 | ||
|
|
4b3adc8d35 | ||
|
|
8b3a4a802c | ||
|
|
8b9f605ffa | ||
|
|
3c56f24db2 | ||
|
|
82dbf60a4c | ||
|
|
b596e81e88 | ||
|
|
b7bbe15547 | ||
|
|
cb5f8c20c8 | ||
|
|
61a1e15103 | ||
|
|
2416c2e466 | ||
|
|
b898ce3c56 | ||
|
|
061e83b9e1 | ||
|
|
0a8f727908 | ||
|
|
ba584ab096 | ||
|
|
1fb1fa2e0a | ||
|
|
8cf780a60f | ||
|
|
ae171115bc | ||
|
|
a8620c3aff | ||
|
|
c154cdf46c | ||
|
|
77c59e2b85 | ||
|
|
f07656757f | ||
|
|
a1c22fb679 | ||
|
|
3640041c47 | ||
|
|
d3934dccb9 | ||
|
|
10cf2f2270 | ||
|
|
c180b2b96d |
No files matched your search
@@ -0,0 +1,34 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: make
|
||||
- name: make check
|
||||
run: make check
|
||||
|
||||
build_windows_mingw:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [windows-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install MINGW
|
||||
run: choco install mingw --no-progress
|
||||
- name: make
|
||||
shell: bash
|
||||
run: mingw32-make CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1"
|
||||
- name: make check
|
||||
shell: bash
|
||||
run: mingw32-make CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
|
||||
@@ -37,3 +37,4 @@ depend
|
||||
# Others
|
||||
other/
|
||||
a*.dat
|
||||
config
|
||||
@@ -1,6 +1,6 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2018, Patrick Pelissier
|
||||
Copyright (c) 2017-2019, Patrick Pelissier
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -7,18 +7,18 @@ INSTALL_DATA=${INSTALL} -m 644
|
||||
|
||||
# Installation directory
|
||||
PREFIX=/usr/local
|
||||
DESTDIR=
|
||||
|
||||
# Package name & version.
|
||||
PACKAGE=m*lib-$(VERSION)
|
||||
VERSION=0.2.0
|
||||
VERSION=0.3.0
|
||||
|
||||
# Define the contain of the distribution tarball
|
||||
# TODO: Get theses lists from GIT itself.
|
||||
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-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-shared.h m-snapshot.h m-string.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-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tuple.h m-variant.h m-worker.h
|
||||
DOC1=LICENSE README.md
|
||||
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/DEV.md doc/ISSUES.org doc/depend.png doc/oplist.png
|
||||
EXAMPLE=example/ex-array01.c example/ex-array04.c example/ex-dict02.c example/ex-grep01.c example/ex-multi01.c example/ex-rbtree01.c example/ex-array02.c example/ex-buffer01.c example/ex-dict03.c example/ex-list01.c example/ex-multi02.c example/Makefile example/ex-array03.c example/ex-dict01.c example/ex-dict04.c example/ex-mph.c example/ex-multi03.c
|
||||
TEST=tests/coverage.h tests/test-malgo.c tests/test-mbptree.c tests/test-mdeque.c tests/test-milist.c tests/test-mmutex.c tests/test-mshared.c tests/test-mtuple.c tests/test-obj.h tests/tgen-mdict.c tests/tgen-openmp.c tests/wip-mregister.c tests/dict.txt tests/test-marray.c tests/test-mbuffer.c tests/test-mdict.c tests/test-mlist.c tests/test-mprioqueue.c tests/test-msnapshot.c tests/test-mvariant.c tests/tgen-bitset.c tests/tgen-mlist.c tests/tgen-queue.c tests/Makefile tests/test-mbitset.c tests/test-mcore.c tests/test-mgenint.c tests/test-mmempool.c tests/test-mrbtree.c tests/test-mstring.c tests/test-mworker.c tests/tgen-marray.c tests/tgen-mstring.c tests/tgen-shared.c
|
||||
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png
|
||||
EXAMPLE=example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-json01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-rbtree01.c example/ex-section.c example/Makefile
|
||||
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/wip-mregister.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt
|
||||
|
||||
.PHONY: all test check doc clean distclean depend install uninstall dist
|
||||
|
||||
@@ -31,6 +31,10 @@ check:
|
||||
cd tests && $(MAKE) check
|
||||
cd example && $(MAKE) all
|
||||
|
||||
checkall:
|
||||
cd tests && $(MAKE) checkall
|
||||
cd example && $(MAKE) all
|
||||
|
||||
html: doc
|
||||
doc: README.md doc/depend.png
|
||||
markdown < README.md > README.html
|
||||
|
||||
+76
-39
@@ -1,14 +1,20 @@
|
||||
CC=cc -std=c11
|
||||
CXX=c++ -std=c++11
|
||||
CFLAGS=-O2 -march=native -Wall
|
||||
XCFLAGS=
|
||||
CPPFLAGS=-I..
|
||||
N=4
|
||||
BENCH_DEF=
|
||||
RM=rm -rf
|
||||
|
||||
.PHONY: all container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
|
||||
.PHONY: all pgo container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
|
||||
|
||||
all: container queue mempool string plain
|
||||
|
||||
pgo:
|
||||
$(MAKE) all XCFLAGS="-fprofile-generate=pgo"
|
||||
$(MAKE) all XCFLAGS="-fprofile-use=pgo"
|
||||
|
||||
#########################################################################
|
||||
|
||||
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap
|
||||
@@ -32,7 +38,7 @@ plain: bench-plain
|
||||
|
||||
|
||||
bench-mlib:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -pthread bench-mlib.c -o bench-mlib.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -pthread bench-mlib.c common.c -o bench-mlib.exe
|
||||
@./bench-mlib.exe 10
|
||||
@./bench-mlib.exe 11
|
||||
@./bench-mlib.exe 20
|
||||
@@ -41,12 +47,14 @@ bench-mlib:
|
||||
@./bench-mlib.exe 40
|
||||
@./bench-mlib.exe 41
|
||||
@./bench-mlib.exe 42
|
||||
@./bench-mlib.exe 46
|
||||
@./bench-mlib.exe 47
|
||||
@./bench-mlib.exe 43
|
||||
@./bench-mlib.exe 50
|
||||
@./bench-mlib.exe 51
|
||||
|
||||
bench-mlib-mempool:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
|
||||
@./bench-mlib-mempool.exe 10
|
||||
@./bench-mlib-mempool.exe 11
|
||||
@./bench-mlib-mempool.exe 20
|
||||
@@ -55,27 +63,31 @@ bench-mlib-mempool:
|
||||
@./bench-mlib-mempool.exe 40
|
||||
@./bench-mlib-mempool.exe 41
|
||||
@./bench-mlib-mempool.exe 42
|
||||
@./bench-mlib-mempool.exe 46
|
||||
@./bench-mlib-mempool.exe 47
|
||||
@./bench-mlib-mempool.exe 43
|
||||
@./bench-mlib-mempool.exe 50
|
||||
@./bench-mlib-mempool.exe 51
|
||||
|
||||
bench-mlib-thread:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
|
||||
@./bench-mlib-thread.exe 60
|
||||
@./bench-mlib-thread.exe 61
|
||||
@./bench-mlib-thread.exe 62
|
||||
@./bench-mlib-thread.exe 63
|
||||
@./bench-mlib-thread.exe 64
|
||||
@./bench-mlib-thread.exe 65
|
||||
@./bench-mlib-thread.exe 66
|
||||
|
||||
bench-stl:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-stl.cpp -o bench-stl.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-stl.cpp common.c -o bench-stl.exe
|
||||
@./bench-stl.exe 10
|
||||
@./bench-stl.exe 20
|
||||
@./bench-stl.exe 30
|
||||
@./bench-stl.exe 40
|
||||
@./bench-stl.exe 41
|
||||
@./bench-stl.exe 42
|
||||
@./bench-stl.exe 46
|
||||
@./bench-stl.exe 43
|
||||
@./bench-stl.exe 50
|
||||
@./bench-stl.exe 51
|
||||
@@ -84,7 +96,7 @@ bench-stl:
|
||||
bench-qt:
|
||||
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
|
||||
bench-qt0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp common.c -o bench-qt.exe
|
||||
@./bench-qt.exe 10
|
||||
@./bench-qt.exe 20
|
||||
@./bench-qt.exe 30
|
||||
@@ -97,7 +109,7 @@ bench-qt0:
|
||||
bench-glib:
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
|
||||
bench-glib0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-glib.c common.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
|
||||
@./bench-glib.exe 10
|
||||
@./bench-glib.exe 20
|
||||
@./bench-glib.exe 30
|
||||
@@ -110,7 +122,7 @@ bench-klib:
|
||||
@if test -n "$${KLIB}" ; then $(MAKE) bench-klib0 ; else echo "Nothing to be done for KLIB." ; fi
|
||||
|
||||
bench-klib0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c -o bench-klib.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c common.c -o bench-klib.exe
|
||||
@./bench-klib.exe 10
|
||||
@./bench-klib.exe 20
|
||||
@./bench-klib.exe 30
|
||||
@@ -123,20 +135,17 @@ bench-libdynamic:
|
||||
@if test -n "$${LIBDYNAMIC}" ; then $(MAKE) bench-libdynamic0 ; else echo "Nothing to be done for LIBDYNAMIC." ; fi
|
||||
|
||||
bench-libdynamic0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-libdynamic.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
|
||||
@./bench-libdynamic.exe 10
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-libdynamic.c common.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
|
||||
@./bench-libdynamic.exe 20
|
||||
@./bench-libdynamic.exe 30
|
||||
@./bench-libdynamic.exe 40
|
||||
@./bench-libdynamic.exe 41
|
||||
@./bench-libdynamic.exe 50
|
||||
|
||||
# SPARSEPP shall point to the directory where the headers are
|
||||
bench-sparsepp:
|
||||
@if test -n "$${SPARSEPP}" ; then $(MAKE) bench-sparsepp0 ; else echo "Nothing to be done for SPARSEPP." ; fi
|
||||
|
||||
bench-sparsepp0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp -o bench-sparsepp.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp common.c -o bench-sparsepp.exe
|
||||
@./bench-sparsepp.exe 41
|
||||
@./bench-sparsepp.exe 42
|
||||
@./bench-sparsepp.exe 43
|
||||
@@ -146,7 +155,7 @@ bench-densehashmap:
|
||||
@if test -n "$${DENSEHASHMAP}" ; then $(MAKE) bench-densehashmap0 ; else echo "Nothing to be done Google DENSEHASHMAP." ; fi
|
||||
|
||||
bench-densehashmap0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp -o bench-densehashmap.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp common.c -o bench-densehashmap.exe
|
||||
@./bench-densehashmap.exe 41
|
||||
@./bench-densehashmap.exe 42
|
||||
@./bench-densehashmap.exe 43
|
||||
@@ -156,7 +165,7 @@ bench-collectionc:
|
||||
@if test -n "$${COLLECTIONC}" ; then $(MAKE) bench-collectionc0 ; else echo "Nothing to be done for COLLECTIONC." ; fi
|
||||
|
||||
bench-collectionc0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c -lcollectc -o bench-collectionc.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c common.c -lcollectc -o bench-collectionc.exe
|
||||
@./bench-collectionc.exe 10
|
||||
@./bench-collectionc.exe 20
|
||||
@./bench-collectionc.exe 30
|
||||
@@ -169,7 +178,7 @@ bench-tommyds:
|
||||
@if test -n "$${TOMMYDS}" ; then $(MAKE) bench-tommyds0 ; else echo "Nothing to be done for TOMMYDS." ; fi
|
||||
|
||||
bench-tommyds0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c -o bench-tommyds.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c common.c -o bench-tommyds.exe
|
||||
@./bench-tommyds.exe 10
|
||||
@./bench-tommyds.exe 20
|
||||
@./bench-tommyds.exe 30
|
||||
@@ -181,9 +190,10 @@ bench-flathashmap:
|
||||
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
|
||||
|
||||
bench-flathashmap0:
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp common.c -o bench-flathashmap.exe
|
||||
@./bench-flathashmap.exe 41
|
||||
@./bench-flathashmap.exe 42
|
||||
@./bench-flathashmap.exe 46
|
||||
@./bench-flathashmap.exe 43
|
||||
|
||||
# HOPSCOTCHMAP shall point to the directory where the source are
|
||||
@@ -191,7 +201,7 @@ bench-hopscotchmap:
|
||||
@if test -n "$${HOPSCOTCHMAP}" ; then $(MAKE) bench-hopscotchmap0 ; else echo "Nothing to be done for HOPSCOTCHMAP." ; fi
|
||||
|
||||
bench-hopscotchmap0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}" bench-hopscotchmap.cpp -o bench-hopscotchmap.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}/include/tsl" bench-hopscotchmap.cpp common.c -o bench-hopscotchmap.exe
|
||||
@./bench-hopscotchmap.exe 41
|
||||
@./bench-hopscotchmap.exe 42
|
||||
@./bench-hopscotchmap.exe 43
|
||||
@@ -201,7 +211,7 @@ bench-uthash:
|
||||
@if test -n "$${UTHASH}" ; then $(MAKE) bench-uthash0 ; else echo "Nothing to be done for UTHASH." ; fi
|
||||
|
||||
bench-uthash0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c -o bench-uthash.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c common.c -o bench-uthash.exe
|
||||
@./bench-uthash.exe 10
|
||||
@./bench-uthash.exe 20
|
||||
@./bench-uthash.exe 41
|
||||
@@ -214,7 +224,7 @@ bench-qlibc:
|
||||
@if test -n "$${QLIBC}" ; then $(MAKE) bench-qlibc0 ; else echo "Nothing to be done for QLIBC." ; fi
|
||||
|
||||
bench-qlibc0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c -o bench-qlibc.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c common.c -o bench-qlibc.exe
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 10
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 20
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 30
|
||||
@@ -222,11 +232,12 @@ bench-qlibc0:
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 41
|
||||
|
||||
# LIBSRT shall point to the directory where the source are
|
||||
# The library shall be built.
|
||||
bench-libsrt:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-libsrt0 ; else echo "Nothing to be done for LIBSRT." ; fi
|
||||
|
||||
bench-libsrt0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c common.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
|
||||
@./bench-libsrt.exe 10
|
||||
@./bench-libsrt.exe 20
|
||||
@./bench-libsrt.exe 41
|
||||
@@ -240,7 +251,7 @@ bench-libsrt0:
|
||||
bench-liblfds:
|
||||
@if test -n "$${LIBLFDS}" ; then $(MAKE) bench-liblfds0 ; else echo "Nothing to be done for LIBLFDS." ; fi
|
||||
bench-liblfds0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c common.c -DMULTI_THREAD_MEASURE -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
|
||||
@LD_LIBRARY_PATH="$${LIBLFDS}/bin:$${LD_LIBRARY_PATH}" ./bench-liblfds.exe 60
|
||||
|
||||
|
||||
@@ -249,36 +260,47 @@ bench-liblfds0:
|
||||
bench-concurrentqueue:
|
||||
@if test -n "$${CONCURRENTQUEUE}" ; then $(MAKE) bench-concurrentqueue0 ; else echo "Nothing to be done for CONCURRENTQUEUE." ; fi
|
||||
bench-concurrentqueue0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp -o bench-concurrentqueue.exe -lpthread
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-concurrentqueue.exe -lpthread
|
||||
@./bench-concurrentqueue.exe 60
|
||||
|
||||
|
||||
# XXHASH shall point to the source directory of xxhash
|
||||
# The library shall be built.
|
||||
bench-xxhash:
|
||||
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
|
||||
bench-xxhash0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c common.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
|
||||
@./bench-xxhash.exe 70
|
||||
|
||||
# BOOST shall point to the header location.
|
||||
bench-boost:
|
||||
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
|
||||
bench-boost0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp -o bench-boost.exe -lpthread
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp common.c -DMULTI_THREAD_MEASURE -o bench-boost.exe -lpthread
|
||||
@./bench-boost.exe 60
|
||||
|
||||
# NEDTRIES shall point to the header location.
|
||||
bench-nedtries:
|
||||
@if test -n "$${NEDTRIES}" ; then $(MAKE) bench-nedtries0 ; else echo "Nothing to be done for NEDTRIES." ; fi
|
||||
bench-nedtries0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c -o bench-nedtries.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c common.c -o bench-nedtries.exe
|
||||
@./bench-nedtries.exe 40
|
||||
@./bench-nedtries.exe 45
|
||||
|
||||
# RIGTORP_HASHMAP shall point to the directory where the headers are
|
||||
bench-rigtorp-hashmap:
|
||||
@if test -n "$${RIGTORP_HASHMAP}" ; then $(MAKE) bench-rigtorp-hashmap0 ; else echo "Nothing to be done for RIGTORP_HASHMAP." ; fi
|
||||
|
||||
bench-rigtorp-hashmap0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_HASHMAP}/include" bench-rigtorp-hashmap.cpp common.c -o bench-rigtorp-hashmap.exe
|
||||
@./bench-rigtorp-hashmap.exe 42
|
||||
@./bench-rigtorp-hashmap.exe 41
|
||||
@./bench-rigtorp-hashmap.exe 43
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-plain:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
|
||||
@./bench-plain-c.exe
|
||||
|
||||
############################################################################
|
||||
@@ -286,13 +308,13 @@ bench-plain:
|
||||
bench-mempool:
|
||||
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
|
||||
bench-mempool0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
|
||||
@./bench-mempool.exe
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-maxdict:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
|
||||
@./bench-max-dict.exe
|
||||
|
||||
############################################################################
|
||||
@@ -300,27 +322,42 @@ bench-maxdict:
|
||||
# BSTRLIB shall point to the BSTRLIB sources
|
||||
# LIBSRT shall point to the LIBSRT sources
|
||||
bench-string:
|
||||
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-with BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-with ; fi
|
||||
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-step1 BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-step1 ; fi
|
||||
|
||||
bench-string-with:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
bench-string-step1:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
|
||||
bench-string-with2:
|
||||
@if test -n "$${SDS}" ; then $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
bench-string-step2:
|
||||
@if test -n "$${SDS}" ; then $(MAKE) bench-string-step3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-step3 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-with3:
|
||||
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
bench-string-step3:
|
||||
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-exe:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BSTRLIB}" -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp -o bench-string.exe $(BENCH_DEF)
|
||||
bench-string-step4:
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_GLIB $$(pkg-config --cflags --libs glib-2.0)" ; else $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-step5:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp common.c $(BENCH_DEF) -o bench-string.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string-2.cpp common.c $(BENCH_DEF) -o bench-string-2.exe
|
||||
@./bench-string.exe
|
||||
@./bench-string-2.exe 2000000 0
|
||||
@./bench-string-2.exe 2000000 1
|
||||
@./bench-string-2.exe 2000000 2
|
||||
@./bench-string-2.exe 2000000 3
|
||||
@./bench-string-2.exe 2000000 4
|
||||
|
||||
bench-string-pgo:
|
||||
$(MAKE) bench-string XCFLAGS="-fprofile-generate=pgo"
|
||||
$(MAKE) bench-string XCFLAGS="-fprofile-use=pgo"
|
||||
|
||||
############################################################################
|
||||
|
||||
clean:
|
||||
rm -f *.o bench-*.exe bench-*.s *~ massif.out.*
|
||||
$(RM) *.o bench-*.exe
|
||||
$(RM) bench-*.s *~ massif.out.*
|
||||
$(RM) plot-*.dat
|
||||
$(RM) pgo
|
||||
cd nucleotide && $(MAKE) clean
|
||||
|
||||
############################################################################
|
||||
|
||||
@@ -6,11 +6,8 @@
|
||||
|
||||
#include <boost/lockfree/queue.hpp>
|
||||
|
||||
#include "m-mutex.h"
|
||||
#define MULTI_THREAD_MEASURE
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
boost::lockfree::queue<unsigned int, boost::lockfree::capacity<4*64>> g_buff;
|
||||
boost::lockfree::queue<unsigned long long, boost::lockfree::capacity<4*64>> g_final;
|
||||
@@ -51,7 +48,7 @@ static void prod(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
@@ -88,11 +85,12 @@ static void test_buffer(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
test("BOOST-LOCKFREE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+10
-13
@@ -250,20 +250,17 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("CollectionC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -6,11 +6,8 @@
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
#include "m-mutex.h"
|
||||
#define MULTI_THREAD_MEASURE
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
moodycamel::ConcurrentQueue<unsigned int> g_buff;
|
||||
moodycamel::ConcurrentQueue<unsigned long long> g_final;
|
||||
@@ -51,7 +48,7 @@ static void prod(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
@@ -88,11 +85,12 @@ static void test_buffer(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
test("CONCURRENT-QUEUE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -131,14 +131,14 @@ test_dict_str(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -42,6 +42,23 @@ test_dict2(size_t n)
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
flat_hash_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
flat_hash_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
@@ -108,14 +125,15 @@ test_dict_str(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+10
-13
@@ -186,20 +186,17 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("GLIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/hopscotch_map.h"
|
||||
#include "hopscotch_map.h"
|
||||
|
||||
using namespace tsl;
|
||||
using namespace std;
|
||||
@@ -109,14 +109,15 @@ test_dict_str(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("HOPSCOTCHMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
|
||||
}
|
||||
+10
-13
@@ -185,21 +185,18 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]): 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("B-tree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("DictOA time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("KLIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+28
-18
@@ -45,6 +45,9 @@ static size_t hash_func (void *a)
|
||||
static int equal_func(void *a, void *b)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
if (b == NULL) {
|
||||
return *pa == -1U;
|
||||
}
|
||||
const unsigned long *pb = b;
|
||||
return *pa == *pb;
|
||||
}
|
||||
@@ -52,8 +55,12 @@ static int equal_func(void *a, void *b)
|
||||
static void set_func(void *a, void *b)
|
||||
{
|
||||
unsigned long *pa = a;
|
||||
const unsigned long *pb = b;
|
||||
*pa = *pb;
|
||||
if (b == NULL) {
|
||||
*pa = -1U;
|
||||
} else {
|
||||
const unsigned long *pb = b;
|
||||
*pa = *pb;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct map_element map_element;
|
||||
@@ -67,10 +74,10 @@ static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
map dict;
|
||||
map_construct(&dict, sizeof(map_element), (map_element[]) {{.key = -1}}, set_func);
|
||||
map_construct(&dict, sizeof(map_element), set_func);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, equal_func, set_func, NULL);
|
||||
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, set_func, equal_func, NULL);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
@@ -90,6 +97,8 @@ typedef char char_array_t[256];
|
||||
static int char_equal (void *a, void *b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
if (b == NULL)
|
||||
return strcmp(*pa, "") == 0;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
}
|
||||
@@ -103,8 +112,12 @@ static size_t char_hash(void *a)
|
||||
static void char_set(void *a, void *b)
|
||||
{
|
||||
char_array_t *pa = (char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
strcpy(*pa,*pb);
|
||||
if (b == NULL) {
|
||||
strcpy(*pa, "");
|
||||
} else {
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
strcpy(*pa,*pb);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct big_map_element big_map_element;
|
||||
@@ -118,16 +131,13 @@ static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
map dict;
|
||||
big_map_element empty;
|
||||
strcpy(empty.key, "");
|
||||
strcpy(empty.value, "");
|
||||
map_construct(&dict, sizeof(big_map_element), &empty, char_set);
|
||||
map_construct(&dict, sizeof(big_map_element), char_set);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
big_map_element el;
|
||||
sprintf(el.key, "%u", rand_get());
|
||||
sprintf(el.value, "%u", rand_get());
|
||||
map_insert(&dict, &el, char_hash, char_equal, char_set, NULL);
|
||||
map_insert(&dict, &el, char_hash, char_set, char_equal, NULL);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
@@ -144,14 +154,14 @@ test_dict_big(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("Dict B time", 1000000, test_dict_big);
|
||||
test("LIBDYNAMIC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+14
-28
@@ -6,11 +6,8 @@
|
||||
|
||||
#include "liblfds711.h"
|
||||
|
||||
#include "m-mutex.h"
|
||||
#define MULTI_THREAD_MEASURE
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct lfds711_queue_bmm_state g_buff;
|
||||
@@ -23,12 +20,9 @@ static void final(void *arg)
|
||||
unsigned long long j, s = 0;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(int i = 0; i < n;i++) {
|
||||
int rv;
|
||||
do {
|
||||
void *p;
|
||||
rv = lfds711_queue_bmm_dequeue(&g_final, &p, NULL);
|
||||
j = (uintptr_t)p;
|
||||
} while (rv == 0);
|
||||
void *p;
|
||||
while (0 == lfds711_queue_bmm_dequeue(&g_final, &p, NULL)) { m_thread_yield(); }
|
||||
j = (uintptr_t)p;
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
@@ -42,18 +36,12 @@ static void conso(void *arg)
|
||||
unsigned long long s = 0;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(int i = 0; i < n;i++) {
|
||||
int rv;
|
||||
do {
|
||||
void *p;
|
||||
rv = lfds711_queue_bmm_dequeue(&g_buff, &p, NULL);
|
||||
j = (uintptr_t) p;
|
||||
} while (rv == 0);
|
||||
void *p;
|
||||
while (0 == lfds711_queue_bmm_dequeue(&g_buff, &p, NULL)) { m_thread_yield(); }
|
||||
j = (uintptr_t) p;
|
||||
s += j;
|
||||
}
|
||||
int rv;
|
||||
do {
|
||||
rv = lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL);
|
||||
} while (rv == 0);
|
||||
while (0 == lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL)) { m_thread_yield(); }
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
@@ -63,15 +51,12 @@ static void prod(void *arg)
|
||||
size_t r = n;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
int rv;
|
||||
do {
|
||||
rv = lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL);
|
||||
} while (rv == 0);
|
||||
while (0 == lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL)) { m_thread_yield(); }
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
@@ -120,11 +105,12 @@ static void test_buffer(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
test("LIBLFDS", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+14
-2
@@ -60,13 +60,25 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 50, "sort", 10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("LIBSRT", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
test_function("Array", 100000000, test_array);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test_function("Sort", 10000000, test_sort);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+170
-56
@@ -220,14 +220,36 @@ test_dict_oa(size_t n)
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_oa_linear(size_t n)
|
||||
{
|
||||
M_LET(dict, DICT_OPLIST(dict_oa_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict_oa_ulong_set_at(dict, i, rand_get() );
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *p = dict_oa_ulong_get(dict, i);
|
||||
if (p)
|
||||
s += *p;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
typedef char char_array_t[256];
|
||||
/********************************************************************************************/
|
||||
#ifndef CHAR_ARRAY_SIZE
|
||||
#define CHAR_ARRAY_SIZE 256
|
||||
#endif
|
||||
typedef char char_array_t[CHAR_ARRAY_SIZE];
|
||||
static void char_init (char_array_t a) { a[0] = 0; }
|
||||
static void char_set (char_array_t a, const char_array_t b) { strcpy(a, b); }
|
||||
static bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a,b)==0; }
|
||||
static size_t char_hash(const char_array_t a) { return m_core_hash (a, strlen(a)); }
|
||||
#define M_OPL_char_array_t() (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p))
|
||||
static bool char_oor_equal_p(const char_array_t a, unsigned char n) { return a[0] == 1+n; }
|
||||
static void char_oor_set(char_array_t a, unsigned char n) { a[0] = 1+n; }
|
||||
#define M_OPL_char_array_t() (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p), OOR_EQUAL(char_oor_equal_p), OOR_SET(char_oor_set))
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(M_OPL_char_array_t(),MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, M_OPL_char_array_t())
|
||||
@@ -235,6 +257,7 @@ DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(M_OPL_char_array_t(),MEM
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPL_char_array_t(), char_array_t, M_OPL_char_array_t())
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
@@ -264,6 +287,31 @@ test_dict_big(size_t n)
|
||||
#endif
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_oa_char, char_array_t, char_array_t)
|
||||
|
||||
static void
|
||||
test_dict_oa_big(size_t n)
|
||||
{
|
||||
M_LET(dict, DICT_OPLIST(dict_oa_char)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
sprintf(s2, "%u", rand_get());
|
||||
dict_oa_char_set_at(dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
char_array_t *p = dict_oa_char_get(dict, s1);
|
||||
if (p)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
@@ -441,7 +489,7 @@ static void conso_mpmc(void *arg)
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_uint_pop(&j, g_buff_mpmc));
|
||||
while (!queue_uint_pop(&j, g_buff_mpmc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
while (!queue_ull_push(g_final_mpmc, s));
|
||||
@@ -453,7 +501,7 @@ static void prod_mpmc(void *arg)
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!queue_uint_push(g_buff_mpmc, r ));
|
||||
while (!queue_uint_push(g_buff_mpmc, r )) m_thread_yield();
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
@@ -526,7 +574,7 @@ static void conso_spsc(void *arg)
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_uint_pop(&j, g_buff_spsc));
|
||||
while (!queue_single_uint_pop(&j, g_buff_spsc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
while (!queue_single_ull_push(g_final_spsc, s));
|
||||
@@ -538,7 +586,7 @@ static void prod_spsc(void *arg)
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!queue_single_uint_push(g_buff_spsc, r ));
|
||||
while (!queue_single_uint_push(g_buff_spsc, r )) m_thread_yield();
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
@@ -581,6 +629,96 @@ static void test_queue_single(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define BULK_SIZE 20
|
||||
|
||||
static void final_spsc_bulk(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_ull_pop(&j, g_final_spsc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso_spsc_bulk(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
unsigned tab[BULK_SIZE];
|
||||
for(int i = 0; i < n;i+= BULK_SIZE) {
|
||||
unsigned k = queue_single_uint_pop_bulk(BULK_SIZE, tab, g_buff_spsc);
|
||||
while (k != BULK_SIZE) {
|
||||
m_thread_yield();
|
||||
k += queue_single_uint_pop_bulk(BULK_SIZE-k, tab+k, g_buff_spsc);
|
||||
}
|
||||
for(k = 0; k < BULK_SIZE;k++)
|
||||
s += tab[k];
|
||||
}
|
||||
while (!queue_single_ull_push(g_final_spsc, s));
|
||||
}
|
||||
|
||||
static void prod_spsc_bulk(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
if ((n % BULK_SIZE) != 0) abort();
|
||||
size_t r = n;
|
||||
unsigned tab[BULK_SIZE];
|
||||
for(unsigned int i = 0; i < n;i+= BULK_SIZE) {
|
||||
for(unsigned k = 0; k < BULK_SIZE; k++) {
|
||||
tab[k] = r;
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
unsigned k = queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE, tab);
|
||||
while (k != BULK_SIZE) {
|
||||
m_thread_yield();
|
||||
k += queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE-k, tab+k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue_single_bulk(size_t n)
|
||||
{
|
||||
const int cpu_count = 2;
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
// Init
|
||||
queue_single_uint_init(g_buff_spsc, 64*cpu_count);
|
||||
queue_single_ull_init (g_final_spsc, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod_spsc_bulk, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso_spsc_bulk, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final_spsc_bulk, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
queue_single_ull_clear(g_final_spsc);
|
||||
queue_single_uint_clear(g_buff_spsc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
DEQUE_DEF(deque_uint, unsigned int)
|
||||
CONCURRENT_DEF(cdeque_uint, deque_uint_t, M_OPEXTEND(DEQUE_OPLIST(deque_uint, M_DEFAULT_OPLIST), PUSH(deque_uint_push_front)))
|
||||
|
||||
@@ -701,57 +839,33 @@ static void test_core_hash(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 11, "DPList", 10000000, 0, test_dlist, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 31, "B+tree", 1000000, 0, test_bptree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(OA)", 1000000, 0, test_dict_oa, 0},
|
||||
{ 43,"DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "dictLinear(OA)", 1000000, 0, test_dict_oa_linear, 0},
|
||||
{ 47, "dictBig(OA)", 1000000, 0, test_dict_oa_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0},
|
||||
{ 60,"Buffer", 1000000, 0, test_buffer, 0},
|
||||
{ 61,"Queue MPMC", 1000000, 0, test_queue, 0},
|
||||
{ 62,"Buffer(P=2)", SIZE_LIMIT+1000000, 0, test_buffer, 0},
|
||||
{ 63,"Queue MPMC (P=2)", SIZE_LIMIT+1000000, 0, test_queue, 0},
|
||||
{ 64,"Queue SPSC (P=2)", 1000000, 0, test_queue_single, 0},
|
||||
{ 65,"Queue Concurrent", 1000000, 0, test_queue_concurrent, 0},
|
||||
{ 66,"Queue SPSC(Bulk)", 1000000, 0, test_queue_single_bulk, 0},
|
||||
{ 70,"M_HASH", 100000000, test_hash_prepare, test_hash, test_hash_final},
|
||||
{ 71,"Core Hash", 100000000, test_hash_prepare, test_core_hash, test_hash_final}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 11)
|
||||
test_function("DPList time",10000000, test_dlist);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 31)
|
||||
test_function("B+tree time", 1000000, test_bptree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 42)
|
||||
test_function("DictOA time", 1000000, test_dict_oa);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS time", 1000000, test_dict_str);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
if (n == 51)
|
||||
test_function("Stable Sort time", 10000000, test_stable_sort);
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
if (n == 61)
|
||||
test_function("Queue MPMC time", 1000000, test_queue);
|
||||
if (n == 62)
|
||||
test_function("Buffer time (P2)", SIZE_LIMIT+1000000, test_buffer);
|
||||
if (n == 63)
|
||||
test_function("Queue MPMC time (P2)", SIZE_LIMIT+1000000, test_queue);
|
||||
if (n == 64)
|
||||
test_function("Queue SPSC time", 1000000, test_queue_single);
|
||||
if (n == 65)
|
||||
test_function("Queue CONCURRENT time", 1000000, test_queue_concurrent);
|
||||
if (n == 70) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("M_HASH time", n, test_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
if (n == 71) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("CORE_HASH time", n, test_core_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
|
||||
test("M-LIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -87,15 +87,13 @@ test_dict_noalloc(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 45, "dict(noalloc)", 1000000, 0, test_dict_noalloc, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 45)
|
||||
test_function("Dict No Alloc time", 1000000, test_dict_noalloc);
|
||||
|
||||
test("NEDTRIES", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+9
-7
@@ -98,14 +98,16 @@ static void test_rbtree(size_t n)
|
||||
|
||||
/* qlibc hash functions only accept a string as the key */
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
test("QLIBC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+11
-15
@@ -162,22 +162,18 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict(m)time", 1000000, test_dict1);
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("QT", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rigtorp/HashMap.h"
|
||||
|
||||
using namespace rigtorp;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Hash for using unsigned long as lookup key
|
||||
struct UL_Hash {
|
||||
size_t operator()(const unsigned long v) { return v * 7; }
|
||||
};
|
||||
|
||||
// Equal comparison for using unsigned long as lookup key
|
||||
struct UL_Equal {
|
||||
bool operator()(const unsigned long lhs, const unsigned long rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal> dict(16, -1UL);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
struct BIG_Hash {
|
||||
size_t operator()(const char_array_s v) { return hash<char_array_s>()(v); }
|
||||
};
|
||||
|
||||
struct BIG_Equal {
|
||||
bool operator()(const char_array_s lhs, const char_array_s rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
char_array_s empty;
|
||||
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal> dict(16, empty);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct STR_Hash {
|
||||
size_t operator()(const string v) { return hash<string>()(v); }
|
||||
};
|
||||
|
||||
struct STR_Equal {
|
||||
bool operator()(const string lhs, const string rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
string empty;
|
||||
HashMap<string, string, STR_Hash, STR_Equal> dict(16, empty);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
HashMap<string, string, STR_Hash, STR_Equal>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RIGTORP-HASHMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -109,14 +109,14 @@ test_dict_str(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("SPARSEPP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+33
-21
@@ -110,6 +110,24 @@ test_dict2(size_t n)
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
unordered_map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unordered_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct char_array_s {
|
||||
@@ -220,28 +238,22 @@ static void test_stable_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
|
||||
{ 43,"DictStr(u)", 1000000, 0, test_dict_str, 0},
|
||||
{ 44,"DictStr(m)", 1000000, 0, test_dict_str2, 0},
|
||||
{ 46, "dictLinear(u)", 1000000, 0, test_dict2_linear, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict(m)time", 1000000, test_dict1);
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
if (n == 44)
|
||||
test_function("DictS(m) time", 1000000, test_dict_str2);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
if (n == 51)
|
||||
test_function("Stable Sort time", 10000000, test_stable_sort);
|
||||
test("STL", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* M*LIB - String Bench
|
||||
*
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "m-string.h"
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
extern "C" {
|
||||
#include "sds.h"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_RAPIDSTRING
|
||||
#include "rapidstring.h"
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
#include "bstrlib.h"
|
||||
#include "bstrwrap.h"
|
||||
#include "bstraux.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* BENCH
|
||||
* 2000000
|
||||
* 1. Generate N strings of random numbers 32 bits.
|
||||
* 2. Concat all strings in a string usigned a random permutation of N.
|
||||
* 3. Replace "1234" in the string into "WELL" until nothing remains.
|
||||
* 4. Replace "56789" in the string into "DONE" until nothing remains.
|
||||
* Return the final string length.
|
||||
*/
|
||||
|
||||
unsigned *permutation_tab = NULL;
|
||||
|
||||
void random_permutation(unsigned n)
|
||||
{
|
||||
if (permutation_tab) free(permutation_tab);
|
||||
permutation_tab = (unsigned *) malloc(n * sizeof(unsigned));
|
||||
if (permutation_tab == NULL) abort();
|
||||
for(unsigned i = 0; i < n; i++)
|
||||
permutation_tab[i] = i;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
unsigned j = rand_get() % n;
|
||||
unsigned k = rand_get() % n;
|
||||
unsigned l = permutation_tab[j];
|
||||
permutation_tab[j] = permutation_tab[k];
|
||||
permutation_tab[k] = l;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
size_t bench_stl(unsigned n)
|
||||
{
|
||||
vector<string> tab;
|
||||
tab.resize(n);
|
||||
for(unsigned i = 0; i < n; i++)
|
||||
tab[i] = to_string(rand_get());
|
||||
|
||||
string str;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str += tab[permutation_tab[i]];
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find("1234", pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, 4, "WELL");
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
|
||||
pos = 0;
|
||||
do {
|
||||
pos = str.find("56789", pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, 5, "DONE");
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
|
||||
return str.length();
|
||||
}
|
||||
|
||||
size_t bench_mlib(unsigned n)
|
||||
{
|
||||
string_t *tab = (string_t*) malloc (n * sizeof (string_t));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_init(tab[i]);
|
||||
string_printf(tab[i], "%u", rand_get());
|
||||
}
|
||||
// P2
|
||||
string_t str;
|
||||
string_init(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_cat(str, tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
size_t i = 0;
|
||||
do {
|
||||
i = string_replace_str(str, "1234", "WELL", i);
|
||||
} while (i != STRING_FAILURE);
|
||||
// P4
|
||||
i = 0;
|
||||
do {
|
||||
i = string_replace_str(str, "56789", "DONE", i);
|
||||
} while (i != STRING_FAILURE);
|
||||
size_t length = string_size(str);
|
||||
// Clean
|
||||
string_clear(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_clear(tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
size_t bench_bstrlib(unsigned n)
|
||||
{
|
||||
vector<CBString> tab;
|
||||
tab.resize(n);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
char tmp[10];
|
||||
sprintf(tmp, "%u", rand_get());
|
||||
tab[i] = tmp;
|
||||
}
|
||||
|
||||
CBString str;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str += tab[permutation_tab[i]];
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find("1234", pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, 4, "WELL");
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
|
||||
pos = 0;
|
||||
do {
|
||||
pos = str.find("56789", pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, 5, "DONE");
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
|
||||
return str.length();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
sds SDS_replace_at(sds str, size_t pos, size_t len, const char str2[])
|
||||
{
|
||||
// simple implementation as replace is not available
|
||||
sds a = sdsnewlen((const void*) str, pos);
|
||||
a = sdscat(a, str2);
|
||||
a = sdscat(a, &str[pos+len]);
|
||||
sdsfree(str);
|
||||
return a;
|
||||
}
|
||||
|
||||
size_t bench_sds(unsigned n)
|
||||
{
|
||||
sds *tab = (sds*) malloc (n * sizeof (sds));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
tab[i] = sdsfromlonglong(rand_get());
|
||||
}
|
||||
// P2
|
||||
sds str = sdsempty();
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str = sdscat(str, tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
size_t pos = 0;
|
||||
do {
|
||||
char *p = strstr(&str[pos], "1234");
|
||||
pos = p == NULL ? -1U : (p - str);
|
||||
if (pos != -1U) {
|
||||
str = SDS_replace_at(str, pos, 4, "WELL");
|
||||
}
|
||||
} while (pos != -1U);
|
||||
// P4
|
||||
pos = 0;
|
||||
do {
|
||||
char *p = strstr(&str[pos], "56789");
|
||||
pos = p == NULL ? -1U : (p - str);
|
||||
if (pos != -1U) {
|
||||
str = SDS_replace_at(str, pos, 5, "DONE");
|
||||
}
|
||||
} while (pos != -1U);
|
||||
size_t length = sdslen(str);
|
||||
// Clean
|
||||
sdsfree(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
sdsfree(tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_RAPIDSTRING
|
||||
void RAPIDSTRING_replace_at(rapidstring *str, size_t pos, size_t len, const char str2[])
|
||||
{
|
||||
// simple implementation as replace is not available
|
||||
rapidstring a;
|
||||
|
||||
rs_init_w_n(&a, rs_data(str), pos);
|
||||
rs_cat(&a, str2);
|
||||
rs_cat(&a, &rs_data(str)[pos+len] );
|
||||
rs_free(str);
|
||||
*str = a;
|
||||
}
|
||||
|
||||
size_t bench_rapidstring(unsigned n)
|
||||
{
|
||||
rapidstring *tab = (rapidstring*) malloc (n * sizeof (rapidstring));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
char tmp[10];
|
||||
sprintf(tmp, "%u", rand_get());
|
||||
rs_init_w(&tab[i], tmp);
|
||||
}
|
||||
// P2
|
||||
rapidstring str;
|
||||
rs_init(&str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
rs_cat_rs(&str, &tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
size_t pos = 0;
|
||||
do {
|
||||
char *p = strstr(&rs_data(&str)[pos], "1234");
|
||||
pos = p == NULL ? -1U : (p - rs_data(&str));
|
||||
if (pos != -1U) {
|
||||
RAPIDSTRING_replace_at(&str, pos, 4, "WELL");
|
||||
}
|
||||
} while (pos != -1U);
|
||||
// P4
|
||||
pos = 0;
|
||||
do {
|
||||
char *p = strstr(&rs_data(&str)[pos], "56789");
|
||||
pos = p == NULL ? -1U : (p - rs_data(&str));
|
||||
if (pos != -1U) {
|
||||
RAPIDSTRING_replace_at(&str, pos, 5, "DONE");
|
||||
}
|
||||
} while (pos != -1U);
|
||||
size_t length = rs_len(&str);
|
||||
// Clean
|
||||
rs_free(&str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
rs_free(&tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
unsigned length, n = atoi(argv[1]);
|
||||
unsigned select = atoi(argv[2]);
|
||||
const char *name;
|
||||
random_permutation(n);
|
||||
rand_init();
|
||||
unsigned long long t0 = cputime();
|
||||
|
||||
switch (select) {
|
||||
case 0:
|
||||
length = bench_mlib(n);
|
||||
name = "MLIB";
|
||||
break;
|
||||
case 1:
|
||||
length = bench_stl(n);
|
||||
name = "STL";
|
||||
break;
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
case 2:
|
||||
length = bench_sds(n);
|
||||
name = "SDS";
|
||||
break;
|
||||
#endif
|
||||
#ifdef BENCH_CAN_USE_RAPIDSTRING
|
||||
case 3:
|
||||
length = bench_rapidstring(n);
|
||||
name = "RAPID";
|
||||
break;
|
||||
#endif
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
case 4:
|
||||
length = bench_bstrlib(n);
|
||||
name = "BSTRLIB";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
unsigned long long t1 = cputime();
|
||||
printf("%5.5s LENGTH=%u T= %f s\n", name, length, (t1-t0) / 1000000.0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+522
-169
File diff suppressed because it is too large.
Load diff
+9
-11
@@ -206,18 +206,16 @@ test_dict_big(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
test("TommyDS", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+10
-12
@@ -199,19 +199,17 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0}
|
||||
{ 50, "sort", 10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]): 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 40 || n == 42)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("UT-HASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -31,17 +31,12 @@ static void test_hash(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 70,"XXHASH64", 100000000, test_hash_prepare, test_hash, test_hash_final}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 70) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("XXHASH64 time", n, test_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
|
||||
test("XXHASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
|
||||
// The global result
|
||||
unsigned long g_result;
|
||||
|
||||
// The rand value
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
M_THREAD_ATTR
|
||||
#endif
|
||||
unsigned int randValue = 0;
|
||||
|
||||
void compiler_barrier(void *p)
|
||||
{
|
||||
// Do nothing, but let the compiler thinks that we did.
|
||||
(void) p;
|
||||
// Can be used for FLTO building
|
||||
// asm volatile("": "+r" (p) : :"memory");
|
||||
}
|
||||
|
||||
struct parse_opt_s {
|
||||
int test_function;
|
||||
double from, to, step, grow;
|
||||
unsigned repeat;
|
||||
bool graph, best, average, quiet;
|
||||
};
|
||||
|
||||
static void
|
||||
parse_config(struct parse_opt_s *opt, int argc, const char *argv[])
|
||||
{
|
||||
char *end;
|
||||
|
||||
opt->test_function = 0;
|
||||
opt->from = 0.0;
|
||||
opt->to = 0.0;
|
||||
opt->step = 0.0;
|
||||
opt->grow = 1.1;
|
||||
opt->graph = false;
|
||||
opt->best = false;
|
||||
opt->average = false;
|
||||
opt->quiet = false;
|
||||
opt->repeat = 1;
|
||||
|
||||
for(int i = 1; i < argc ; i++)
|
||||
{
|
||||
if (argv[i][0] == '-') {
|
||||
if (strcmp(argv[i], "--from") == 0){
|
||||
i++;
|
||||
opt->from = strtod(argv[i], &end);
|
||||
opt->to = opt->from;
|
||||
} else if (strcmp(argv[i], "--to") == 0) {
|
||||
i++;
|
||||
opt->to = strtod(argv[i], &end);
|
||||
opt->grow = 1.1;
|
||||
} else if (strcmp(argv[i], "--step") == 0) {
|
||||
i++;
|
||||
opt->step = strtod(argv[i], &end);
|
||||
opt->grow = 0;
|
||||
} else if (strcmp(argv[i], "--grow") == 0) {
|
||||
i++;
|
||||
opt->grow = strtod(argv[i], &end);
|
||||
opt->step = 0;
|
||||
} else if (strcmp(argv[i], "--repeat") == 0) {
|
||||
i++;
|
||||
opt->repeat = strtol(argv[i], &end, 10);
|
||||
} else if (strcmp(argv[i], "--graph") == 0) {
|
||||
opt->graph = true;
|
||||
} else if (strcmp(argv[i], "--best") == 0) {
|
||||
opt->best = true;
|
||||
opt->average = false;
|
||||
} else if (strcmp(argv[i], "--average") == 0) {
|
||||
opt->average = true;
|
||||
opt->best = false;
|
||||
} else if (strcmp(argv[i], "--quiet") == 0) {
|
||||
opt->quiet = true;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Option unkown: %s.\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
if (*end != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot parse %s. Expected float\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
} else {
|
||||
opt->test_function = strtol (argv[i], &end, 10);
|
||||
if (*end != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot parse %s. Expected function number\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
select_config(int func, size_t n, const config_func_t functions[])
|
||||
{
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
if (functions[i].num == func)
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "ERROR: Function number %d not found. Available functions are:\n", func);
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
fprintf(stderr, " %d: %s\n", functions[i].num, functions[i].funcname);
|
||||
}
|
||||
fprintf(stderr, "USAGE: FUNC_NUMBER [--from number --to number (--grow number | --step number)] [--best|--average] [--quiet|--graph]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void
|
||||
test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[])
|
||||
{
|
||||
// options
|
||||
// NUMBER
|
||||
// --from NUMBER --to NUMBER --step NUMBER --grow NUMBER
|
||||
// --graph --best --average --quiet
|
||||
// --repeat N
|
||||
struct parse_opt_s arg;
|
||||
parse_config(&arg, argc, argv);
|
||||
int i = select_config(arg.test_function, n, functions);
|
||||
double from = arg.from == 0 ? functions[i].default_n : arg.from;
|
||||
double to = arg.from == 0 ? functions[i].default_n : arg.to;
|
||||
FILE *graph_file = NULL;
|
||||
if (arg.graph) {
|
||||
char filename[100];
|
||||
sprintf(filename, "plot-%s-%d.dat", library, arg.test_function);
|
||||
graph_file = fopen(filename, "wt");
|
||||
if (!graph_file) {
|
||||
fprintf(stderr, "ERROR: Cannot create plot.dat\n");
|
||||
exit(-2);
|
||||
}
|
||||
fprintf(graph_file, "# plotting %s-%d : %s\n# N T", library, arg.test_function, functions[i].funcname);
|
||||
}
|
||||
// Do the bench
|
||||
for(double n = from; n <= to ; n = (arg.grow == 0 ? n + arg.step : n * arg.grow))
|
||||
{
|
||||
double best = (1.0/0.0);
|
||||
double avg = 0.0;
|
||||
if (functions[i].init != NULL)
|
||||
functions[i].init(n);
|
||||
for(unsigned r = 0; r < arg.repeat; r++) {
|
||||
double t0 = test_function(arg.graph|arg.best|arg.average|arg.quiet ? NULL : functions[i].funcname, (size_t) n, functions[i].func);
|
||||
best = t0 < best ? t0 : best;
|
||||
avg += t0;
|
||||
}
|
||||
if (functions[i].clear != NULL)
|
||||
functions[i].clear();
|
||||
avg /= arg.repeat;
|
||||
if (arg.graph) {
|
||||
fprintf(graph_file, "%f %f\n", n, arg.average ? avg : best);
|
||||
} else if (arg.quiet) {
|
||||
printf("%lu\n", (unsigned long)(arg.average ? avg : best));
|
||||
} else if (arg.repeat > 1) {
|
||||
if (arg.average == false)
|
||||
printf ("%20.20s time %lu ms for n = %lu *** BEST ***\n", functions[i].funcname, (unsigned long) best, (unsigned long) n);
|
||||
if (arg.best == false)
|
||||
printf ("%20.20s time %lu ms for n = %lu *** AVERAGE ***\n", functions[i].funcname, (unsigned long) avg, (unsigned long) n);
|
||||
}
|
||||
}
|
||||
if (arg.graph) {
|
||||
fclose(graph_file);
|
||||
printf("File plot-%s-%d.dat generated.\n"
|
||||
"Run in gnuplot the following command:\n"
|
||||
"\tplot 'plot-%s-%d.dat' with linespoints linestyle 1\n",
|
||||
library, arg.test_function, library, arg.test_function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
+81
-32
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,35 +25,59 @@
|
||||
#ifndef __COMMON_BENCH__H
|
||||
#define __COMMON_BENCH__H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Needed for M_THREAD_ATTR
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
#include "m-mutex.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) \
|
||||
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/types.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define USE_SYSCTL
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
// Number of elements of a table
|
||||
#define numberof(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
/* Get the current CPU time. */
|
||||
#if defined(_WIN32)
|
||||
/* WINDOWS variant */
|
||||
#include <windows.h>
|
||||
|
||||
static unsigned long long
|
||||
static inline unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
LARGE_INTEGER freq, val;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&val);
|
||||
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
|
||||
LARGE_INTEGER freq, val;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&val);
|
||||
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
|
||||
}
|
||||
|
||||
#else
|
||||
/* UNIX variant */
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static unsigned long long
|
||||
static inline unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
// Multi thread
|
||||
// Multi thread: Uses real time
|
||||
struct timeval tv;
|
||||
gettimeofday (&tv, NULL);
|
||||
return tv.tv_sec * 1000000ULL + tv.tv_usec;
|
||||
#else
|
||||
// Single thread
|
||||
// Single thread: Uses process usage time
|
||||
struct rusage rus;
|
||||
getrusage (0, &rus);
|
||||
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
|
||||
@@ -62,41 +86,61 @@ cputime (void)
|
||||
|
||||
#endif
|
||||
|
||||
unsigned long g_result;
|
||||
unsigned int randValue = 0;
|
||||
// The result of the bench
|
||||
extern unsigned long g_result;
|
||||
|
||||
static void rand_init(void)
|
||||
// The rand value
|
||||
extern
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
M_THREAD_ATTR
|
||||
#endif
|
||||
unsigned int randValue;
|
||||
|
||||
// Pseudo rand value generator.
|
||||
// Not very random but sufficient and fast enough for the bench
|
||||
static inline void rand_init(void)
|
||||
{
|
||||
randValue = 0;
|
||||
}
|
||||
static unsigned int rand_get(void)
|
||||
static inline unsigned int rand_get(void)
|
||||
{
|
||||
randValue = randValue * 31421U + 6927U;
|
||||
return randValue;
|
||||
}
|
||||
|
||||
static void test_function(const char str[], size_t n, void (*func)(size_t))
|
||||
// The function that performs the bench
|
||||
static inline double
|
||||
test_function(const char *str, size_t n, void (*func)(size_t))
|
||||
{
|
||||
double result;
|
||||
unsigned long long start, end;
|
||||
rand_init();
|
||||
start = cputime();
|
||||
(*func)(n);
|
||||
end = cputime();
|
||||
result = (double)(end-start) / 1000.0;
|
||||
end = (end - start) / 1000U;
|
||||
printf ("%s %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
|
||||
if (str != NULL) {
|
||||
printf ("%20.20s time %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) \
|
||||
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define USE_SYSCTL
|
||||
#else
|
||||
# include <unistd.h>
|
||||
// Configuration table
|
||||
typedef struct {
|
||||
int num;
|
||||
const char *funcname;
|
||||
size_t default_n;
|
||||
void (*init)(size_t);
|
||||
void (*func)(size_t);
|
||||
void (*clear)(void);
|
||||
} config_func_t;
|
||||
|
||||
// Perform a parsing of the given arguments and do the bench according to the table
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[]);
|
||||
|
||||
/* Return the number of CPU of the system */
|
||||
static inline int get_cpu_count(void)
|
||||
@@ -120,4 +164,9 @@ static inline int get_cpu_count(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void compiler_barrier(void *p);
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MLIB - TEST module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -59,7 +59,7 @@ static void find_max(void)
|
||||
dict_int64_set_at (dict, get_rand(), get_rand());
|
||||
n++;
|
||||
if ((n & ((1<<20)-1)) == 0) {
|
||||
printf ("n=%lu\n", n);
|
||||
printf ("n=%zu\n", n);
|
||||
}
|
||||
}
|
||||
dict_int64_clear(dict);
|
||||
|
||||
@@ -35,3 +35,7 @@ V0.2.*: * worker_start takes a 'worker_t' as an additional parameter
|
||||
The provided _remove doesn't use an iterator but a 'key', incompatible with the one of array or list.
|
||||
But is like the one provided by the priority queue.
|
||||
|
||||
V0.3.*: * Remove B+TREE _remove method as it has been deprecated (use _erase instead)
|
||||
* Remove DICT _remove method as it has been deprecated (use _erase instead)
|
||||
* Make _it_to_p method of B+TREE / RB-TREE as deprecated (uses _it_until_p instead ).
|
||||
|
||||
Binary file not shown.
+37
@@ -62,3 +62,40 @@ This will have the following gains:
|
||||
|
||||
Once one issue of ISSUES.org is taken into account,
|
||||
its state shall be changed to DONE (the issue shall not be removed).
|
||||
|
||||
|
||||
Debugging
|
||||
===============
|
||||
|
||||
Debugging the library can be quite difficult
|
||||
as it performs heavy preprocessing expansion
|
||||
and expands all code in one line.
|
||||
Different strategy for debugging the library itself are still possible:
|
||||
|
||||
1) Good old printf.
|
||||
Add printf of interresting values in the debugged function.
|
||||
|
||||
|
||||
2) Preprocess the library and then compile the preprocessed file:
|
||||
|
||||
cc -std=c99 -E test-file.c > test-file.i
|
||||
perl -pi -e 's/;/;\n/g' test-file.i
|
||||
cc -std=c99 -c -Wall test-file.i
|
||||
|
||||
The resulting executable can be debugged much more easily.
|
||||
|
||||
|
||||
3) To debug the preprocessing itself, the Boot Wave library can be used
|
||||
and in particular the [wave driver](https://www.boost.org/doc/libs/1_71_0/libs/wave/doc/wave_driver.html)
|
||||
|
||||
Add in the code the following pragmas around the code to debug:
|
||||
|
||||
#pragma wave trace(enable)
|
||||
ARRAY_DEF (array_charp, char *)
|
||||
#pragma wave trace(disable)
|
||||
|
||||
Run the wave driver:
|
||||
|
||||
./wave ex-array01.c -I .. --c99 -t test.trace
|
||||
|
||||
The different expansion performed by the library are described in test.trace
|
||||
+172
-40
@@ -1,29 +1,86 @@
|
||||
|
||||
*Long term issues*
|
||||
------------------
|
||||
|
||||
* TODO #21: Generic Binary serialization :ENHANCEMENT:
|
||||
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
|
||||
|
||||
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
|
||||
|
||||
Some kind of "binary serialization" on the model of get_str/parse_str
|
||||
could be possible. It would be a great feature from the application
|
||||
point-of-view: binary representation is more bandwidth-efficient if
|
||||
used on network communications.
|
||||
Specialize Hashmap for huge objects on supported hardware:
|
||||
|
||||
It will be good to have import/export methods to the
|
||||
XML/JSON/MSGPACK/PROTOBUF/BINARY format.
|
||||
However, adding all of them on by one in the M*LIB containers
|
||||
doesn't seem satisfactory.
|
||||
|
||||
Instead, adding a generic interface for the serialization of data
|
||||
that may be customized by the user to perform the import/export of
|
||||
objects in whatever format they want into what they want (FILE/memory/...).
|
||||
To simplify it, this interface could only support one kind of import/export
|
||||
per compilation unit.
|
||||
based on OA hash map of taggued pointer (needs space for tagging)
|
||||
low bits = low bits of hash
|
||||
pointer to object { key, hash, value }
|
||||
|
||||
Problem:
|
||||
the interface is either too high level (limiting too much what we can do)
|
||||
or too low level (exporting too many internal things of the library).
|
||||
Can detect collision without dereferencing object with 1/2^lowbits chances.
|
||||
Reduce cache usage ==> Shall be faster.
|
||||
|
||||
Use of pointer to avoid moving object on GC.
|
||||
|
||||
|
||||
* TODO #25 : Support of error code model for error handling. :ENHANCEMENT:
|
||||
|
||||
Find a way to support error return code for the API in case of allocation
|
||||
failure.
|
||||
|
||||
==> Any service that returns void shall return a "int".
|
||||
==> In case of allocation failure, it shall return an error.
|
||||
==> M_CALL macro shall stop its execution if the service returns an error code
|
||||
and the error code represents an error (avoid rewritting everything)
|
||||
and throw back the error code (stopping the execution flow).
|
||||
|
||||
services returning already something shall not be modified.
|
||||
|
||||
This model should be applied at the container level only and not globally.
|
||||
Different containers may need different levels of error handling.
|
||||
|
||||
New operator: ERROR_MODEL which value is 0 (abort on error), 1 (throw on error)
|
||||
or 2 (return code on error).
|
||||
|
||||
Find a way to make it while not making the code too complex
|
||||
(try to keep as simple as possible).
|
||||
|
||||
|
||||
* TODO #24 : Add MIN-MAX-HEAP container :ENHANCEMENT:
|
||||
|
||||
See https://en.wikipedia.org/wiki/Min-max_heap
|
||||
as DPRIORITY_QUEUE_DEF ?
|
||||
|
||||
NOTE: Needs for such container?
|
||||
|
||||
|
||||
* TODO #23: Force MOVE semantic :ENHANCEMENT:
|
||||
|
||||
Some type may need to have a force MOVE semantic (for example, they can store
|
||||
pointer to themselves). Currently the INIT_MOVE & MOVE operators are more
|
||||
a help for performance than a strict semantic usage.
|
||||
|
||||
** ARRAY container
|
||||
|
||||
The ARRAY container doesn't support strict MOVE semantic for example.
|
||||
It is not a simple matter as it performs a realloc of the table, thus
|
||||
moving the data before they can be moved using MOVE. Two solutions:
|
||||
|
||||
- New operator MOVE_SELF to fix a type after it has been moved.
|
||||
- If MOVE defined, force another table and then copy by hand the type. This will be slower and consumme more memory.
|
||||
|
||||
** MOVE in OPLIST :ENHANCEMENT:
|
||||
|
||||
Proposal: do not export INIT_MOVE / MOVE operator in OPLIST if the MOVE operator is
|
||||
compatible with a pure COPY semantic. An exported MOVE operator will tell
|
||||
other containers than the type shall be carefully moved using the provided
|
||||
MOVE operator.
|
||||
|
||||
For example for tuple, it shall
|
||||
|
||||
- create an init_move operator if no one has disabled INIT_MOVE,
|
||||
- export the init_move operator if at least one has exported a INIT_MOVE and no one has disabled INIT_MOVE.
|
||||
|
||||
** DO_INIT_MOVE operator
|
||||
|
||||
DO_INIT_MOVE macro is not also fully working for structure
|
||||
defined with [1] tricks but without an explicit INIT_MOVE / MOVE
|
||||
operators as it uses MOVE_DEFAULT which is not (fully compatible).
|
||||
==> Analyse limitation and possible constraint usages.
|
||||
|
||||
|
||||
* TODO #20: Bucket priority queue :ENHANCEMENT:
|
||||
@@ -46,6 +103,7 @@ implementation.
|
||||
* TODO #19: Intrusive Red Black Tree :ENHANCEMENT:
|
||||
|
||||
Add intrusive red black tree.
|
||||
Look also for AVL tree (NOTE: Is there a performance difference between the two?)
|
||||
|
||||
|
||||
* TODO #18: Missing methods :ENHANCEMENT:
|
||||
@@ -59,16 +117,17 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
|
||||
|
||||
A ressouce handler which shall associated a unique handle to a ressource.
|
||||
The handle shall be unique for the ressource and shall not be reused.
|
||||
It is typically a 64 bits integers always incremented (even the program
|
||||
It is typically a 64 bits integers always incremented (even if the program
|
||||
creates one billion ressources per second, the counter won't overflow
|
||||
until 585 years).
|
||||
|
||||
The ressource handler shall also keep count on:
|
||||
The ressource handler shall make an association between a HANDLE 64 bits and:
|
||||
|
||||
- how much real owners claim to own the ressource
|
||||
(the ressource is only owned by the ressource handler, however
|
||||
it acts as delegate of the real owner),
|
||||
it acts as a delegate of the real owner),
|
||||
- how much users keep a pointer to the ressource.
|
||||
- pointer to the resource itself.
|
||||
|
||||
This may be a better alternative than shared_ptr & weak_ptr:
|
||||
|
||||
@@ -77,7 +136,12 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
|
||||
- shared_ref & weak_ref becomes only HANDLE,
|
||||
- all ressources can be freed in one pass.
|
||||
|
||||
Needs lock free dictionnary.
|
||||
Needs lock free dictionnary or at least concurrent dictionnary.
|
||||
|
||||
How to handle multiple resource ?
|
||||
|
||||
* variant: works fine. Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
|
||||
* embedded the type in the ressource handler: more work, API more complex. Memory usage seems better.
|
||||
|
||||
|
||||
* TODO #16: Lock Free List :ENHANCEMENT:
|
||||
@@ -85,28 +149,30 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
|
||||
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.
|
||||
|
||||
A node can be in the following state:
|
||||
** First step: backoff methods :DONE:
|
||||
** Second step: lock free node pool : :DONE:
|
||||
Done as m-c-mempool header.
|
||||
|
||||
- FREE: Can be used for allocation.
|
||||
- IN_USE: The node is currently used in the list.
|
||||
- TO_BE_RECLAIMED: The node is semantically removed, but some others may still be used it.
|
||||
- CAN_BE_RECLAIMED: The node is semantically removed and no one still use it.
|
||||
** Third step: Implement generic lock free list on top of it.
|
||||
|
||||
So separate lists shall be used for the memory handler.
|
||||
It shall also be compatible with NUMA and avoid putting all nodes in the same lists if possible:
|
||||
otherwise the head will become a hot spot and slow down everything.
|
||||
The ABA problem is already taken into account by the memory alloctor
|
||||
provided that the lock free list doesn't try to be smart.
|
||||
|
||||
backoff has be used when using CAS.
|
||||
|
||||
The ABA problem shall also be taken into account. Normaly with the provided state of the nodes,
|
||||
it should not be a problem.
|
||||
|
||||
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be taken into account.
|
||||
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be crefully analyzed when taken into account.
|
||||
|
||||
Questions:
|
||||
- singly or doubly or dual push?
|
||||
- needs to be logically deleted : needs a previous field
|
||||
(NULL if not logically deleted) ? TBC
|
||||
|
||||
|
||||
* TODO #15: Constructor / Destructor Prologue / Epilogue for Stack Exception Handling :ENHANCEMENT:
|
||||
* 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 Stack Exception Handling so that exceptions throwing may work reliably.
|
||||
This is in order to register the constructed object into a proper Exception
|
||||
Handling Stack so that throwing exceptions may work reliably.
|
||||
|
||||
Proposal:
|
||||
|
||||
@@ -115,15 +181,18 @@ Proposal:
|
||||
- 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 will recursively remove them).
|
||||
Object creation will need to add all sub-objects into the stack,
|
||||
then unstack all to push instead the root object (which recursively remove them).
|
||||
|
||||
See also http://freetype.sourceforge.net/david/reliable-c.html#cseh
|
||||
|
||||
How to handle like allocation of the object?
|
||||
How to avoid calling the destructor multiple times? (It is needed?)
|
||||
|
||||
|
||||
* TODO #14: Memory allocation enhancement :ENHANCEMENT:
|
||||
|
||||
Enhancement of the memory allocation scheme to find way to deal properly with advance allocators:
|
||||
Enhancement of the memory allocation scheme to find way to deal properly with advanced allocators:
|
||||
|
||||
- non-default alignment requirements for types,
|
||||
- instance-based allocator (may need instance based variable access),
|
||||
@@ -144,6 +213,17 @@ Update:
|
||||
API transformation support enables "instance-based allocator" to be made easily.
|
||||
Needs some formal operator in the oplist to support it fully and an example.
|
||||
|
||||
Can be supported using another API extension, some more operators and forcing some names:
|
||||
|
||||
* API_N: call like FUNC(obj->extra_data, type)
|
||||
|
||||
'obj' is a forced named corresponding to an alias to an object in the function.
|
||||
Operator needed:
|
||||
|
||||
- EXTRA_DATA: Add an extra-data field wihtin the container. Defines the type of data.
|
||||
|
||||
It is a kind of object inheritance where the container inherits some extra data from its base.
|
||||
|
||||
|
||||
* TODO #12: Atomic shared pointer :ENHANCEMENT:
|
||||
|
||||
@@ -162,7 +242,10 @@ No _ref or direct _init: we need to init first a normal shared_ptr then the atom
|
||||
|
||||
** _atomic_set method:
|
||||
|
||||
It can be implemented by incrementing the non atomic shared pointer reference, then performs a compare_and_swap to the data of the atomic shared pointer, finally decrement and dec/free the swapped previous data of the atomic .
|
||||
It can be implemented by incrementing the non atomic shared pointer reference,
|
||||
then performs a compare_and_swap to the data of the atomic shared pointer,
|
||||
finally decrement and dec/free the swapped previous data of the atomic like a normal shared pointer.
|
||||
All 3 steps are safe.
|
||||
|
||||
** _set_atomic method:
|
||||
|
||||
@@ -261,3 +344,52 @@ Another solution is to create a huge list of items which is:
|
||||
|
||||
Hash table will only give quick access to items to this list. Expanding the table won't change the order of the list (so the items will remain at the same place and always accessible by other threads) but just add sentinel in the sentinel at the right place (there won't be any reallocation of the list). Inserting will need to insert an item at the right place.
|
||||
Not sure it will really help. To analyse.
|
||||
|
||||
Concurrent dictionary is possible now with CONCURRENT_DEF + DICT_DEF.
|
||||
But it uses a global lock for all dictionary access.
|
||||
|
||||
** Open Addressing Indirect Table with a lock free node pool used as transaction pool.
|
||||
|
||||
A good way maybe Open Addessing table used only for indirection and a freelist memory reclamation container for handle the entries
|
||||
(like a transaction).
|
||||
|
||||
* DONE #22: Enhanced services for SPSC Queue :ENHANCEMENT:
|
||||
|
||||
Add services:
|
||||
|
||||
** DONE _push_bulk
|
||||
Test the capacity of the queue and push as much as possible in the queue
|
||||
with one check of the atomic structure.
|
||||
|
||||
** DONE _pop_bulk
|
||||
Test the capacity of the queue and push as much as possible in the queue
|
||||
with one check of the atomic structure.
|
||||
|
||||
** DONE _push_force:
|
||||
|
||||
If the capacity of the queue is full, pop one element and push it:
|
||||
push always succeed and the queue always keep the youngest element.
|
||||
|
||||
* DONE #21: Generic Binary serialization :ENHANCEMENT:
|
||||
|
||||
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
|
||||
|
||||
Some kind of "binary serialization" on the model of get_str/parse_str
|
||||
could be possible. It would be a great feature from the application
|
||||
point-of-view: binary representation is more bandwidth-efficient if
|
||||
used on network communications.
|
||||
|
||||
It will be good to have import/export methods to the
|
||||
XML/JSON/MSGPACK/PROTOBUF/BINARY format.
|
||||
However, adding all of them on by one in the M*LIB containers
|
||||
doesn't seem satisfactory.
|
||||
|
||||
Instead, adding a generic interface for the serialization of data
|
||||
that may be customized by the user to perform the import/export of
|
||||
objects in whatever format they want into what they want (FILE/memory/...).
|
||||
To simplify it, this interface could only support one kind of import/export
|
||||
per compilation unit.
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 99 KiB |
+10
-4
@@ -1,18 +1,24 @@
|
||||
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
|
||||
GMP_DIR=
|
||||
CPPFLAGS=-I..
|
||||
LDFLAGS=-pthread -lgmp
|
||||
LDFLAGS=-pthread
|
||||
RM=rm -f
|
||||
|
||||
.SUFFIXES: .c .exe
|
||||
|
||||
.c.exe:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -o $@
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) `cat config` $< -o $@
|
||||
|
||||
all:
|
||||
all: config
|
||||
$(MAKE) `ls ex-*.c|sed -e 's/\.c/\.exe/g'`
|
||||
|
||||
config:
|
||||
printf "#include <gmp.h>\nint main(void){mpz_t z; mpz_init(z); return 0;}\n" > config-test.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp config-test.c -o config-test.exe 2> /dev/null && echo "-DHAVE_GMP=1 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp" > config || echo "-DHAVE_GMP=0" > config
|
||||
$(RM) config-test.*
|
||||
|
||||
clean:
|
||||
$(RM) *.exe *.s *~ *.o ./a.dat
|
||||
$(RM) *.exe *.s *~ *.o ./a.dat config
|
||||
|
||||
|
||||
@@ -0,0 +1,619 @@
|
||||
/* Include M*LIB header */
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-tuple.h"
|
||||
|
||||
/* Provide several examples of different types usage with array,
|
||||
with the oplist to use for each case.
|
||||
|
||||
Each time it creates and initializes an array,
|
||||
fills it with some number,
|
||||
computes its sum,
|
||||
and returns it.
|
||||
|
||||
This is done for several basic C types:
|
||||
- integers,
|
||||
- floats,
|
||||
- pointers,
|
||||
- structures,
|
||||
- tuples
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Generate prototypes and inline functions
|
||||
for basic types.
|
||||
For integer and floats, there is no need to specify
|
||||
an oplist. M_DEFAULT_OPLIST will be used.
|
||||
*/
|
||||
ARRAY_DEF(r_bool, bool)
|
||||
|
||||
/* Basic usage of the type */
|
||||
static int
|
||||
test_bool(int n)
|
||||
{
|
||||
r_bool_t array;
|
||||
|
||||
r_bool_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_bool_push_back(array, i & 1);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_bool_get(array, i);
|
||||
}
|
||||
|
||||
r_bool_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type char */
|
||||
ARRAY_DEF(r_char, char)
|
||||
|
||||
static int
|
||||
test_char(int n)
|
||||
{
|
||||
r_char_t array;
|
||||
|
||||
r_char_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_char_push_back(array, i & 255);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_char_get(array, i);
|
||||
}
|
||||
|
||||
r_char_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type signed int. */
|
||||
ARRAY_DEF(r_int, signed int)
|
||||
|
||||
static int
|
||||
test_int(int n)
|
||||
{
|
||||
r_int_t array;
|
||||
|
||||
r_int_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_int_push_back(array, i);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_int_get(array, i);
|
||||
}
|
||||
|
||||
r_int_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type signed int.
|
||||
But with an explicit oplist.
|
||||
This generates exactly the same code than r_int
|
||||
*/
|
||||
ARRAY_DEF(r_int2, signed int, M_DEFAULT_OPLIST)
|
||||
|
||||
static int
|
||||
test_int2(int n)
|
||||
{
|
||||
r_int2_t array;
|
||||
|
||||
r_int2_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_int2_push_back(array, i);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_int2_get(array, i);
|
||||
}
|
||||
|
||||
r_int2_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type unsigned long long */
|
||||
ARRAY_DEF(r_ullong, unsigned long long)
|
||||
|
||||
static int
|
||||
test_ullong(int n)
|
||||
{
|
||||
r_ullong_t array;
|
||||
|
||||
r_ullong_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_ullong_push_back(array, i);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_ullong_get(array, i);
|
||||
}
|
||||
|
||||
r_ullong_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type float */
|
||||
ARRAY_DEF(r_float, float)
|
||||
|
||||
static int
|
||||
test_float(int n)
|
||||
{
|
||||
r_float_t array;
|
||||
|
||||
r_float_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_float_push_back(array, i/4.0);
|
||||
}
|
||||
|
||||
float s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_float_get(array, i);
|
||||
}
|
||||
|
||||
r_float_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type double and an explicit oplist*/
|
||||
ARRAY_DEF(r_double, double, M_DEFAULT_OPLIST)
|
||||
|
||||
static int
|
||||
test_double(int n)
|
||||
{
|
||||
r_double_t array;
|
||||
|
||||
r_double_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_double_push_back(array, i/4.0);
|
||||
}
|
||||
|
||||
float s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_double_get(array, i);
|
||||
}
|
||||
|
||||
r_double_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Helper function strdup */
|
||||
static char *my_strdup(const char *p)
|
||||
{
|
||||
size_t s = strlen(p);
|
||||
char *d= malloc(s+1);
|
||||
if(!d) abort();
|
||||
strcpy(d, p);
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Same with type const char *, representing a contant string.
|
||||
We *** need *** an explicit oplist to tell M*LIB
|
||||
to consider the type as a constant string.
|
||||
The oplist to use is M_CSTR_OPLIST.
|
||||
We could have used M_PTR_OPLIST too,
|
||||
but in this case, we won't get proper string order or
|
||||
string equality.
|
||||
*/
|
||||
ARRAY_DEF(r_cstring, const char *, M_CSTR_OPLIST)
|
||||
|
||||
static int
|
||||
test_cstring(int n)
|
||||
{
|
||||
r_cstring_t array;
|
||||
|
||||
r_cstring_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
char buffer[10];
|
||||
// Create a string from the integer
|
||||
sprintf(buffer, "%d", i);
|
||||
// We need to make an explicit copy of the string
|
||||
// since it is not supported by the basic type.
|
||||
char *p = my_strdup(buffer);
|
||||
// Push the copy in the container.
|
||||
r_cstring_push_back(array, p);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get back the string
|
||||
const char *p = *r_cstring_get(array, i);
|
||||
// Convert it to integer
|
||||
int j = atoi(p);
|
||||
s += j;
|
||||
}
|
||||
|
||||
// We also need a special unalloc pass
|
||||
// to free all constant strings we have allocated.
|
||||
for(int i = 0; i < n; i++) {
|
||||
const char *p = *r_cstring_get(array, i);
|
||||
free((void*)(uintptr_t)p);
|
||||
}
|
||||
|
||||
r_cstring_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type string_t of M*LIB, representing a variable string.
|
||||
We don't need explicit oplist to tell M*LIB!
|
||||
The oplist to use is STRING_OPLIST,
|
||||
but it has been registered globaly, so we don't need to explicit
|
||||
give it to array_def! */
|
||||
ARRAY_DEF(r_string, string_t)
|
||||
|
||||
static int
|
||||
test_string(int n)
|
||||
{
|
||||
r_string_t array;
|
||||
string_t str;
|
||||
|
||||
r_string_init(array);
|
||||
string_init (str);
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
// create a string from an integer
|
||||
string_printf(str, "%d", i);
|
||||
// No need to make a copy in the container!
|
||||
r_string_push_back(array, str);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get back the string and convert it back to an integer
|
||||
int j = atoi(string_get_cstr(*r_string_get(array, i)));
|
||||
s += j;
|
||||
}
|
||||
|
||||
// No need to explicit dealloc !
|
||||
string_clear(str);
|
||||
r_string_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type string_t of M*LIB, representing a variable string
|
||||
and its explicit oplist. And said above, it generates exactly the
|
||||
same code. */
|
||||
ARRAY_DEF(r_string2, string_t, STRING_OPLIST)
|
||||
|
||||
|
||||
|
||||
/* Same with type 'volatile unsigned int *'
|
||||
The array stores pointers to another integer,
|
||||
allocated somewhere else.
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as pointers without caring for what the value of
|
||||
the pointer is.
|
||||
So here, we **need** to specify the oplist of the type
|
||||
as M_PTR_OPLIST.
|
||||
*/
|
||||
ARRAY_DEF(r_vintptr, volatile unsigned int *, M_PTR_OPLIST)
|
||||
|
||||
static int
|
||||
test_vintptr(int n)
|
||||
{
|
||||
r_vintptr_t array;
|
||||
|
||||
// Allocate a secondary table to store the value.
|
||||
unsigned *tab = calloc(n, sizeof(volatile unsigned int));
|
||||
if (!tab) abort();
|
||||
|
||||
r_vintptr_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
tab[i] = i*i - i;
|
||||
// Push the pointer to this integer in the array
|
||||
r_vintptr_push_back(array, &tab[i]);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the integer.
|
||||
volatile unsigned *p = *r_vintptr_get(array, i);
|
||||
s += *p;
|
||||
}
|
||||
|
||||
r_vintptr_clear(array);
|
||||
free(tab);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type.
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as POD;
|
||||
So here, we **need** to specify the oplist of the type
|
||||
as M_POD_OPLIST.
|
||||
*/
|
||||
struct rock_me_out {
|
||||
int n;
|
||||
float other;
|
||||
};
|
||||
|
||||
ARRAY_DEF(r_rockme, struct rock_me_out, M_POD_OPLIST)
|
||||
|
||||
static int
|
||||
test_rockme(int n)
|
||||
{
|
||||
r_rockme_t array;
|
||||
|
||||
r_rockme_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
struct rock_me_out rock;
|
||||
rock.n = i*i - i;
|
||||
// Push the struct in the array
|
||||
r_rockme_push_back(array, rock);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the struct
|
||||
struct rock_me_out *p = r_rockme_get(array, i);
|
||||
s += p->n;
|
||||
}
|
||||
|
||||
r_rockme_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined with [1]:
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as a special array;
|
||||
So here, we **need** to specify the oplist of the type
|
||||
as M_A1_OPLIST.
|
||||
*/
|
||||
typedef struct rock_me_in {
|
||||
int n;
|
||||
float other;
|
||||
} rock_me_in[1];
|
||||
|
||||
ARRAY_DEF(r_rockme2, rock_me_in, M_A1_OPLIST)
|
||||
|
||||
static int
|
||||
test_rockme2(int n)
|
||||
{
|
||||
r_rockme2_t array;
|
||||
|
||||
r_rockme2_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_me_in rock;
|
||||
rock->n = i*i - i;
|
||||
r_rockme2_push_back(array, rock);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the struct.
|
||||
rock_me_in *p = r_rockme2_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme2_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined with [1] and a global registeration.
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as a special array.
|
||||
|
||||
To do so, we register the oplist of rock_us_t by defining a macro based
|
||||
on M_OPL_ + type name + () which expands with the oplist of the type.
|
||||
|
||||
Then we define the array, giving it only the type.
|
||||
M*LIB will look at the global registered oplist of the type, we don't
|
||||
need to give it explictly.
|
||||
|
||||
Global registeration makes easier code.
|
||||
Registeration should be done when the type is defined.
|
||||
*/
|
||||
#define M_OPL_rock_me_in() M_A1_OPLIST
|
||||
|
||||
ARRAY_DEF(r_rockme2b, rock_me_in)
|
||||
|
||||
static int
|
||||
test_rockme2b(int n)
|
||||
{
|
||||
r_rockme2b_t array;
|
||||
|
||||
r_rockme2b_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_me_in rock;
|
||||
rock->n = i*i - i;
|
||||
r_rockme2b_push_back(array, rock);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the struct.
|
||||
rock_me_in *p = r_rockme2b_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme2b_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined as a tuple.
|
||||
|
||||
We first define a tuple. No need to specify the oplist as it uses basic C types
|
||||
so M_DEFAULT_OPLIST is used.
|
||||
|
||||
Then we define the array, giving it the oplist definition of the tuple:
|
||||
the macro TUPLE_OPLIST is used to build it based on the tuple name,
|
||||
and the oplists of the tuple.
|
||||
*/
|
||||
TUPLE_DEF2(rock_you, (n, int), (other, float))
|
||||
|
||||
ARRAY_DEF(r_rockme3, rock_you_t, TUPLE_OPLIST(rock_you, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST))
|
||||
|
||||
static int
|
||||
test_rockme3(int n)
|
||||
{
|
||||
r_rockme3_t array;
|
||||
rock_you_t x;
|
||||
|
||||
r_rockme3_init(array);
|
||||
rock_you_init(x);
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_you_set_n(x, i*i - i);
|
||||
r_rockme3_push_back(array, x);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the structure containing the integer.
|
||||
rock_you_t *p = r_rockme3_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme3_clear(array);
|
||||
rock_you_clear(x);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined as a tuple using global registeration.
|
||||
|
||||
We first define a tuple. No need to specify the oplist as it uses basic C types
|
||||
so M_DEFAULT_OPLIST is used.
|
||||
|
||||
Then we register the oplist of rock_us_t by defining a macro based
|
||||
on M_OPL_ + type name + () which expands with the oplist of the type.
|
||||
|
||||
Then we define the array, giving it only the type.
|
||||
M*LIB will look at the global registered oplist of the type, we don't
|
||||
need to give it explictly.
|
||||
|
||||
Global registeration makes easier code.
|
||||
*/
|
||||
TUPLE_DEF2(rock_us, (n, int), (other, float))
|
||||
|
||||
#define M_OPL_rock_us_t() TUPLE_OPLIST(rock_us, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
ARRAY_DEF(r_rockme4, rock_us_t)
|
||||
|
||||
static int
|
||||
test_rockme4(int n)
|
||||
{
|
||||
r_rockme4_t array;
|
||||
rock_us_t x;
|
||||
|
||||
r_rockme4_init(array);
|
||||
rock_us_init(x);
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_us_set_n(x, i*i - i);
|
||||
r_rockme4_push_back(array, x);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the structure containing the integer.
|
||||
rock_us_t *p = r_rockme4_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme4_clear(array);
|
||||
rock_us_clear(x);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, const char *argv[])
|
||||
{
|
||||
int n = argc == 1 ? 10 : atoi(argv[1]);
|
||||
int s;
|
||||
|
||||
s = test_bool(n);
|
||||
printf ("S[bool] = %d\n", s);
|
||||
|
||||
s = test_char(n);
|
||||
printf ("S[char] = %d\n", s);
|
||||
|
||||
s = test_int(n);
|
||||
printf ("S[int] = %d\n", s);
|
||||
|
||||
s = test_int2(n);
|
||||
printf ("S[int2] = %d\n", s);
|
||||
|
||||
s = test_ullong(n);
|
||||
printf ("S[ullong] = %d\n", s);
|
||||
|
||||
s = test_float(n);
|
||||
printf ("S[float] = %d\n", s);
|
||||
|
||||
s = test_double(n);
|
||||
printf ("S[double] = %d\n", s);
|
||||
|
||||
s = test_cstring(n);
|
||||
printf ("S[cstring] = %d\n", s);
|
||||
|
||||
s = test_string(n);
|
||||
printf ("S[string] = %d\n", s);
|
||||
|
||||
s = test_vintptr(n);
|
||||
printf ("S[vintptr] = %d\n", s);
|
||||
|
||||
s = test_rockme(n);
|
||||
printf ("S[rockme] = %d\n", s);
|
||||
|
||||
s = test_rockme2(n);
|
||||
printf ("S[rockme2] = %d\n", s);
|
||||
|
||||
s = test_rockme2b(n);
|
||||
printf ("S[rockme2b] = %d\n", s);
|
||||
|
||||
s = test_rockme3(n);
|
||||
printf ("S[rockme3] = %d\n", s);
|
||||
|
||||
s = test_rockme3(n);
|
||||
printf ("S[rockme4] = %d\n", s);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
+13
-1
@@ -1,9 +1,13 @@
|
||||
#if HAVE_GMP
|
||||
|
||||
#include <gmp.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
/* Define an array of mpz_t */
|
||||
ARRAY_DEF(array_mpz, mpz_t, M_CLASSIC_OPLIST(mpz))
|
||||
/* Define some algorithm functions on array_mpz_t */
|
||||
ALGO_DEF(array_mpz, ARRAY_OPLIST(array_mpz, M_CLASSIC_OPLIST(mpz)))
|
||||
|
||||
static inline void my_mpz_inc(mpz_t *d, const mpz_t a){
|
||||
@@ -19,14 +23,22 @@ int main(void)
|
||||
mpz_init_set_ui(z, 1);
|
||||
array_mpz_t a;
|
||||
array_mpz_init(a);
|
||||
|
||||
array_mpz_push_back(a, z);
|
||||
for(size_t i = 2 ; i < 1000; i++) {
|
||||
mpz_mul_ui(z, z, i);
|
||||
array_mpz_push_back(a, z);
|
||||
}
|
||||
/* z = sum (a[i]^2) */
|
||||
|
||||
/* Compute z = sum (a[i]^2) */
|
||||
array_mpz_map_reduce (&z, a, my_mpz_inc, my_mpz_sqr);
|
||||
gmp_printf ("Z=%Zd\n", z);
|
||||
|
||||
array_mpz_clear(a);
|
||||
mpz_clear(z);
|
||||
}
|
||||
|
||||
#else
|
||||
// Nothing to do
|
||||
int main(void) { return 0; }
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* This example show how to use complex structure with array
|
||||
embedding another library */
|
||||
|
||||
/* This is a trivial library */
|
||||
typedef struct lib_ext_struct {
|
||||
int id;
|
||||
// Other data
|
||||
} lib_ext_struct;
|
||||
|
||||
static lib_ext_struct *lib_ext_struct_Duplicate(const lib_ext_struct *obj)
|
||||
{
|
||||
lib_ext_struct *p = malloc(sizeof(lib_ext_struct));
|
||||
if (!p) abort();
|
||||
p->id = obj->id;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void lib_ext_struct_Delete(lib_ext_struct *obj)
|
||||
{
|
||||
free(obj);
|
||||
}
|
||||
|
||||
/* This is the complex structure */
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
string_t type;
|
||||
lib_ext_struct* properties;
|
||||
} data_node;
|
||||
|
||||
static void data_node_init(data_node *obj)
|
||||
{
|
||||
obj->id = 0;
|
||||
string_init(obj->type);
|
||||
obj->properties = NULL;
|
||||
}
|
||||
|
||||
static void data_node_init_set(data_node *obj, const data_node *src)
|
||||
{
|
||||
obj->id = src->id;
|
||||
string_init_set(obj->type, src->type);
|
||||
if (src->properties)
|
||||
obj->properties = lib_ext_struct_Duplicate(src->properties);
|
||||
else
|
||||
obj->properties = NULL;
|
||||
}
|
||||
|
||||
static void data_node_set(data_node *obj, const data_node *src)
|
||||
{
|
||||
obj->id = src->id;
|
||||
string_set(obj->type, src->type);
|
||||
if (obj->properties)
|
||||
lib_ext_struct_Delete(obj->properties);
|
||||
if (src->properties)
|
||||
obj->properties = lib_ext_struct_Duplicate(src->properties);
|
||||
else
|
||||
obj->properties = NULL;
|
||||
}
|
||||
|
||||
static void data_node_clear(data_node *obj)
|
||||
{
|
||||
string_clear(obj->type);
|
||||
if (obj->properties)
|
||||
lib_ext_struct_Delete(obj->properties);
|
||||
}
|
||||
|
||||
|
||||
ARRAY_DEF(array_data_node, data_node, (INIT(API_2(data_node_init)),SET(API_6(data_node_set)),INIT_SET(API_6(data_node_init_set)),CLEAR(API_2(data_node_clear))))
|
||||
|
||||
array_data_node_t global_array;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
array_data_node_init(global_array);
|
||||
array_data_node_clear(global_array);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "m-bptree.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Define a map int -> string
|
||||
BPTREE_DEF2(map_employee_id, 5, int, string_t)
|
||||
#define M_OPL_map_employee_id_t() BPTREE_OPLIST2(map_employee_id, M_DEFAULT_OPLIST, STRING_OPLIST)
|
||||
|
||||
// Define a map string -> int
|
||||
BPTREE_DEF2(map_employee_name, 5, string_t, int)
|
||||
#define M_OPL_map_employee_name_t() BPTREE_OPLIST2(map_employee_name, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
map_employee_id_t Employees;
|
||||
map_employee_id_init(Employees);
|
||||
|
||||
// Assign
|
||||
map_employee_id_set_at(Employees, 17425, STRING_CTE("Mike"));
|
||||
map_employee_id_set_at(Employees, 7147, STRING_CTE("Tango"));
|
||||
map_employee_id_set_at(Employees, 841, STRING_CTE("Charlie"));
|
||||
map_employee_id_set_at(Employees, 11632, STRING_CTE("John Do"));
|
||||
map_employee_id_set_at(Employees, 852, STRING_CTE("Peter"));
|
||||
map_employee_id_set_at(Employees, 530, STRING_CTE("Amanda"));
|
||||
map_employee_id_set_at(Employees, 4258, STRING_CTE("Elisabeth"));
|
||||
|
||||
printf("Number of employees: %zu\n", map_employee_id_size(Employees));
|
||||
|
||||
// Iterate over the employee list
|
||||
printf("\nList of employees (sorted by id):\n");
|
||||
map_employee_id_it_t it;
|
||||
for(map_employee_id_it(it, Employees); !map_employee_id_end_p(it); map_employee_id_next(it)) {
|
||||
// Get a pointer to this employee from the iterator
|
||||
const map_employee_id_type_t *e = map_employee_id_cref(it);
|
||||
// Print it
|
||||
printf ("%d: %s\n", *(e->key_ptr), string_get_cstr(*(e->value_ptr)));
|
||||
}
|
||||
|
||||
// Let's create a reverse map
|
||||
M_LET(Employees_r, map_employee_name_t) {
|
||||
|
||||
// Let's fill in the reverse map with each employee
|
||||
// (using macro M_EACH)
|
||||
for M_EACH(e, Employees, map_employee_id_t) {
|
||||
map_employee_name_set_at(Employees_r, *(e->value_ptr), *(e->key_ptr));
|
||||
}
|
||||
|
||||
// Iterate over the employee list
|
||||
printf("\nList of employees (sorted by name):\n");
|
||||
for M_EACH(e, Employees_r, map_employee_name_t) {
|
||||
printf ("%d: %s\n", *(e->value_ptr), string_get_cstr(*(e->key_ptr)));
|
||||
}
|
||||
|
||||
// Print
|
||||
printf("\nElisabeth has id = %d\n", *map_employee_name_get(Employees_r, STRING_CTE("Elisabeth")));
|
||||
|
||||
}
|
||||
|
||||
map_employee_id_clear(Employees);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "m-serial-json.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
|
||||
// Serial json is not supported for standard types if not C11
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
|
||||
// Let's define a 'person' composed of several fields.
|
||||
TUPLE_DEF2(person,
|
||||
(surname, string_t),
|
||||
(age, int),
|
||||
(present, bool)
|
||||
)
|
||||
#define M_OPL_person_t() TUPLE_OPLIST(person, M_DEFAULT_OPLIST, STRING_OPLIST, M_DEFAULT_OPLIST )
|
||||
|
||||
// Let's define an array of person
|
||||
ARRAY_DEF(base, person_t)
|
||||
#define M_OPL_base_t() ARRAY_OPLIST(base, M_OPL_person_t())
|
||||
|
||||
// Let's read an array of person from the given JSON file
|
||||
static void read(base_t base, const char filename[])
|
||||
{
|
||||
m_serial_read_t in;
|
||||
m_serial_return_code_t ret;
|
||||
|
||||
// Open the file
|
||||
FILE *f = fopen(filename, "rt");
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open file '%s'.\n", filename);
|
||||
exit(2);
|
||||
}
|
||||
// Initialize the serializer with the file
|
||||
m_serial_json_read_init(in, f);
|
||||
// Read the array
|
||||
ret = base_in_serial(base, in);
|
||||
if (ret != M_SERIAL_OK_DONE) {
|
||||
fprintf(stderr, "ERROR: Cannot read JSON data from file '%s'.\n", filename);
|
||||
exit(2);
|
||||
}
|
||||
// Let's close & clear all
|
||||
m_serial_json_read_clear(in);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
M_LET(base, base_t) {
|
||||
read(base, "ex-json01.json");
|
||||
printf ("List of presents:\n");
|
||||
for M_EACH(el, base, base_t) {
|
||||
if ((*el)->present) {
|
||||
printf("%s\n", string_get_cstr((*el)->surname));
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{ "surname": "Angel", "age": 1, "present": false},
|
||||
{ "surname": "Snake", "age": 2, "present": true},
|
||||
{ "surname": "BallOfBase", "age": 5, "present": true}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#if HAVE_GMP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gmp.h>
|
||||
|
||||
@@ -61,3 +63,8 @@ int main(void)
|
||||
}
|
||||
} /* All variables are automatically cleared beyond this point */
|
||||
}
|
||||
|
||||
#else
|
||||
// Nothing to do
|
||||
int main(void) { return 0; }
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-string.h"
|
||||
|
||||
TUPLE_DEF2(symbol, (offset, long), (value, long))
|
||||
#define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
ARRAY_DEF(array_symbol, symbol_t)
|
||||
#define M_OPL_array_symbol_t() ARRAY_OPLIST(array_symbol, M_OPL_symbol_t())
|
||||
|
||||
DICT_DEF2(sections, string_t, array_symbol_t)
|
||||
#define M_OPL_sections_t() DICT_OPLIST(sections, STRING_OPLIST, M_OPL_array_symbol_t())
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc < 2) abort();
|
||||
FILE *f = fopen(argv[1], "rt");
|
||||
if (!f) abort();
|
||||
M_LET(sc, sections_t) {
|
||||
sections_in_str(sc, f);
|
||||
array_symbol_t *a = sections_get(sc, STRING_CTE(".text"));
|
||||
if (a == NULL) {
|
||||
printf("There is no .text section.");
|
||||
} else {
|
||||
printf("Section .text is :");
|
||||
array_symbol_out_str(stdout, *a);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int main(void) { return 0; }
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - ALGO module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -44,7 +44,7 @@
|
||||
ALGOI_FOR_EACH_ARG(container, M_GLOBAL_OPLIST(cont_oplist), __VA_ARGS__ ))
|
||||
|
||||
|
||||
/* Extract a subset of a container to fill in another container.
|
||||
/* Extract a subset of a container to copy into another container.
|
||||
USAGE:
|
||||
ALGO_EXTRACT(contDst, contDstOplist, contSrc, contSrcOplist, function
|
||||
[, extra arguments of function]) */
|
||||
@@ -105,6 +105,8 @@
|
||||
|
||||
#define ALGOI_DEF_P2(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, type_oplist)( \
|
||||
\
|
||||
/* It supposes that the container is not sorted */ \
|
||||
static inline void \
|
||||
M_C(name, _find_again) (it_t it, type_t const data) \
|
||||
@@ -117,20 +119,90 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _find) (it_t it, container_t l, type_t const data) \
|
||||
M_C(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); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _contain_p) (container_t l, type_t const data) \
|
||||
M_C(name, _contain_p) (container_t const l, type_t const data) \
|
||||
{ \
|
||||
it_t it; \
|
||||
M_C(name,_find)(it, l, data); \
|
||||
return !M_CALL_IT_END_P(cont_oplist, it); \
|
||||
} \
|
||||
\
|
||||
/* For the definition of _find_last, if the methods \
|
||||
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) \
|
||||
{ \
|
||||
for (M_CALL_IT_LAST(cont_oplist, it, l); \
|
||||
!M_CALL_IT_END_P(cont_oplist, it) ; \
|
||||
M_CALL_IT_PREVIOUS(cont_oplist, it)) { \
|
||||
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
|
||||
/* We can stop as soon as a match is found */ \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
, \
|
||||
/* Otherwise search forward, but don't stop on the first occurence */ \
|
||||
static inline void \
|
||||
M_C(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; \
|
||||
for (M_CALL_IT_FIRST(cont_oplist, it2, l); \
|
||||
!M_CALL_IT_END_P(cont_oplist, it2) ; \
|
||||
M_CALL_IT_NEXT(cont_oplist, it2)) { \
|
||||
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \
|
||||
/* We cannot stop as soon as a match is found */ \
|
||||
M_CALL_IT_SET(cont_oplist, it, it2) ; \
|
||||
} \
|
||||
} \
|
||||
) /* End of alternative of _find_last */ \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _count) (container_t const l, type_t const data) \
|
||||
{ \
|
||||
it_t it; \
|
||||
size_t count = 0; \
|
||||
for (M_CALL_IT_FIRST(cont_oplist, it, l); \
|
||||
!M_CALL_IT_END_P(cont_oplist, it) ; \
|
||||
M_CALL_IT_NEXT(cont_oplist, it)) { \
|
||||
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
|
||||
count++ ; \
|
||||
} \
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
static inline void \
|
||||
M_C(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); \
|
||||
M_CALL_IT_NEXT(cont_oplist, it1), \
|
||||
M_CALL_IT_NEXT(cont_oplist, it2)) { \
|
||||
if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \
|
||||
*M_CALL_IT_CREF(cont_oplist, it2))) \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(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); \
|
||||
} \
|
||||
\
|
||||
, /* NO EQUAL */) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _find_again_if) (it_t it, bool (*func)(type_t const)) \
|
||||
{ \
|
||||
@@ -148,54 +220,9 @@
|
||||
M_C(name, _find_again_if)(it, func); \
|
||||
} \
|
||||
\
|
||||
/* For the definition of _find_last, if the methods \
|
||||
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 l, type_t const data) \
|
||||
{ \
|
||||
for (M_CALL_IT_LAST(cont_oplist, it, l); \
|
||||
!M_CALL_IT_END_P(cont_oplist, it) ; \
|
||||
M_CALL_IT_PREVIOUS(cont_oplist, it)) { \
|
||||
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
|
||||
/* We can stop as soon as a match is found */ \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
, \
|
||||
/* Otherwise search forward, but don't stop on the first occurence */ \
|
||||
static inline void \
|
||||
M_C(name, _find_last) (it_t it, container_t l, type_t const data) \
|
||||
{ \
|
||||
M_CALL_IT_END(cont_oplist, it, l); \
|
||||
it_t it2; \
|
||||
for (M_CALL_IT_FIRST(cont_oplist, it2, l); \
|
||||
!M_CALL_IT_END_P(cont_oplist, it2) ; \
|
||||
M_CALL_IT_NEXT(cont_oplist, it2)) { \
|
||||
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it2), data)) \
|
||||
/* We cannot stop as soon as a match is found */ \
|
||||
M_CALL_IT_SET(cont_oplist, it, it2) ; \
|
||||
} \
|
||||
} \
|
||||
) /* End of alternative of _find_last */ \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _count) (container_t l, type_t const data) \
|
||||
{ \
|
||||
it_t it; \
|
||||
size_t count = 0; \
|
||||
for (M_CALL_IT_FIRST(cont_oplist, it, l); \
|
||||
!M_CALL_IT_END_P(cont_oplist, it) ; \
|
||||
M_CALL_IT_NEXT(cont_oplist, it)) { \
|
||||
if (M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it), data)) \
|
||||
count++ ; \
|
||||
} \
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _count_if) (container_t l, bool (*func)(type_t const data)) \
|
||||
M_C(name, _count_if) (container_t const l, bool (*func)(type_t const data)) \
|
||||
{ \
|
||||
it_t it; \
|
||||
size_t count = 0; \
|
||||
@@ -209,27 +236,6 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(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); \
|
||||
M_CALL_IT_NEXT(cont_oplist, it1), \
|
||||
M_CALL_IT_NEXT(cont_oplist, it2)) { \
|
||||
if (!M_CALL_EQUAL(type_oplist, *M_CALL_IT_CREF(cont_oplist, it1), \
|
||||
*M_CALL_IT_CREF(cont_oplist, it2))) \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _mismatch) (it_t it1, it_t it2, container_t l1, container_t l2 ) \
|
||||
{ \
|
||||
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
|
||||
M_CALL_IT_FIRST(cont_oplist, it2, l2); \
|
||||
M_C(name, _mismatch_again)(it1, it2); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _mismatch_again_if) (it_t it1, it_t it2, bool (*func)(type_t const, type_t const) ) \
|
||||
{ \
|
||||
for (/*nothing */ ; !M_CALL_IT_END_P(cont_oplist, it1) && \
|
||||
@@ -243,7 +249,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _mismatch_if) (it_t it1, it_t it2, container_t l1, \
|
||||
M_C(name, _mismatch_if) (it_t it1, it_t it2, container_t const l1, \
|
||||
container_t l2, bool (*func)(type_t const, type_t const) ) \
|
||||
{ \
|
||||
M_CALL_IT_FIRST(cont_oplist, it1, l1); \
|
||||
@@ -260,7 +266,8 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _reduce) (type_t *dest, container_t l, void (*f)(type_t *, type_t const) ) \
|
||||
M_C(name, _reduce) (type_t *dest, container_t const l, \
|
||||
void (*f)(type_t *, type_t const) ) \
|
||||
{ \
|
||||
bool initDone = false; \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
@@ -273,9 +280,10 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, type_oplist)( \
|
||||
static inline \
|
||||
void M_C(name, _map_reduce) (type_t *dest, \
|
||||
container_t l, \
|
||||
const container_t l, \
|
||||
void (*redFunc)(type_t*, type_t const), \
|
||||
void (*mapFunc)(type_t*, type_t const) ) \
|
||||
{ \
|
||||
@@ -293,9 +301,10 @@
|
||||
} \
|
||||
M_CALL_CLEAR(type_oplist, tmp); \
|
||||
} \
|
||||
, ) \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _any_of_p) (container_t l, bool (*f)(type_t const) ) \
|
||||
M_C(name, _any_of_p) (container_t const l, bool (*f)(type_t const) ) \
|
||||
{ \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
if (f(*item)) \
|
||||
@@ -305,7 +314,7 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _all_of_p) (container_t l, bool (*f)(type_t const) ) \
|
||||
M_C(name, _all_of_p) (container_t const l, bool (*f)(type_t const) ) \
|
||||
{ \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
if (!f(*item)) \
|
||||
@@ -391,6 +400,36 @@
|
||||
M_CALL_IT_NEXT(cont_oplist, it2); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove_val)(container_t l, type_t const val) \
|
||||
{ \
|
||||
it_t it1; \
|
||||
M_CALL_IT_FIRST(cont_oplist, it1, l); \
|
||||
while (!M_CALL_IT_END_P(cont_oplist, it1)) { \
|
||||
type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \
|
||||
if (M_CALL_EQUAL(type_oplist, *ref1, val)) { \
|
||||
M_CALL_IT_REMOVE(cont_oplist, l, it1); \
|
||||
} else { \
|
||||
M_CALL_IT_NEXT(cont_oplist, it1); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove_if)(container_t l, bool (*func)(type_t const)) \
|
||||
{ \
|
||||
it_t it1; \
|
||||
M_CALL_IT_FIRST(cont_oplist, it1, l); \
|
||||
while (!M_CALL_IT_END_P(cont_oplist, it1)) { \
|
||||
type_t const *ref1 = M_CALL_IT_CREF(cont_oplist, it1); \
|
||||
if (func(*ref1)) { \
|
||||
M_CALL_IT_REMOVE(cont_oplist, l, it1); \
|
||||
} else { \
|
||||
M_CALL_IT_NEXT(cont_oplist, it1); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
, /* No IT_REMOVE method */) \
|
||||
\
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - dynamic ARRAY module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,27 +30,32 @@
|
||||
/* Define a dynamic array of the given type and its associated functions.
|
||||
USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define ARRAY_DEF(name, ...) \
|
||||
ARRAYI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
ARRAYI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
|
||||
/* Define the oplist of a dynamic array given its name and its oplist.
|
||||
If no oplist is given it is assumed to be M_DEFAULT_OPLIST
|
||||
USAGE: ARRAY_OPLIST(name[, oplist of the type]) */
|
||||
#define ARRAY_OPLIST(...) \
|
||||
ARRAYI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
ARRAYI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
|
||||
/********************************************************************************/
|
||||
/********************************** INTERNAL ************************************/
|
||||
/********************************************************************************/
|
||||
|
||||
// Deferred evaluation for the oplist definition.
|
||||
#define ARRAYI_OPLIST(arg) ARRAYI_OPLIST2 arg
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define ARRAYI_OPLIST_P1(arg) ARRAYI_OPLIST_P2 arg
|
||||
|
||||
/* OPLIST definition of a dynamic array */
|
||||
/* FIXME: Do we want to export some methods as they are slow and
|
||||
are not fit to be used for building other methods (like _remove)? */
|
||||
#define ARRAYI_OPLIST2(name, oplist) \
|
||||
are not fit to be used for building other methods (like _it_remove)? */
|
||||
#define ARRAYI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
@@ -85,6 +90,7 @@
|
||||
,GET_KEY(M_C(name, _get)) \
|
||||
,M_IF_METHOD(INIT, oplist)(GET_SET_KEY(M_C(name, _get_at)) ,) \
|
||||
,ERASE_KEY(M_C(name, _erase)) \
|
||||
,GET_SIZE(M_C(name, _size)) \
|
||||
,PUSH(M_C(name,_push_back)) \
|
||||
,POP(M_C(name,_pop_back)) \
|
||||
,PUSH_MOVE(M_C(name,_push_move)) \
|
||||
@@ -95,6 +101,8 @@
|
||||
,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),) \
|
||||
@@ -102,23 +110,30 @@
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
/* Define the contract of an array */
|
||||
/* Define the internal contract of an array */
|
||||
#define ARRAYI_CONTRACT(a) do { \
|
||||
assert (a != NULL); \
|
||||
assert (a->size <= a->alloc); \
|
||||
assert (a->size == 0 || a->ptr != NULL); \
|
||||
} while (0)
|
||||
|
||||
// Deferred evaluation for the array definition.
|
||||
#define ARRAYI_DEF(arg) ARRAYI_DEF2 arg
|
||||
/* Deferred evaluation for the array definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define ARRAYI_DEF_P1(arg) ARRAYI_DEF_P2 arg
|
||||
|
||||
// Internal definition.
|
||||
#define ARRAYI_DEF2(name, type, oplist, array_t, array_it_t) \
|
||||
/* Internal definition:
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the array
|
||||
- array_t: alias for M_C(name, _t) [ type of the array ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the array ]
|
||||
*/
|
||||
#define ARRAYI_DEF_P2(name, type, oplist, array_t, it_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
size_t size; /* Number of elements in the array */ \
|
||||
size_t alloc; /* Allocated size for the array */ \
|
||||
type *ptr; /* Pointer to the array */ \
|
||||
size_t alloc; /* Allocated size for the array base */ \
|
||||
type *ptr; /* Pointer to the array base */ \
|
||||
} array_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
@@ -126,9 +141,9 @@
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
size_t index; \
|
||||
const struct M_C(name, _s) *array; \
|
||||
} array_it_t[1]; \
|
||||
size_t index; /* Index of the element */ \
|
||||
const struct M_C(name, _s) *array; /* Reference of the array */ \
|
||||
} it_t[1]; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(array_t v) \
|
||||
@@ -218,7 +233,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _set_at)(array_t v, size_t i, type x) \
|
||||
M_C(name, _set_at)(array_t v, size_t i, type const x) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert(v->size > 0 && v->ptr != NULL && i < v->size); \
|
||||
@@ -434,7 +449,7 @@
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop_until)(array_t v, array_it_t pos) \
|
||||
M_C(name, _pop_until)(array_t v, it_t pos) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (v == pos->array); \
|
||||
@@ -495,10 +510,17 @@
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert(i <= v->size); \
|
||||
size_t size = v->size + num; \
|
||||
/* Avoid overflow of v->size + num */ \
|
||||
if (v->size > v->alloc-num) { \
|
||||
/* Test for overflow of variable size */ \
|
||||
if (M_UNLIKELY (size <= v->size)) { \
|
||||
/* Unlikely case of nothing to do */ \
|
||||
if (num == 0) return; \
|
||||
M_MEMORY_FULL(sizeof (type) * v->size); \
|
||||
return ; \
|
||||
} \
|
||||
/* Test if alloc array is sufficient */ \
|
||||
if (size > v->alloc) { \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
||||
if (M_UNLIKELY (size <= v->size || alloc <= v->alloc)) { \
|
||||
if (M_UNLIKELY (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
} \
|
||||
@@ -554,16 +576,9 @@
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert(i < v->size && j < v->size && v->ptr != NULL); \
|
||||
type tmp; \
|
||||
M_IF_METHOD(INIT_MOVE, oplist) ( \
|
||||
M_CALL_INIT_MOVE(oplist, tmp, v->ptr[i]); \
|
||||
M_CALL_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_CALL_INIT_MOVE(oplist, v->ptr[j], tmp); \
|
||||
, \
|
||||
M_CALL_INIT_SET(oplist, tmp, v->ptr[i]); \
|
||||
M_CALL_SET(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_CALL_SET(oplist, v->ptr[j], tmp); \
|
||||
M_CALL_CLEAR(oplist, tmp); \
|
||||
) /* IF INIT_MOVE method */ \
|
||||
M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]); \
|
||||
M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_DO_INIT_MOVE(oplist, v->ptr[j], tmp); \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
@@ -592,7 +607,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(array_it_t it, const array_t v) \
|
||||
M_C(name, _it)(it_t it, const array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (it != NULL); \
|
||||
@@ -601,7 +616,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_last)(array_it_t it, const array_t v) \
|
||||
M_C(name, _it_last)(it_t it, const array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (it != NULL); \
|
||||
@@ -610,7 +625,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(array_it_t it, const array_t v) \
|
||||
M_C(name, _it_end)(it_t it, const array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (it != NULL); \
|
||||
@@ -619,7 +634,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(array_it_t it, const array_it_t org) \
|
||||
M_C(name, _it_set)(it_t it, const it_t org) \
|
||||
{ \
|
||||
assert (it != NULL && org != NULL); \
|
||||
it->index = org->index; \
|
||||
@@ -628,14 +643,14 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const array_it_t it) \
|
||||
M_C(name, _end_p)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->array != NULL); \
|
||||
return it->index >= it->array->size; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _last_p)(const array_it_t it) \
|
||||
M_C(name, _last_p)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->array != NULL); \
|
||||
/* NOTE: Can not compute 'size-1' due to potential overflow \
|
||||
@@ -644,22 +659,22 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_equal_p)(const array_it_t it1, \
|
||||
const array_it_t it2) \
|
||||
M_C(name, _it_equal_p)(const it_t it1, \
|
||||
const it_t it2) \
|
||||
{ \
|
||||
assert(it1 != NULL && it2 != NULL); \
|
||||
return it1->array == it2->array && it1->index == it2->index; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _next)(array_it_t it) \
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->array != NULL); \
|
||||
it->index ++; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _previous)(array_it_t it) \
|
||||
M_C(name, _previous)(it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->array != NULL); \
|
||||
/* NOTE: In the case index=0, it will be set to (unsigned) -1 \
|
||||
@@ -668,28 +683,28 @@
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _ref)(const array_it_t it) \
|
||||
M_C(name, _ref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL); \
|
||||
return M_C(name, _get)(it->array, it->index); \
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const array_it_t it) \
|
||||
M_C(name, _cref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL); \
|
||||
return M_C(name, _cget)(it->array, it->index); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _insert)(array_t a, array_it_t it, type const x) \
|
||||
M_C(name, _insert)(array_t a, it_t it, type const x) \
|
||||
{ \
|
||||
assert (it != NULL && a == it->array); \
|
||||
M_C(name, _push_at)(a, it->index + 1, x); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove)(array_t a, array_it_t it) \
|
||||
M_C(name, _remove)(array_t a, it_t it) \
|
||||
{ \
|
||||
assert (it != NULL && a == it->array); \
|
||||
M_C(name, _pop_at)(NULL, a, it->index); \
|
||||
@@ -799,6 +814,7 @@
|
||||
\
|
||||
,) /* IF CMP oplist */ \
|
||||
\
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, array_t const array, \
|
||||
@@ -807,7 +823,7 @@
|
||||
STRINGI_CONTRACT(str); \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
(append ? string_cat_str : string_set_str) (str, "["); \
|
||||
array_it_t it; \
|
||||
it_t it; \
|
||||
for (M_C(name, _it)(it, array) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
@@ -896,6 +912,52 @@
|
||||
} \
|
||||
, /* no IN_STR & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, const array_t array) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
ret = f->m_interface->write_array_start(local, f, array->size); \
|
||||
for (size_t i = 0; i < array->size; i++) { \
|
||||
type const *item = M_C(name, _cget)(array, i); \
|
||||
if (i != 0) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(array_t array, m_serial_read_t f) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
size_t estimated_size = 0; \
|
||||
M_C(name,_clean)(array); \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
if (estimated_size != 0) M_C(name, _reserve)(array, estimated_size); \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_C(name, _push_back)(array, item); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no IN_SERIAL & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _equal_p)(const array_t array1, \
|
||||
|
||||
+18
-6
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Thin stdatomic wrapper for C++ compatibility
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -98,7 +98,7 @@ using std::memory_order_seq_cst;
|
||||
|
||||
#define _Atomic(T) std::atomic< T >
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|
||||
|| ( defined(__GNUC__) && !defined(__clang__) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|
||||
|| (defined(__clang__) && __clang_major__ >= 4)
|
||||
|
||||
@@ -106,6 +106,18 @@ using std::memory_order_seq_cst;
|
||||
and doesn't provide its own stdatomic.h */
|
||||
#include <stdatomic.h>
|
||||
|
||||
/* MSYS2 has a conflict between cdefs.h which defines a _Atomic macro (if not C11)
|
||||
not compatible with the used stdatomic.h (from GCC).
|
||||
Provide a configurable mechanism to undef it with auto-detection of msys2 / gcc */
|
||||
#ifndef M_USE_UNDEF_ATOMIC
|
||||
# if defined(__MSYS__) && defined(__GNUC__) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L)
|
||||
# define M_USE_UNDEF_ATOMIC 1
|
||||
# endif
|
||||
#endif
|
||||
#if defined(M_USE_UNDEF_ATOMIC) && M_USE_UNDEF_ATOMIC == 1
|
||||
# undef _Atomic
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/* Non working atomic.h, nor working stdatomic.h found.
|
||||
@@ -125,10 +137,10 @@ using std::memory_order_seq_cst;
|
||||
*/
|
||||
#define _Atomic(T) \
|
||||
struct { \
|
||||
volatile T _val; \
|
||||
T _zero; \
|
||||
T _previous; \
|
||||
m_mutex_t _lock; \
|
||||
T volatile _val; \
|
||||
T _zero; \
|
||||
T _previous; \
|
||||
m_mutex_t _lock; \
|
||||
}
|
||||
|
||||
/* Even if memory order is defined, only the strongest constraint is used */
|
||||
|
||||
+22
-18
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - BITSET module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,33 +30,21 @@
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// Define a limb of a bitset
|
||||
// Define the limb of a bitset
|
||||
typedef uint64_t bitset_limb;
|
||||
// And its size
|
||||
#define BITSET_LIMB_BIT (sizeof(bitset_limb) * CHAR_BIT)
|
||||
|
||||
typedef struct bitset_s {
|
||||
size_t size; // Size is the number of bits
|
||||
size_t alloc; // Alloc is the number of allocated limbs
|
||||
bitset_limb *ptr; // Pointer to the allocated limbs
|
||||
} bitset_t[1];
|
||||
typedef struct bitset_s *bitset_ptr;
|
||||
typedef const struct bitset_s *bitset_srcptr;
|
||||
|
||||
typedef struct bitset_it_s {
|
||||
size_t index; // index to the array of bit
|
||||
bool value; // value used for _ref & _cref to store the value
|
||||
struct bitset_s *set; // the associated bitset
|
||||
} bitset_it_t[1];
|
||||
|
||||
// bitset grow policy
|
||||
// bitset grow policy. n is limb size
|
||||
#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 4 ? 4 : (n) * 2)
|
||||
|
||||
// Compute the number of allocated limbs needed to handle 'n' bits.
|
||||
#define BITSETI_TO_ALLOC(n) (((n) + BITSET_LIMB_BIT - 1) / BITSET_LIMB_BIT)
|
||||
|
||||
// Compute the number of bits available from the allocated size
|
||||
// Compute the number of bits available from the allocated size in limbs
|
||||
#define BITSETI_FROM_ALLOC(n) ((n) * BITSET_LIMB_BIT)
|
||||
|
||||
// Contract of a bitset
|
||||
#define BITSETI_CONTRACT(t) do { \
|
||||
assert (t != NULL); \
|
||||
assert (t->size <= BITSETI_FROM_ALLOC (t->alloc)); \
|
||||
@@ -68,6 +56,21 @@ typedef struct bitset_it_s {
|
||||
|
||||
/********************************** EXTERNAL ************************************/
|
||||
|
||||
typedef struct bitset_s {
|
||||
size_t size; // Size is the number of bits
|
||||
size_t alloc; // Alloc is the number of allocated limbs
|
||||
bitset_limb *ptr; // Pointer to the allocated limbs
|
||||
} bitset_t[1];
|
||||
|
||||
typedef struct bitset_s *bitset_ptr;
|
||||
typedef const struct bitset_s *bitset_srcptr;
|
||||
|
||||
typedef struct bitset_it_s {
|
||||
size_t index; // index to the array of bit
|
||||
bool value; // value used for _ref & _cref to store the value
|
||||
struct bitset_s *set; // the associated bitset
|
||||
} bitset_it_t[1];
|
||||
|
||||
static inline void
|
||||
bitset_init(bitset_t t)
|
||||
{
|
||||
@@ -689,6 +692,7 @@ bitset_clz(const bitset_t set)
|
||||
,TYPE(bitset_t) \
|
||||
,SUBTYPE(bool) \
|
||||
,TEST_EMPTY(bitset_empty_p), \
|
||||
,GET_SIZE(bitset_size) \
|
||||
,IT_TYPE(bitset_it_t) \
|
||||
,IT_FIRST(bitset_it) \
|
||||
,IT_SET(bitset_it_set) \
|
||||
|
||||
+300
-129
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - B+TREE module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,7 +27,7 @@
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
/* Define a B+tree which maps a key to a value, of size N
|
||||
/* Define a B+tree of size 'N' that maps a 'key' to a 'value'
|
||||
with its associated functions.
|
||||
USAGE:
|
||||
BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
||||
@@ -36,8 +36,8 @@
|
||||
*/
|
||||
#define BPTREE_DEF2(name, N, key_type, ...) \
|
||||
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||
(name, N, key_type, __VA_ARGS__, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
|
||||
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||
(name, N, key_type, __VA_ARGS__, 1, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
|
||||
|
||||
|
||||
/* Define a B+tree of a given type, of size N.
|
||||
@@ -45,20 +45,45 @@
|
||||
USAGE: BPTREE_DEF(name, N, type, [, oplist_of_the_type]) */
|
||||
#define BPTREE_DEF(name, N, ...) \
|
||||
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||
(name, N, __VA_ARGS__, __VA_ARGS__, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
|
||||
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
|
||||
|
||||
|
||||
/* Define a B+tree of size 'N' that maps a 'key' to a 'value',
|
||||
allowing multiple equal keys to exist,
|
||||
with its associated functions.
|
||||
USAGE:
|
||||
BPTREE_MULTI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
||||
OR
|
||||
BPTREE_MULTI_DEF2(name, N, key_t, value_t)
|
||||
*/
|
||||
#define BPTREE_MULTI_DEF2(name, N, key_type, ...) \
|
||||
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||
(name, N, key_type, __VA_ARGS__, 1, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
|
||||
|
||||
|
||||
/* Define a B+tree of a given type, of size N.
|
||||
allowing multiple equal keys to exist,
|
||||
with its associated functions
|
||||
USAGE: BPTREE_MULTI_DEF(name, N, type, [, oplist_of_the_type]) */
|
||||
#define BPTREE_MULTI_DEF(name, N, ...) \
|
||||
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
|
||||
|
||||
|
||||
|
||||
/* Define the oplist of a rbtree of type (from BPTREE_DEF).
|
||||
USAGE: BPTREE_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define BPTREE_OPLIST(...) \
|
||||
BPTREEI_KEY_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
BPTREEI_KEY_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLISTR ), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
/* Define the oplist of a rbtree of type (from BPTREE_DEF2).
|
||||
USAGE: BPTREE_OPLIST2(name, key_oplist, value_oplist) */
|
||||
#define BPTREE_OPLIST2(name, key_oplist, value_oplist) \
|
||||
#define BPTREE_OPLIST2(name, key_oplist, value_oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
SET(M_C(name, _set)), \
|
||||
@@ -85,7 +110,6 @@
|
||||
VALUE_TYPE(M_C(name, _value_type_t)), \
|
||||
SET_KEY(M_C(name, _set_at)), \
|
||||
GET_KEY(M_C(name, _get)), \
|
||||
/*GET_SET_KEY(M_C(name, _get_at))*/, \
|
||||
ERASE_KEY(M_C(name, _erase)), \
|
||||
KEY_OPLIST(key_oplist), \
|
||||
VALUE_OPLIST(value_oplist), \
|
||||
@@ -93,6 +117,8 @@
|
||||
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_C(name, _parse_str)),), \
|
||||
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_C(name, _out_str)),), \
|
||||
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_C(name, _in_str)),), \
|
||||
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_C(name, _out_serial)),), \
|
||||
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_C(name, _in_serial)),), \
|
||||
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_C(name, _equal_p)),), \
|
||||
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_C(name, _hash)),) \
|
||||
,M_IF_METHOD(NEW, key_oplist)(NEW(M_GET_NEW oplist),) \
|
||||
@@ -104,10 +130,12 @@
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// deferred evaluation
|
||||
#define BPTREEI_KEY_OPLIST(arg) BPTREEI_KEY_OPLIST2 arg
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define BPTREEI_KEY_OPLIST_P1(arg) BPTREEI_KEY_OPLIST_P2 arg
|
||||
|
||||
#define BPTREEI_KEY_OPLIST2(name, oplist) \
|
||||
/* OPLIST definition of a b+tree */
|
||||
#define BPTREEI_KEY_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
SET(M_C(name, _set)), \
|
||||
@@ -134,6 +162,8 @@
|
||||
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),) \
|
||||
@@ -141,10 +171,11 @@
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
/* Internal contract of a B+TREE node */
|
||||
#ifdef NDEBUG
|
||||
# define BPTREEI_NODE_CONTRACT(N, key_oplist, node, root) do { } while (0)
|
||||
# define BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { } while (0)
|
||||
#else
|
||||
# define BPTREEI_NODE_CONTRACT(N, key_oplist, node, root) do { \
|
||||
# define BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { \
|
||||
assert ((node) != NULL); \
|
||||
assert ((root) != NULL); \
|
||||
int num2 = (node)->num; \
|
||||
@@ -159,18 +190,18 @@
|
||||
assert (c2 <= num2 && num2 <= N); \
|
||||
} \
|
||||
for(int i2 = 1; i2 < num2 ; i2++) { \
|
||||
assert (M_CALL_CMP(key_oplist, (node)->key[i2-1], (node)->key[i2]) < 0); \
|
||||
assert (M_CALL_CMP(key_oplist, (node)->key[i2-1], (node)->key[i2]) M_IF(isMulti)(<=, <) 0); \
|
||||
} \
|
||||
if ((node)->next != NULL) { \
|
||||
assert (num2 >= 1); \
|
||||
assert (M_CALL_CMP(key_oplist, (node)->key[num2-1], (node)->next->key[0]) < 0); \
|
||||
assert (M_CALL_CMP(key_oplist, (node)->key[num2-1], (node)->next->key[0]) M_IF(isMulti)(<=, <) 0); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define BPTREEI_CONTRACT(N, key_oplist, b) do { \
|
||||
#define BPTREEI_CONTRACT(N, isMulti, key_oplist, b) do { \
|
||||
assert (N >= 3); /* TBC: 2 instead ? */ \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, (b)->root, (b)->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, (b)->root, (b)->root); \
|
||||
assert ((b)->root->next == NULL); \
|
||||
if ((b)->root->num <= 0) assert (-(b)->root->num == (int) (b)->size); \
|
||||
} while (0)
|
||||
@@ -178,18 +209,36 @@
|
||||
/* Max depth of any B+tree */
|
||||
#define BPTREEI_MAX_STACK ((int)(CHAR_BIT*sizeof (size_t)))
|
||||
|
||||
// Deferred evaluation.
|
||||
/* Deferred evaluation for the b+tree definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define BPTREEI_DEF_P1(arg) BPTREEI_DEF_P2 arg
|
||||
|
||||
#define BPTREEI_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, tree_t, node_t, pit_t, it_t) \
|
||||
/* Internal b+tree definition
|
||||
- name: prefix to be used
|
||||
- N: size of the node
|
||||
- key_t: key type of the elements of the container
|
||||
- key_oplist: oplist of the key type of the elements of the container
|
||||
- value_t: value type of the elements of the container
|
||||
- value_oplist: oplist of the value type of the elements of the container
|
||||
- isMap: true if map, false if set
|
||||
- isMulti: true if multimap/multiset, false otherwise
|
||||
- tree_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
- pit_t: alias for M_C(name, _pit_t) [parent iterator]
|
||||
*/
|
||||
#define BPTREEI_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t) \
|
||||
\
|
||||
M_IF(isMap)( \
|
||||
typedef struct M_C(name, _pair_s) { \
|
||||
key_t *key_ptr; \
|
||||
value_t *value_ptr; \
|
||||
} M_C(name, _type_t); \
|
||||
, \
|
||||
typedef key_t M_C(name, _type_t); \
|
||||
/* Type returned by the iterator. Due to having key and value \
|
||||
separated in their own array in the node, it is pointers to \
|
||||
the objects, not a global pointer to both objects. */ \
|
||||
typedef struct M_C(name, _pair_s) { \
|
||||
key_t *key_ptr; \
|
||||
value_t *value_ptr; \
|
||||
} M_C(name, _type_t); \
|
||||
, \
|
||||
typedef key_t M_C(name, _type_t); \
|
||||
) \
|
||||
typedef key_t M_C(name, _key_type_t); \
|
||||
typedef value_t M_C(name, _value_type_t); \
|
||||
@@ -197,8 +246,8 @@
|
||||
/* Node of a B+TREE */ \
|
||||
typedef struct M_C(name, _node_s) { \
|
||||
int num; /* Abs=Number of keys. Sign <0 is leaf */ \
|
||||
key_t key[N+1]; /* We can temporary push one more key */ \
|
||||
struct M_C(name, _node_s) *next; /* next node */ \
|
||||
key_t key[N+1]; /* We can temporary push one more key */ \
|
||||
struct M_C(name, _node_s) *next; /* next node reference */ \
|
||||
union M_C(name, _kind_s) { /* either value or pointer to other nodes */ \
|
||||
M_IF(isMap)(value_t value[N+1];,) \
|
||||
struct M_C(name, _node_s) *node[N+2]; \
|
||||
@@ -226,7 +275,7 @@
|
||||
int idx; \
|
||||
} it_t[1]; \
|
||||
\
|
||||
/* Can be optimized later to alloc for leaf or for node */ \
|
||||
/* TODO: Can be optimized to alloc for leaf or for node */ \
|
||||
static inline node_t M_C(name, _new_node)(void) \
|
||||
{ \
|
||||
node_t n = M_CALL_NEW(key_oplist, struct M_C(name, _node_s)); \
|
||||
@@ -243,7 +292,7 @@
|
||||
{ \
|
||||
b->root = M_C(name, _new_node)(); \
|
||||
b->size = 0; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline bool M_C(name, _is_leaf)(node_t n) \
|
||||
@@ -262,7 +311,7 @@
|
||||
\
|
||||
static inline void M_C(name, _clean)(tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
node_t next, n = b->root; \
|
||||
pit_t pit; \
|
||||
int np = 0; \
|
||||
@@ -298,12 +347,12 @@
|
||||
/* Clean root */ \
|
||||
b->root->num = 0; \
|
||||
b->size = 0; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _clear)(tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
M_C(name, _clean)(b); \
|
||||
/* Once the tree is clean, only the root remains */ \
|
||||
M_CALL_DEL(key_oplist, b->root); \
|
||||
@@ -342,17 +391,17 @@
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, (o==root) ? n : root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, (o==root) ? n : root); \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _init_set)(tree_t b, const tree_t o) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, o); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, o); \
|
||||
assert (b != NULL); \
|
||||
b->root = M_C(name, _copy_node)(o->root, o->root); \
|
||||
b->size = o->size; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _set)(tree_t b, const tree_t o) \
|
||||
@@ -363,22 +412,22 @@
|
||||
\
|
||||
static inline bool M_C(name, _empty_p)(const tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
/* root shall be an empty leaf */ \
|
||||
return b->size == 0; \
|
||||
} \
|
||||
\
|
||||
static inline size_t M_C(name, _size)(const tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
return b->size; \
|
||||
} \
|
||||
\
|
||||
static inline node_t M_C(name, _search_leaf)(pit_t pit, const tree_t b, key_t const key) \
|
||||
static inline node_t M_C(name, _search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
|
||||
{ \
|
||||
node_t n = b->root; \
|
||||
int np = 0; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
||||
while (!M_C(name, _is_leaf)(n)) { \
|
||||
assert (np <= BPTREEI_MAX_STACK); \
|
||||
int i, hi = n->num; \
|
||||
@@ -388,25 +437,25 @@
|
||||
the choosen type and size , it simply means that the \
|
||||
size of B+TREE is too big (TBC) and shall be reduced. */ \
|
||||
for(i = 0; i < hi; i++) { \
|
||||
if (M_CALL_CMP(key_oplist, key, n->key[i]) < 0) \
|
||||
if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \
|
||||
break; \
|
||||
} \
|
||||
pit->parent[np++] = n; \
|
||||
n = n->kind.node[i]; \
|
||||
assert (n != NULL); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
||||
} \
|
||||
pit->num = np; \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
static inline value_t *M_C(name, _get)(tree_t b, key_t const key) \
|
||||
static inline value_t *M_C(name, _get)(const tree_t b, key_t const key) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
pit_t pit; \
|
||||
node_t n = M_C(name, _search_leaf)(pit, b, key); \
|
||||
node_t n = M_C(name, _search_for_leaf)(pit, b, key); \
|
||||
int cmp = 0; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
||||
for(int i = 0; cmp >= 0 && i < -n->num; i++) { \
|
||||
cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||
if (cmp == 0) \
|
||||
@@ -415,12 +464,14 @@
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
static inline const value_t *M_C(name, _cget)(tree_t b, key_t const key) \
|
||||
static inline const value_t *M_C(name, _cget)(const tree_t b, key_t const key) \
|
||||
{ \
|
||||
return M_CONST_CAST(value_t, M_C(name, _get)(b, key)); \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _search_and_insert_leaf)(node_t n, key_t const key M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
|
||||
static inline int \
|
||||
M_C(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
|
||||
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
|
||||
{ \
|
||||
assert (M_C(name, _is_leaf)(n)); \
|
||||
int i, num = M_C(name, _get_num)(n); \
|
||||
@@ -428,10 +479,13 @@
|
||||
for(i = 0; i < num; i++) { \
|
||||
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||
if (cmp <= 0) { \
|
||||
if (M_UNLIKELY (cmp == 0)) { \
|
||||
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
|
||||
M_IF(isMulti)( /* Nothing to do : fallthrough */, \
|
||||
/* Update value if keys are equal */ \
|
||||
if (M_UNLIKELY (cmp == 0)) { \
|
||||
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
|
||||
return -1; \
|
||||
} \
|
||||
} \
|
||||
) \
|
||||
/* Move tables to make space for insertion */ \
|
||||
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(num-i)); \
|
||||
M_IF(isMap)(memmove(&n->kind.value[i+1], &n->kind.value[i], sizeof(value_t)*(num-i));,) \
|
||||
@@ -444,48 +498,55 @@
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _search_and_insert_node)(node_t n, key_t key) \
|
||||
static inline int \
|
||||
M_C(name, _search_and_insert_in_node)(node_t n, node_t l, key_t key) \
|
||||
{ \
|
||||
assert (!M_C(name, _is_leaf)(n)); \
|
||||
int i, num = M_C(name, _get_num)(n); \
|
||||
assert (num <= N); \
|
||||
for(i = 0; i < num; i++) { \
|
||||
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||
assert (cmp != 0); \
|
||||
if (cmp < 0) { \
|
||||
if (n->kind.node[i] == l) { \
|
||||
/* Move tables to make space for insertion */ \
|
||||
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(num-i)); \
|
||||
memmove(&n->kind.node[i+1], &n->kind.node[i], sizeof(node_t)*(num-i+1)); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
||||
M_CALL_INIT_SET(key_oplist, n->key[i], key); \
|
||||
n->num += 1; \
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
static inline void M_IF(isMap)(M_C(name, _set_at),M_C(name,_push))(tree_t b, key_t const key M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
|
||||
static inline void \
|
||||
M_IF(isMap)(M_C(name, _set_at),M_C(name,_push))(tree_t b, key_t const key \
|
||||
M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
|
||||
{ \
|
||||
pit_t pit; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
node_t leaf = M_C(name, _search_leaf)(pit, b, key); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
node_t leaf = M_C(name, _search_for_leaf)(pit, b, key); \
|
||||
/* Insert key into the leaf.*/ \
|
||||
/* NOTE: Even if there is N elements, we can still add one more.*/ \
|
||||
int i = M_C(name, _search_and_insert_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
|
||||
int i = M_C(name, _search_and_insert_in_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
|
||||
if (i < 0) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
/* Nothing to do anymore. key already exists in the tree. \
|
||||
value has been updated if needed */ \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
return; \
|
||||
} \
|
||||
b->size ++; \
|
||||
/* Most likely case: leaf can accept key!*/ \
|
||||
/* Most likely case: leaf can accept key */ \
|
||||
int num = -leaf->num; \
|
||||
assert (num > 0); \
|
||||
if (M_LIKELY (num <= N)) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
/* nothing more to do */ \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
return; \
|
||||
} \
|
||||
assert (num == N+1); \
|
||||
/* leaf is full: need to slip it in two */ \
|
||||
\
|
||||
/* Needs to rebalance the B+TREE */ \
|
||||
/* leaf is full: need to slip the leaf in two */ \
|
||||
int nnum = (N + 1) / 2; \
|
||||
num = N + 1 - nnum; \
|
||||
node_t nleaf = M_C(name, _new_node)(); \
|
||||
@@ -496,39 +557,41 @@
|
||||
nleaf->num = -nnum; \
|
||||
nleaf->next = leaf->next; \
|
||||
leaf->next = nleaf; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, leaf, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, nleaf, b->root); \
|
||||
/* Update parent to inject *key_ptr which split between (leaf, nleaf) */ \
|
||||
key_t *key_ptr = &nleaf->key[0]; \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, leaf, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, nleaf, b->root); \
|
||||
/* Update parent to inject *key_ptr that splits between (leaf, nleaf) */ \
|
||||
key_t *key_ptr = &leaf->key[num-1]; \
|
||||
while (true) { \
|
||||
if (pit->num == 0) { \
|
||||
/* We reach root. Need to increase the height of the tree.*/ \
|
||||
/* We reach root ==> Need to increase the height of the tree.*/ \
|
||||
node_t parent = M_C(name, _new_node)(); \
|
||||
parent->num = 1; \
|
||||
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
||||
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
|
||||
parent->kind.node[0] = leaf; \
|
||||
parent->kind.node[1] = nleaf; \
|
||||
b->root = parent; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
return; \
|
||||
} \
|
||||
/* Non root node. Get parent */ \
|
||||
/* Non root node. Get the parent node */ \
|
||||
node_t parent = pit->parent[--pit->num]; \
|
||||
/* Insert into parent (It is big enough to receive temporary one more) */ \
|
||||
i = M_C(name, _search_and_insert_node)(parent, *key_ptr); \
|
||||
i = M_C(name, _search_and_insert_in_node)(parent, leaf, *key_ptr); \
|
||||
parent->kind.node[i] = leaf; \
|
||||
parent->kind.node[i+1] = nleaf; \
|
||||
/* Test if parent node is full? */ \
|
||||
if (M_LIKELY (parent->num <= N)) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
return; /* No need to split parent.*/ \
|
||||
} \
|
||||
assert (parent->num == N+1); \
|
||||
/* Need to split parent in {np} {1} {nnp} */ \
|
||||
/* Need to split parent in {np} {med} {nnp} */ \
|
||||
int nnp = N / 2; \
|
||||
int np = N - nnp; \
|
||||
assert (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
|
||||
node_t nparent = M_C(name, _new_node)(); \
|
||||
/* Move half items to new node (Like a classic B-TREEE) \
|
||||
/* Move half items to new node (Like a classic B-TREE) \
|
||||
and the median key to the grand-parent*/ \
|
||||
memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*nnp); \
|
||||
memmove(&nparent->kind.node[0], &parent->kind.node[np+1], sizeof(node_t)*(nnp+1)); \
|
||||
@@ -536,8 +599,8 @@
|
||||
nparent->num = nnp; \
|
||||
nparent->next = parent->next; \
|
||||
parent->next = nparent; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, parent, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, nparent, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, parent, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, nparent, b->root); \
|
||||
/* Prepare for the next step */ \
|
||||
key_ptr = &parent->key[np]; \
|
||||
leaf = parent; \
|
||||
@@ -545,13 +608,15 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _search_and_remove_leaf)(node_t n, key_t const key) \
|
||||
static inline int \
|
||||
M_C(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \
|
||||
{ \
|
||||
assert(M_C(name, _is_leaf)(n)); \
|
||||
int num = M_C(name, _get_num)(n); \
|
||||
const int num = M_C(name, _get_num)(n); \
|
||||
for(int i = 0; i < num; i++) { \
|
||||
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||
const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||
if (cmp == 0) { \
|
||||
/* found key ==> delete it */ \
|
||||
M_CALL_CLEAR(key_oplist, n->key[i]); \
|
||||
M_IF(isMap)(M_CALL_CLEAR(value_oplist, n->kind.value[i]);,) \
|
||||
memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(num-1-i)); \
|
||||
@@ -574,6 +639,7 @@
|
||||
int num_right = M_C(name, _get_num)(right); \
|
||||
assert (num_left > N/2); \
|
||||
assert (num_right < N/2); \
|
||||
\
|
||||
/* Move one item from the left node to the right node */ \
|
||||
memmove(&right->key[1], &right->key[0], sizeof(key_t)*num_right); \
|
||||
if (M_C(name, _is_leaf)(left)) { \
|
||||
@@ -582,10 +648,10 @@
|
||||
M_IF(isMap)(memmove (&right->kind.value[0], &left->kind.value[num_left-1], sizeof (value_t));,) \
|
||||
right->num = -num_right - 1; \
|
||||
left->num = -num_left + 1; \
|
||||
M_CALL_SET(key_oplist, parent->key[k], right->key[0]); \
|
||||
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left-2]); \
|
||||
} else { \
|
||||
memmove(&right->kind.node[1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
|
||||
/* parent[k] is move to right[0] (clear). parent[k] is therefore clear */ \
|
||||
/* parent[k] is moved to right[0] (clear). parent[k] is therefore clear */ \
|
||||
memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \
|
||||
right->kind.node[0] = left->kind.node[num_left]; \
|
||||
right->num = num_right + 1; \
|
||||
@@ -608,7 +674,8 @@
|
||||
int num_right = M_C(name, _get_num)(right); \
|
||||
assert (num_left < N/2); \
|
||||
assert (num_right > N/2); \
|
||||
/* Move one item from right to left. */ \
|
||||
\
|
||||
/* Move one item from the right node to the left node. */ \
|
||||
if (M_C(name, _is_leaf)(right)) { \
|
||||
memmove (&left->key[num_left], &right->key[0], sizeof(key_t)); \
|
||||
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(num_right-1)); \
|
||||
@@ -616,7 +683,7 @@
|
||||
M_IF(isMap)(memmove (&right->kind.value[0], &right->kind.value[1], sizeof(value_t)*(num_right-1));,) \
|
||||
right->num = -num_right + 1; \
|
||||
left->num = -num_left - 1; \
|
||||
M_CALL_SET(key_oplist, parent->key[k], right->key[0]); \
|
||||
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left]); \
|
||||
} else { \
|
||||
memmove (&left->key[num_left], &parent->key[k], sizeof (key_t)); \
|
||||
memmove (&parent->key[k], &right->key[0], sizeof (key_t)); \
|
||||
@@ -641,13 +708,14 @@
|
||||
int num_left = M_C(name, _get_num)(left); \
|
||||
int num_right = M_C(name, _get_num)(right); \
|
||||
\
|
||||
/* Merge node 'k' and 'k+1' into a single one */ \
|
||||
if (leaf) { \
|
||||
assert (num_left + num_right <= N); \
|
||||
memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*num_right); \
|
||||
M_IF(isMap)(memmove(&left->kind.value[num_left], &right->kind.value[0], sizeof(value_t)*num_right);,) \
|
||||
left->num = -num_left - num_right; \
|
||||
} else { \
|
||||
assert (num_left + num_right + 1 <= N); \
|
||||
assert (num_left + num_right <= N -1); \
|
||||
memmove(&left->key[num_left+1], &right->key[0], sizeof(key_t)*num_right); \
|
||||
memmove(&left->kind.node[num_left+1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
|
||||
M_CALL_INIT_SET(key_oplist, left->key[num_left], parent->key[k]); \
|
||||
@@ -664,10 +732,11 @@
|
||||
\
|
||||
/* We can also cache the index when we descend the tree. \
|
||||
TODO: Bench if this is worth the effort.*/ \
|
||||
static inline int M_C(name, _search_node)(node_t parent, node_t child) \
|
||||
static inline int \
|
||||
M_C(name, _search_for_node)(node_t parent, node_t child) \
|
||||
{ \
|
||||
assert (!M_C(name, _is_leaf)(parent)); \
|
||||
int num = M_C(name, _get_num)(parent); \
|
||||
const int num = M_C(name, _get_num)(parent); \
|
||||
for(int i = 0; i <= num; i++) { \
|
||||
if (parent->kind.node[i] == child) \
|
||||
return i; \
|
||||
@@ -678,14 +747,17 @@
|
||||
\
|
||||
static inline bool M_C(name, _erase)(tree_t b, key_t const key) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
pit_t pit; \
|
||||
node_t leaf = M_C(name, _search_leaf)(pit, b, key); \
|
||||
int k = M_C(name, _search_and_remove_leaf)(leaf, key); \
|
||||
/* If not found, or number of keys greater than N>2 or root */ \
|
||||
node_t leaf = M_C(name, _search_for_leaf)(pit, b, key); \
|
||||
int k = M_C(name, _search_and_remove_in_leaf)(leaf, key); \
|
||||
/* If key is not found ==> erase failed */ \
|
||||
if (k < 0) return false; \
|
||||
/* Remove one item from the B+TREE */ \
|
||||
b->size --; \
|
||||
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || pit->num == 0) return true; \
|
||||
/* If number of keys greater than N>2 or root ==> Nothing more to do */ \
|
||||
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || pit->num == 0) \
|
||||
return true; \
|
||||
/* Leaf is too small. Needs rebalancing */ \
|
||||
assert (M_C(name, _get_num)(leaf) == N/2-1); \
|
||||
bool pass1 = true; \
|
||||
@@ -694,9 +766,9 @@
|
||||
/* Search for node 'leaf' in parent */ \
|
||||
node_t parent = pit->parent[--pit->num]; \
|
||||
assert (parent != NULL); \
|
||||
k = M_C(name, _search_node)(parent, leaf); \
|
||||
k = M_C(name, _search_for_node)(parent, leaf); \
|
||||
/* Look for the neighboor of the removed key. */ \
|
||||
/* if we can still one key to keep our node balanced */ \
|
||||
/* if we can steal one key from them to keep our node balanced */ \
|
||||
if (k > 0 && M_C(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
|
||||
M_C(name, _left_shift)(parent, k-1); \
|
||||
return true; \
|
||||
@@ -711,8 +783,9 @@
|
||||
assert(k >= 0 && k < M_C(name, _get_num)(parent)); \
|
||||
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
|
||||
M_C(name, _merge_node)(parent, k, pass1); \
|
||||
/* Check if we need to backport */ \
|
||||
if (M_C(name, _get_num)(parent) >= N/2) return true; \
|
||||
/* Check if we need to continue */ \
|
||||
if (M_C(name, _get_num)(parent) >= N/2) \
|
||||
return true; \
|
||||
if (pit->num == 0) { \
|
||||
/* We reach the root */ \
|
||||
if (M_C(name, _get_num)(parent) == 0) { \
|
||||
@@ -728,12 +801,6 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline bool M_ATTR_DEPRECATED \
|
||||
M_C(name, _remove)(tree_t b, key_t const key) \
|
||||
{ \
|
||||
return M_C(name, _erase)(b, key); \
|
||||
} \
|
||||
\
|
||||
static inline bool M_C(name, _pop_at)(value_t *ptr, tree_t b, key_t const key) \
|
||||
{ \
|
||||
if (ptr != NULL) { \
|
||||
@@ -749,7 +816,7 @@
|
||||
static inline void \
|
||||
M_C(name, _it)(it_t it, const tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
assert (it != NULL); \
|
||||
node_t n = b->root; \
|
||||
/* Scan down the nodes */ \
|
||||
@@ -763,7 +830,7 @@
|
||||
static inline void \
|
||||
M_C(name, _it_end)(it_t it, const tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
assert (it != NULL); \
|
||||
node_t n = b->root; \
|
||||
/* Scan down the nodes */ \
|
||||
@@ -831,13 +898,13 @@
|
||||
static inline void \
|
||||
M_C(name, _it_from)(it_t it, const tree_t b, key_t const key) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
assert (it != NULL); \
|
||||
pit_t pit; \
|
||||
node_t n = M_C(name, _search_leaf)(pit, b, key); \
|
||||
node_t n = M_C(name, _search_for_leaf)(pit, b, key); \
|
||||
it->node = n; \
|
||||
int i; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
||||
for(i = 0; i < -n->num; i++) { \
|
||||
if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \
|
||||
break; \
|
||||
@@ -850,7 +917,7 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_to_p)(it_t it, key_t const key) \
|
||||
M_C(name, _it_until_p)(it_t it, key_t const key) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
node_t n = it->node; \
|
||||
@@ -859,10 +926,26 @@
|
||||
return (cmp >= 0); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_while_p)(it_t it, key_t const key) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
node_t n = it->node; \
|
||||
if (it->idx >= -n->num) return false; \
|
||||
int cmp = M_CALL_CMP(key_oplist, n->key[it->idx], key); \
|
||||
return (cmp <= 0); \
|
||||
} \
|
||||
\
|
||||
static inline bool M_ATTR_DEPRECATED \
|
||||
M_C(name, _it_to_p)(it_t it, key_t const key) \
|
||||
{ \
|
||||
return M_C(name, _it_until_p)(it, key); \
|
||||
} \
|
||||
\
|
||||
static inline value_t * \
|
||||
M_C(name, _min)(const tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
assert (b->size > 0); \
|
||||
node_t n = b->root; \
|
||||
/* Scan down the nodes */ \
|
||||
@@ -875,7 +958,7 @@
|
||||
static inline value_t * \
|
||||
M_C(name, _max)(const tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
node_t n = b->root; \
|
||||
/* Scan down the nodes */ \
|
||||
while (!M_C(name, _is_leaf)(n)) { \
|
||||
@@ -899,46 +982,47 @@
|
||||
static inline void \
|
||||
M_C(name, _init_move)(tree_t b, tree_t ref) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, ref); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, ref); \
|
||||
assert (b != NULL && b != ref); \
|
||||
b->size = ref->size; \
|
||||
b->root = ref->root; \
|
||||
ref->root = NULL; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _move)(tree_t b, tree_t ref) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, ref); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, ref); \
|
||||
assert (b != ref); \
|
||||
M_C(name,_clear)(b); \
|
||||
M_C(name,_init_move)(b, ref); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _swap)(tree_t tree1, tree_t tree2) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, tree1); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, tree2); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree2); \
|
||||
M_SWAP(size_t, tree1->size, tree2->size); \
|
||||
M_SWAP(node_t, tree1->root, tree2->root); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, tree1); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, tree2); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, tree2); \
|
||||
} \
|
||||
\
|
||||
BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, tree_t, node_t, pit_t, it_t)
|
||||
BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t)
|
||||
|
||||
|
||||
/* Define additional functions. Do not used any fields but the already defined methods */
|
||||
#define BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, tree_t, node_t, pit_t, it_t) \
|
||||
/* Define additional functions.
|
||||
Do not used any fields but the already defined methods */
|
||||
#define BPTREEI_FUNC_ADDITIONAL_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)( \
|
||||
static inline bool M_C(name,_equal_p)(const tree_t t1, const tree_t t2) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t2); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t2); \
|
||||
if (t1->size != t2->size) return false; \
|
||||
if (t1->size == 0) return true; \
|
||||
/* Slow comparaison */ \
|
||||
@@ -973,7 +1057,7 @@
|
||||
\
|
||||
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)( \
|
||||
static inline size_t M_C(name,_hash)(const tree_t t1) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
M_HASH_DECL(hash); \
|
||||
/* NOTE: We can't compute the hash directly for the same reason \
|
||||
than for EQUAL operator. */ \
|
||||
@@ -996,7 +1080,7 @@
|
||||
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
|
||||
static inline void M_C(name, _get_str)(string_t str, \
|
||||
const tree_t t1, bool append) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
assert(str != NULL); \
|
||||
(append ? string_cat_str : string_set_str) (str, "["); \
|
||||
/* NOTE: The print is really naive, and not really efficient */ \
|
||||
@@ -1025,7 +1109,7 @@
|
||||
static inline void \
|
||||
M_C(name, _out_str)(FILE *file, tree_t const t1) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
assert (file != NULL); \
|
||||
fputc ('[', file); \
|
||||
bool commaToPrint = false; \
|
||||
@@ -1053,7 +1137,7 @@
|
||||
static inline bool \
|
||||
M_C(name, _parse_str)(tree_t t1, const char str[], const char **endp) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
assert (str != NULL); \
|
||||
M_C(name,_clean)(t1); \
|
||||
bool success = false; \
|
||||
@@ -1095,7 +1179,7 @@
|
||||
static inline bool \
|
||||
M_C(name, _in_str)(tree_t t1, FILE *file) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, t1); \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
assert (file != NULL); \
|
||||
M_C(name,_clean)(t1); \
|
||||
int c = fgetc(file); \
|
||||
@@ -1128,6 +1212,93 @@
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no in_str */ ) \
|
||||
|
||||
\
|
||||
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
const M_C(name, _type_t) *item; \
|
||||
bool first_done = false; \
|
||||
it_t it; \
|
||||
/* Format is different between associative container \
|
||||
& set container */ \
|
||||
M_IF(isMap)( \
|
||||
ret = f->m_interface->write_map_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); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_map_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item->key_ptr); \
|
||||
ret |= f->m_interface->write_map_value(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(value_oplist, f, *item->value_ptr); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_map_end(local, f); \
|
||||
, \
|
||||
ret = f->m_interface->write_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); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
) \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(tree_t t1, m_serial_read_t f) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
key_t key; \
|
||||
M_C(name,_clean)(t1); \
|
||||
M_IF(isMap)( \
|
||||
value_t value; \
|
||||
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
M_CALL_INIT (value_oplist, value); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
||||
ret = f->m_interface->read_map_value(local, f); \
|
||||
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
|
||||
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
||||
M_C(name, _set_at)(t1, key, value); \
|
||||
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_CALL_CLEAR(value_oplist, value); \
|
||||
, \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_C(name, _push)(t1, key); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
) /* End of IF isMap */ \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no in_serial */ ) \
|
||||
\
|
||||
|
||||
#endif
|
||||
+147
-44
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Fixed size BUFFER & STACK interface
|
||||
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -29,12 +29,12 @@
|
||||
#include "m-mutex.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
/* Define the different kind of policy a buffer can have:
|
||||
/* Define the different kind of policy a lock-based buffer can have:
|
||||
* - the buffer can be either a queue (policy is FIFO) or a stack (policy is FILO),
|
||||
* - if the push is blocking (waiting for the buffer to has some space) or not,
|
||||
* - if the pop is blocking (waiting for the buffer to has some space) or not,
|
||||
* - if both push / pop are blocking,
|
||||
* - if it shall be thread safe or not (i.e. remove mutex and atomic costs),
|
||||
* - if the push method is by default blocking (waiting for the buffer to has some space) or not,
|
||||
* - if the pop method is by default blocking (waiting for the buffer to has some data) or not,
|
||||
* - if both methods are blocking,
|
||||
* - if it shall be thread safe or not (i.e. remove the mutex lock and atomic costs),
|
||||
* - if the buffer has to be init with empty elements, or if it shall init an element when it is pushed (and moved when popped),
|
||||
* - if the buffer has to overwrite the last element if the buffer is full,
|
||||
* - if the pop of an element is not complete until the call to pop_release (preventing push until this call).
|
||||
@@ -53,14 +53,14 @@ typedef enum {
|
||||
/* Define a lock based buffer.
|
||||
USAGE: BUFFER_DEF(name, type, size_of_buffer_or_0, policy[, oplist]) */
|
||||
#define BUFFER_DEF(name, type, m_size, ... ) \
|
||||
BUFFERI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
BUFFERI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, type, m_size,__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), M_C(name,_t)), \
|
||||
(name, type, m_size,__VA_ARGS__, M_C(name,_t))))
|
||||
|
||||
/* Define the oplist of a lock based buffer given its name and its oplist.
|
||||
USAGE: BUFFER_OPLIST(name[, oplist of the type]) */
|
||||
#define BUFFER_OPLIST(...) \
|
||||
BUFFERI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
BUFFERI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
@@ -69,7 +69,7 @@ typedef enum {
|
||||
Size of created queue can only a power of 2.
|
||||
*/
|
||||
#define QUEUE_MPMC_DEF(name, type, ...) \
|
||||
QUEUEI_MPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
QUEUEI_MPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), M_C(name,_t)), \
|
||||
(name, type, __VA_ARGS__, M_C(name,_t))))
|
||||
|
||||
@@ -78,7 +78,7 @@ typedef enum {
|
||||
Size of created queue can only a power of 2.
|
||||
*/
|
||||
#define QUEUE_SPSC_DEF(name, type, ...) \
|
||||
QUEUEI_SPSC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
QUEUEI_SPSC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), M_C(name,_t)), \
|
||||
(name, type, __VA_ARGS__, M_C(name,_t))))
|
||||
|
||||
@@ -102,9 +102,11 @@ typedef enum {
|
||||
assert (atomic_load(&buffer->number[0]) <= BUFFERI_SIZE(size)); \
|
||||
} while (0)
|
||||
|
||||
#define BUFFERI_DEF(arg) BUFFERI_DEF2 arg
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define BUFFERI_DEF_P1(arg) BUFFERI_DEF_P2 arg
|
||||
|
||||
#define BUFFERI_DEF2(name, type, m_size, policy, oplist, buffer_t) \
|
||||
#define BUFFERI_DEF_P2(name, type, m_size, policy, oplist, buffer_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
m_mutex_t mutexPush; /* MUTEX used for pushing elements */ \
|
||||
@@ -566,24 +568,6 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
} \
|
||||
|
||||
|
||||
// Deferred evaluation for the oplist definition.
|
||||
#define BUFFERI_OPLIST(arg) BUFFERI_OPLIST2 arg
|
||||
|
||||
/* OPLIST defininition of a buffer */
|
||||
#define BUFFERI_OPLIST2(name, oplist) \
|
||||
(INIT(M_C(name, _int_init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
,CLEAR(M_C(name, _clear)) \
|
||||
,TYPE(M_C(name,_t)) \
|
||||
,SUBTYPE(M_C(name, _type_t)) \
|
||||
,CLEAN(M_C(name,_clean)) \
|
||||
,PUSH(M_C(name,_push)) \
|
||||
,POP(M_C(name,_pop)) \
|
||||
,OPLIST(oplist) \
|
||||
)
|
||||
|
||||
|
||||
|
||||
/* Definition of a a QUEUE for Many Produccer / Many Consummer
|
||||
for high bandwidth scenario:
|
||||
@@ -595,7 +579,10 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
* size of queue is always a power of 2
|
||||
* no overwriting.
|
||||
*/
|
||||
#define QUEUEI_MPMC_DEF(arg) QUEUEI_MPMC_DEF2 arg
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define QUEUEI_MPMC_DEF_P1(arg) QUEUEI_MPMC_DEF_P2 arg
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define QUEUEI_MPMC_CONTRACT(v) /* nothing */
|
||||
@@ -611,7 +598,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define QUEUEI_MPMC_DEF2(name, type, policy, oplist, buffer_t) \
|
||||
#define QUEUEI_MPMC_DEF_P2(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, \
|
||||
@@ -624,22 +611,22 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
typedef struct M_C(name, _el_s) { \
|
||||
atomic_uint seq; /* Can only increase until overflow */ \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_uint)+sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_uint)-sizeof(type) : 1]; \
|
||||
M_CACHELINE_ALIGN(align, atomic_uint, type); \
|
||||
} M_C(name, _el_t); \
|
||||
\
|
||||
/* 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) { \
|
||||
atomic_uint ProdIdx; /* Can only increase until overflow */ \
|
||||
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_CACHELINE_ALIGN(align1, atomic_uint); \
|
||||
atomic_uint ConsoIdx; /* Can only increase until overflow */ \
|
||||
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_CACHELINE_ALIGN(align2, atomic_uint); \
|
||||
M_C(name, _el_t) *Tab; \
|
||||
unsigned int size; \
|
||||
} buffer_t[1]; \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _push)(buffer_t table, type x) \
|
||||
M_C(name, _push)(buffer_t table, type const x) \
|
||||
{ \
|
||||
QUEUEI_MPMC_CONTRACT(table); \
|
||||
unsigned int idx = atomic_load_explicit(&table->ProdIdx, \
|
||||
@@ -783,7 +770,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
|
||||
|
||||
|
||||
/* Definition of a a QUEUE for Single Produccer / Single Consummer
|
||||
/* Definition of a a QUEUE for Single Producer / Single Consummer
|
||||
for high bandwidth scenario:
|
||||
* wait-free,
|
||||
* quite fast
|
||||
@@ -792,7 +779,10 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
* size of queue is always a power of 2
|
||||
* no overwriting.
|
||||
*/
|
||||
#define QUEUEI_SPSC_DEF(arg) QUEUEI_SPSC_DEF2 arg
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define QUEUEI_SPSC_DEF_P1(arg) QUEUEI_SPSC_DEF_P2 arg
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define QUEUEI_SPSC_CONTRACT(table) do { } while (0)
|
||||
@@ -808,7 +798,7 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define QUEUEI_SPSC_DEF2(name, type, policy, oplist, buffer_t) \
|
||||
#define QUEUEI_SPSC_DEF_P2(name, type, policy, oplist, buffer_t) \
|
||||
\
|
||||
typedef struct M_C(name, _el_s) { \
|
||||
type x; \
|
||||
@@ -818,12 +808,12 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
atomic_uint consoIdx; /* Can only increase until overflow */ \
|
||||
unsigned int size; \
|
||||
M_C(name, _el_t) *Tab; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_uint) +sizeof(size_t) + sizeof(void*) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_uint) -sizeof(size_t)-sizeof(void*): 1]; \
|
||||
M_CACHELINE_ALIGN(align, atomic_uint, size_t, M_C(name, _el_t) *); \
|
||||
atomic_uint prodIdx; /* Can only increase until overflow */ \
|
||||
} buffer_t[1]; \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _push)(buffer_t table, type x) \
|
||||
M_C(name, _push)(buffer_t table, type const x) \
|
||||
{ \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
unsigned int r = atomic_load_explicit(&table->consoIdx, \
|
||||
@@ -844,6 +834,27 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _push_move)(buffer_t table, type x) \
|
||||
{ \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
unsigned int r = atomic_load_explicit(&table->consoIdx, \
|
||||
memory_order_relaxed); \
|
||||
unsigned int w = atomic_load_explicit(&table->prodIdx, \
|
||||
memory_order_acquire); \
|
||||
if (w-r >= table->size) \
|
||||
return false; \
|
||||
unsigned int i = w & (table->size -1); \
|
||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||
M_DO_MOVE(oplist, table->Tab[i].x, x); \
|
||||
} else { \
|
||||
M_DO_INIT_MOVE(oplist, table->Tab[i].x, x); \
|
||||
} \
|
||||
atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _pop)(type *ptr, buffer_t table) \
|
||||
{ \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
@@ -865,6 +876,81 @@ 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[]) \
|
||||
{ \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
assert (x != NULL); \
|
||||
assert (n <= table->size); \
|
||||
unsigned int r = atomic_load_explicit(&table->consoIdx, \
|
||||
memory_order_relaxed); \
|
||||
unsigned int w = atomic_load_explicit(&table->prodIdx, \
|
||||
memory_order_acquire); \
|
||||
unsigned int max = M_MIN(n, table->size - (w-r) ); \
|
||||
if (max == 0) \
|
||||
return 0; \
|
||||
for(unsigned int k = 0; k < max; k++) { \
|
||||
unsigned int i = (w+k) & (table->size -1); \
|
||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||
M_CALL_SET(oplist, table->Tab[i].x, x[k]); \
|
||||
} else { \
|
||||
M_CALL_INIT_SET(oplist, table->Tab[i].x, x[k]); \
|
||||
} \
|
||||
} \
|
||||
atomic_store_explicit(&table->prodIdx, w+max, memory_order_release); \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
return max; \
|
||||
} \
|
||||
\
|
||||
static inline unsigned \
|
||||
M_C(name, _pop_bulk)(unsigned int n, type ptr[], buffer_t table) \
|
||||
{ \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
assert (ptr != NULL); \
|
||||
assert (n <= table->size); \
|
||||
unsigned int w = atomic_load_explicit(&table->prodIdx, \
|
||||
memory_order_relaxed); \
|
||||
unsigned int r = atomic_load_explicit(&table->consoIdx, \
|
||||
memory_order_acquire); \
|
||||
if (w-r == 0) \
|
||||
return 0; \
|
||||
unsigned int max = M_MIN(w-r, n); \
|
||||
for(unsigned int k = 0; k < max; k++) { \
|
||||
unsigned int i = (r+k) & (table->size -1); \
|
||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||
M_CALL_SET(oplist, ptr[k], table->Tab[i].x); \
|
||||
} else { \
|
||||
M_DO_INIT_MOVE (oplist, ptr[k], table->Tab[i].x); \
|
||||
} \
|
||||
} \
|
||||
atomic_store_explicit(&table->consoIdx, r+max, memory_order_release); \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
return max; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _push_force)(buffer_t table, type const x) \
|
||||
{ \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
unsigned int r = atomic_load_explicit(&table->consoIdx, \
|
||||
memory_order_relaxed); \
|
||||
unsigned int w = atomic_load_explicit(&table->prodIdx, \
|
||||
memory_order_acquire); \
|
||||
/* If no place in queue, try to skip the last one */ \
|
||||
while (w-r >= table->size) { \
|
||||
bool b = atomic_compare_exchange_strong(&table->consoIdx, &r, r+1); \
|
||||
r += b; \
|
||||
} \
|
||||
unsigned int i = w & (table->size -1); \
|
||||
if (!BUFFERI_POLICY_P((policy), BUFFER_PUSH_INIT_POP_MOVE)) { \
|
||||
M_CALL_SET(oplist, table->Tab[i].x, x); \
|
||||
} else { \
|
||||
M_CALL_INIT_SET(oplist, table->Tab[i].x, x); \
|
||||
} \
|
||||
atomic_store_explicit(&table->prodIdx, w+1, memory_order_release); \
|
||||
QUEUEI_SPSC_CONTRACT(table); \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _size)(buffer_t table) \
|
||||
{ \
|
||||
@@ -952,8 +1038,25 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
||||
} \
|
||||
\
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define BUFFERI_OPLIST_P1(arg) BUFFERI_OPLIST_P2 arg
|
||||
|
||||
/* OPLIST defininition of a buffer */
|
||||
#define BUFFERI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _int_init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
,CLEAR(M_C(name, _clear)) \
|
||||
,TYPE(M_C(name,_t)) \
|
||||
,SUBTYPE(M_C(name, _type_t)) \
|
||||
,CLEAN(M_C(name,_clean)) \
|
||||
,PUSH(M_C(name,_push)) \
|
||||
,POP(M_C(name,_pop)) \
|
||||
,OPLIST(oplist) \
|
||||
,TEST_EMPTY(M_C(name, _empty_p)), \
|
||||
,GET_SIZE(M_C(name, _size)) \
|
||||
)
|
||||
|
||||
// NOTE: SET & INIT_SET are deliberatly missing. TBC if it the right way.
|
||||
// NOTE: There is no oplist defined for this container.
|
||||
|
||||
#endif
|
||||
+826
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
* M*LIB - Concurrent memory pool allocator
|
||||
*
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_CONCURRENT_MEMPOOL_H
|
||||
#define MSTARLIB_CONCURRENT_MEMPOOL_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
#include "m-genint.h"
|
||||
|
||||
/* Minimum number of nodes per group of nodes */
|
||||
#define C_MEMPOOL_MIN_NODE_PER_GROUP 16
|
||||
|
||||
#define C_MEMPOOL_DEF(name, type_t) \
|
||||
C_MEMPOOL_DEF_SINGLY_LIST(name, type_t) \
|
||||
C_MEMPOOL_DEF_LF_QUEUE(name, type_t) \
|
||||
C_MEMPOOL_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
|
||||
C_MEMPOOL_DEF_SYSTEM_ALLOC(name, type_t) \
|
||||
C_MEMPOOL_DEF_LF_MEMPOOL(name, type_t)
|
||||
|
||||
/* Classic Singly List without allocation */
|
||||
#define C_MEMPOOL_DEF_SINGLY_LIST(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _slist_node_s) { \
|
||||
struct M_C(name, _slist_node_s) *next; \
|
||||
type_t data; \
|
||||
} M_C(name, _slist_node_t); \
|
||||
\
|
||||
typedef struct M_C(name, _slist_node_s) *M_C(name, _slist_t)[1]; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_init)(M_C(name, _slist_t) list) \
|
||||
{ \
|
||||
*list = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_push)(M_C(name, _slist_t) list, \
|
||||
M_C(name, _slist_node_t) *node) \
|
||||
{ \
|
||||
node->next = *list; \
|
||||
*list = node; \
|
||||
} \
|
||||
\
|
||||
static inline M_C(name, _slist_node_t) * \
|
||||
M_C(name, _slist_pop)(M_C(name, _slist_t) list) \
|
||||
{ \
|
||||
assert (*list != NULL); \
|
||||
M_C(name, _slist_node_t) *node = *list; \
|
||||
*list = node->next; \
|
||||
M_IF_DEBUG(node->next = NULL;) \
|
||||
return node; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _slist_empty_p)(M_C(name, _slist_t) list) \
|
||||
{ \
|
||||
return *list == NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_move)(M_C(name, _slist_t) list, \
|
||||
M_C(name, _slist_t) src) \
|
||||
{ \
|
||||
*list = *src; \
|
||||
*src = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_clear)(M_C(name, _slist_t) list) \
|
||||
{ \
|
||||
M_C(name, _slist_node_t) *it = *list, *next; \
|
||||
while (it) { \
|
||||
next = it->next; \
|
||||
M_MEMORY_DEL(it); \
|
||||
it = next; \
|
||||
} \
|
||||
*list = NULL; \
|
||||
} \
|
||||
|
||||
|
||||
/* Lock Free free queue list (not generic one) of lists without allocation
|
||||
Based on Michael & Scott Lock Free Queue List algorithm.
|
||||
Each list is considered empty if there is only one node within.
|
||||
This LF Queue List doesn't try to prevent the ABA problem. It is up to the
|
||||
caller to avoid recycling the nodes too fast.
|
||||
Each list has its own unique NIL ptr in order to avoid issues when
|
||||
migrating a node from a Q to another: in the following scenario,
|
||||
- Thread 1 performs a PUSH of N in Q1 with Q1 empty (only node is NA)
|
||||
NA.next is NIL.
|
||||
- Thread 1 is interrupted just before the CAS on NA.next
|
||||
- Thread 2 performs a sucessfull push of NB in Q1. NA.next is set to NB.
|
||||
- Thread 2 performs a sucessfull pop of NA in Q1
|
||||
- Thread 2 performs a sucessfull push of NA in Q2. NA.next is set to NIL.
|
||||
- Thread 1 is restored and will succeed as NA.next is once again NIL.
|
||||
In order to prevent the last CAS to succeed, each queue uses its own NIL pointer.
|
||||
It is a derived problem of the ABA problem.
|
||||
*/
|
||||
/* TODO: Optimize alignement to reduce memory consumption. NIL object can use []
|
||||
to reduce memory consumption too (non compatible with C++ ...) */
|
||||
#define C_MEMPOOL_DEF_LF_QUEUE(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _lf_node_s) { \
|
||||
M_ATTR_EXTENSION _Atomic(struct M_C(name, _lf_node_s) *) next; \
|
||||
m_gc_atomic_ticket_t cpt; \
|
||||
M_C(name, _slist_t) list; \
|
||||
} M_C(name, _lf_node_t); \
|
||||
\
|
||||
typedef struct M_C(name, _lflist_s) { \
|
||||
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) head; \
|
||||
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) tail; \
|
||||
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_C(name, _lf_node_t) nil; \
|
||||
} M_C(name, _lflist_t)[1]; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lflist_init)(M_C(name, _lflist_t) list, \
|
||||
M_C(name, _lf_node_t) *node) \
|
||||
{ \
|
||||
atomic_init(&list->head, node); \
|
||||
atomic_init(&list->tail, node); \
|
||||
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _lflist_empty_p)(M_C(name, _lflist_t) list) \
|
||||
{ \
|
||||
return atomic_load(&list->tail) == atomic_load(&list->head); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lflist_push)(M_C(name, _lflist_t) list, \
|
||||
M_C(name, _lf_node_t) *node, m_backoff_t bkoff) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) *tail; \
|
||||
M_C(name, _lf_node_t) *next; \
|
||||
\
|
||||
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
|
||||
m_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load_explicit(&tail->next, memory_order_acquire); \
|
||||
if (M_UNLIKELY(next != &list->nil)) { \
|
||||
/* Tail was not pointing to the last node \
|
||||
Try to swing Tail to the next node */ \
|
||||
atomic_compare_exchange_weak_explicit(&list->tail, \
|
||||
&tail, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Try to link node at the end of the linked list */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&tail->next, \
|
||||
&next, node, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) \
|
||||
break; \
|
||||
m_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
/* Enqueue is done. Try to swing Tail to the inserted node \
|
||||
If it fails, someone else will do it or has already did it. */ \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, node, \
|
||||
memory_order_acq_rel, \
|
||||
memory_order_relaxed); \
|
||||
} \
|
||||
\
|
||||
static inline M_C(name, _lf_node_t) * \
|
||||
M_C(name, _lflist_pop)(M_C(name, _lflist_t) list, m_backoff_t bkoff) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) *head; \
|
||||
M_C(name, _lf_node_t) *tail; \
|
||||
M_C(name, _lf_node_t) *next; \
|
||||
\
|
||||
/* Reinitialize backoff */ \
|
||||
m_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
head = atomic_load(&list->head); \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load(&head->next); \
|
||||
/* Are head, tail, and next consistent?*/ \
|
||||
if (M_LIKELY(head == \
|
||||
atomic_load_explicit(&list->head, memory_order_relaxed))) \
|
||||
{ \
|
||||
/* Is queue empty or Tail falling behind? */ \
|
||||
if (head == tail) { \
|
||||
/* Is queue empty? */ \
|
||||
if (next == &list->nil) \
|
||||
return NULL; \
|
||||
/* Tail is falling behind. Try to advance it */ \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, \
|
||||
next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Try to swing Head to the next node */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&list->head, \
|
||||
&head, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) { \
|
||||
break; \
|
||||
} \
|
||||
/* Failure: perform a random exponential backoff */ \
|
||||
m_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
/* dequeue returns an element that becomes the new dummy element (the new head), \
|
||||
and the former dummy element (the former head) is removed: \
|
||||
Since we want a link of free list, and we don't care about the content itsef, \
|
||||
provided that the node we return is older than the one we should return, \
|
||||
Therefore, we return the previous dummy head. \
|
||||
As such, it is not the original MSqueue algorithm. */ \
|
||||
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
|
||||
return head; \
|
||||
} \
|
||||
\
|
||||
/* Dequeue a node if the node is old enough */ \
|
||||
static inline M_C(name, _lf_node_t) * \
|
||||
M_C(name, _lflist_pop_if)(M_C(name, _lflist_t) list, \
|
||||
m_gc_ticket_t age, m_backoff_t bkoff) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) *head; \
|
||||
M_C(name, _lf_node_t) *tail; \
|
||||
M_C(name, _lf_node_t) *next; \
|
||||
\
|
||||
m_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
head = atomic_load(&list->head); \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load(&head->next); \
|
||||
if (M_LIKELY(head == atomic_load_explicit(&list->head, memory_order_relaxed))) \
|
||||
{ \
|
||||
if (head == tail) { \
|
||||
if (next == &list->nil) \
|
||||
return NULL; \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Test if the node is old enought to be popped */ \
|
||||
if (atomic_load_explicit(&next->cpt, memory_order_relaxed) >= age) \
|
||||
return NULL; \
|
||||
/* Try to swing Head to the next node */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&list->head, \
|
||||
&head, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) { \
|
||||
break; \
|
||||
} \
|
||||
m_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
|
||||
return head; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lflist_clear)(M_C(name, _lflist_t) list) \
|
||||
{ \
|
||||
m_backoff_t bkoff; \
|
||||
m_backoff_init(bkoff); \
|
||||
while (true) { \
|
||||
M_C(name, _lf_node_t) *node = M_C(name, _lflist_pop)(list, bkoff); \
|
||||
if (node == NULL) break; \
|
||||
M_C(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
|
||||
memory_order_relaxed); \
|
||||
M_C(name, _slist_clear)(node->list); \
|
||||
M_MEMORY_DEL(node); \
|
||||
node = next; \
|
||||
} \
|
||||
/* Dummy node to free too */ \
|
||||
M_C(name, _lf_node_t) *dummy; \
|
||||
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
|
||||
M_C(name, _slist_clear)(dummy->list); \
|
||||
M_MEMORY_DEL(dummy); \
|
||||
} \
|
||||
|
||||
/* System node allocator: request memory to the system.
|
||||
As such it is a non Lock-Free path. */
|
||||
#define C_MEMPOOL_DEF_SYSTEM_ALLOC(name, type_t) \
|
||||
\
|
||||
static inline M_C(name, _lf_node_t) * \
|
||||
M_C(name, _alloc_node)(unsigned int initial) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) * node; \
|
||||
node = M_MEMORY_ALLOC(M_C(name, _lf_node_t)); \
|
||||
if (M_UNLIKELY(node == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
|
||||
return NULL; \
|
||||
} \
|
||||
atomic_init(&node->next, (M_C(name, _lf_node_t) *) 0); \
|
||||
atomic_init(&node->cpt, 0UL); \
|
||||
M_C(name, _slist_init)(node->list); \
|
||||
for(unsigned i = 0; i < initial; i++) { \
|
||||
M_C(name, _slist_node_t) *n; \
|
||||
n = M_MEMORY_ALLOC(M_C(name, _slist_node_t)); \
|
||||
if (M_UNLIKELY(n == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
|
||||
return NULL; \
|
||||
} \
|
||||
M_C(name, _slist_push)(node->list, n); \
|
||||
} \
|
||||
return node; \
|
||||
} \
|
||||
|
||||
/* Concurrent Memory pool
|
||||
The data structure is the following.
|
||||
Each thread has its own pool of nodes (local) that only it can
|
||||
access (it is a singly list). If there is no longer any node in this
|
||||
pool, it requests a new pool to the lock free queue of pool (group of
|
||||
nodes). If it fails, it requests a new pool to the system allocator
|
||||
(and from there it is no longer lock free).
|
||||
This memory pool can only be lock free if the initial state is
|
||||
sufficiently dimensionned to avoid calling the system allocator during
|
||||
the normal processing.
|
||||
Then each thread pushs its deleted node into another pool of nodes,
|
||||
where the node is logically deleted (no contain of the node is destroyed
|
||||
at this point and the node can be freely accessed by other threads).
|
||||
Once the thread mempool is put to sleep, the age of the pool of logical
|
||||
deleted nodes is computed and this pool is move to the Lock Free Queue
|
||||
List of pools to be reclaimed. Then A Garbage Collector is performed
|
||||
on this Lock Free Queue list to reclaim all pools thare are sufficiently
|
||||
aged (taking into account the grace period of the pool) to be moved back
|
||||
to the Lock Free Queue of the free pools.
|
||||
|
||||
Each pool of nodes can be in the following state:
|
||||
* FREE state if it is present in the Lock Free Queue of free pools.
|
||||
* EMPTY state if it is present in the Lock Free Queue of empty pools
|
||||
which means that the nodes present in it has been USED directly by a thread,
|
||||
* TO_BE_RECLAIMED state if it is present in the Lock Free Queue of TBR pools
|
||||
|
||||
A pool of nodes will go to the following state:
|
||||
FREE --> EMPTY --> TO_BE_RECLAIMED
|
||||
^ |
|
||||
+----------------------+
|
||||
|
||||
The ABA problem is taken into account as a node cannot be reused in the
|
||||
same queue without performing a full cycle of its state. Moreover
|
||||
it can only move from TO_BE_RECLAIMED to FREE if and only if a grace
|
||||
period is finished (and then we are sure that no thread references any
|
||||
older node).
|
||||
|
||||
Each thread has its own backoff structure (with local pseudo-random
|
||||
generator).
|
||||
|
||||
The grace period is detected through a global age counter (ticket)
|
||||
that is incremented each time a thread is awaken / sleep.
|
||||
Each thread has its own age that is set to the global ticket on sleep/awaken.
|
||||
The age of the pool to be reclaimed is also set to this global age counter.
|
||||
|
||||
To ensure that the grace period is finished, it tests if all threads
|
||||
are younger than the age of the pool to be reclaimed.
|
||||
|
||||
From a performance point of view, this puts a bottleneck on the global
|
||||
age counter that is shared and incremented by all threads. However,
|
||||
the sleep/awaken operations are much less frequent than other operations.
|
||||
Thus, it shall not have a huge impact on the performance if the user
|
||||
code is intelligent with the sleep/awaken operations.
|
||||
|
||||
As such it won't support more than ULONG_MAX sleep for all threads.
|
||||
*/
|
||||
#define C_MEMPOOL_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _lfmp_thread_s) { \
|
||||
M_C(name, _slist_t) free; \
|
||||
M_C(name, _slist_t) to_be_reclaimed; \
|
||||
M_CACHELINE_ALIGN(align1, M_C(name, _slist_t), M_C(name, _slist_t)); \
|
||||
} M_C(name, _lfmp_thread_t); \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lfmp_thread_init)(M_C(name, _lfmp_thread_t) *t) \
|
||||
{ \
|
||||
M_C(name, _slist_init)(t->free); \
|
||||
M_C(name, _slist_init)(t->to_be_reclaimed); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lfmp_thread_clear)(M_C(name, _lfmp_thread_t) *t) \
|
||||
{ \
|
||||
assert(M_C(name, _slist_empty_p)(t->to_be_reclaimed)); \
|
||||
M_C(name, _slist_clear)(t->free); \
|
||||
M_C(name, _slist_clear)(t->to_be_reclaimed); \
|
||||
} \
|
||||
|
||||
/* NOTE: once a node is deleted, its data are kept readable until the future GC */
|
||||
#define C_MEMPOOL_DEF_LF_MEMPOOL(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
unsigned initial; \
|
||||
M_C(name, _lfmp_thread_t) *thread_data; \
|
||||
M_C(name, _lflist_t) free; \
|
||||
M_C(name, _lflist_t) to_be_reclaimed; \
|
||||
M_C(name, _lflist_t) empty; \
|
||||
m_gc_mempool_list_t mempool_node; \
|
||||
struct m_gc_s *gc_mem; \
|
||||
} M_C(name, _t)[1]; \
|
||||
\
|
||||
/* Garbage collect of the nodes of the mempool on sleep */ \
|
||||
static inline void \
|
||||
M_C(name, _int_gc_on_sleep)(m_gc_t gc_mem, m_gc_mempool_list_t *data, \
|
||||
m_gc_tid_t id, m_gc_ticket_t ticket, m_gc_ticket_t min_ticket) \
|
||||
{ \
|
||||
/* Get back the mempool from the node */ \
|
||||
struct M_C(name, _s) *mempool = \
|
||||
M_TYPE_FROM_FIELD(struct M_C(name, _s), data, m_gc_mempool_list_t, mempool_node); \
|
||||
\
|
||||
/* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \
|
||||
if (!M_C(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
|
||||
M_C(name, _lf_node_t) *node; \
|
||||
/* Get a new empty group of nodes */ \
|
||||
node = M_C(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \
|
||||
if (M_UNLIKELY (node == NULL)) { \
|
||||
/* Fail to get an empty group of node. \
|
||||
Alloc a new one from the system */ \
|
||||
node = M_C(name, _alloc_node)(0); \
|
||||
assert(node != NULL); \
|
||||
} \
|
||||
assert(M_C(name, _slist_empty_p)(node->list)); \
|
||||
M_C(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
|
||||
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \
|
||||
M_C(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
\
|
||||
/* Perform a GC of the freelist of nodes */ \
|
||||
while (true) { \
|
||||
M_C(name, _lf_node_t) *node; \
|
||||
node = M_C(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
|
||||
min_ticket, gc_mem->thread_data[id].bkoff); \
|
||||
if (node == NULL) break; \
|
||||
M_C(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(M_C(name, _t) mem, m_gc_t gc_mem, \
|
||||
unsigned init_node_count, unsigned init_group_count) \
|
||||
{ \
|
||||
const unsigned long max_thread = gc_mem->max_thread; \
|
||||
/* Initialize the thread data of the mempool */ \
|
||||
mem->thread_data = M_MEMORY_REALLOC(M_C(name, _lfmp_thread_t), NULL, max_thread); \
|
||||
if (mem->thread_data == NULL) { \
|
||||
M_MEMORY_FULL(max_thread * sizeof(M_C(name, _lfmp_thread_t))); \
|
||||
return; \
|
||||
} \
|
||||
for(unsigned i = 0; i < max_thread;i++) { \
|
||||
M_C(name, _lfmp_thread_init)(&mem->thread_data[i]); \
|
||||
} \
|
||||
/* Preallocate some group of nodes for the mempool */ \
|
||||
mem->initial = M_MAX(C_MEMPOOL_MIN_NODE_PER_GROUP, init_node_count); \
|
||||
M_C(name, _lflist_init)(mem->free, M_C(name, _alloc_node)(init_node_count)); \
|
||||
M_C(name, _lflist_init)(mem->to_be_reclaimed, M_C(name, _alloc_node)(init_node_count)); \
|
||||
M_C(name, _lflist_init)(mem->empty, M_C(name, _alloc_node)(0)); \
|
||||
for(unsigned i = 1; i < init_group_count; i++) { \
|
||||
M_C(name, _lflist_push)(mem->free, M_C(name, _alloc_node)(init_node_count), \
|
||||
gc_mem->thread_data[0].bkoff); \
|
||||
M_C(name, _lflist_push)(mem->empty, M_C(name, _alloc_node)(0), \
|
||||
gc_mem->thread_data[0].bkoff); \
|
||||
} \
|
||||
/* Register the mempool in the GC */ \
|
||||
mem->mempool_node.gc_on_sleep = M_C(name, _int_gc_on_sleep); \
|
||||
mem->mempool_node.next = gc_mem->mempool_list; \
|
||||
gc_mem->mempool_list = &mem->mempool_node; \
|
||||
mem->gc_mem = gc_mem; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _clear)(M_C(name, _t) mem) \
|
||||
{ \
|
||||
const unsigned max_thread = mem->gc_mem->max_thread; \
|
||||
for(unsigned i = 0; i < max_thread;i++) { \
|
||||
M_C(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
|
||||
} \
|
||||
M_MEMORY_FREE(mem->thread_data); \
|
||||
mem->thread_data = NULL; \
|
||||
M_C(name, _lflist_clear)(mem->empty); \
|
||||
M_C(name, _lflist_clear)(mem->free); \
|
||||
assert(M_C(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
|
||||
M_C(name, _lflist_clear)(mem->to_be_reclaimed); \
|
||||
/* TODO: Unregister from the GC? */ \
|
||||
} \
|
||||
\
|
||||
static inline type_t * \
|
||||
M_C(name, _new)(M_C(name, _t) mem, m_gc_tid_t id) \
|
||||
{ \
|
||||
M_C(name, _slist_node_t) *snode; \
|
||||
M_C(name, _lf_node_t) *node; \
|
||||
while (true) { \
|
||||
/* Fast & likely path where we access the thread pool of nodes */ \
|
||||
if (M_LIKELY(!M_C(name, _slist_empty_p)(mem->thread_data[id].free))) { \
|
||||
snode = M_C(name, _slist_pop)(mem->thread_data[id].free); \
|
||||
return &snode->data; \
|
||||
} \
|
||||
/* Request a group node to the freelist of groups */ \
|
||||
node = M_C(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \
|
||||
if (M_UNLIKELY (node == NULL)) { \
|
||||
/* Request a new group to the system. Non Lock Free path */ \
|
||||
assert(mem->initial > 0); \
|
||||
node = M_C(name, _alloc_node)(mem->initial); \
|
||||
assert(node != NULL); \
|
||||
assert(!M_C(name, _slist_empty_p)(node->list)); \
|
||||
} \
|
||||
M_C(name, _slist_move)(mem->thread_data[id].free, node->list); \
|
||||
/* Push back the empty group */ \
|
||||
assert (M_C(name, _slist_empty_p)(node->list)); \
|
||||
M_C(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _del)(M_C(name, _t) mem, type_t *d, m_gc_tid_t id) \
|
||||
{ \
|
||||
M_C(name, _slist_node_t) *snode; \
|
||||
assert( d != NULL); \
|
||||
snode = M_TYPE_FROM_FIELD(M_C(name, _slist_node_t), d, type_t, data); \
|
||||
M_C(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
|
||||
} \
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/* Define the ID of a thread */
|
||||
typedef int m_gc_tid_t;
|
||||
|
||||
/* Define the age of a node */
|
||||
/* TODO: Compute if sufficient (worst cast ULONG_MAX is 32 bits) */
|
||||
typedef unsigned long m_gc_ticket_t;
|
||||
typedef atomic_ulong m_gc_atomic_ticket_t;
|
||||
|
||||
/* Define the Linked List of mempools that are registered in the GC */
|
||||
struct m_gc_s;
|
||||
typedef struct m_gc_mempool_list_s {
|
||||
struct m_gc_mempool_list_s *next;
|
||||
void (*gc_on_sleep)(struct m_gc_s *gc_mem,
|
||||
struct m_gc_mempool_list_s *data, m_gc_tid_t id,
|
||||
m_gc_ticket_t ticket, m_gc_ticket_t min_ticket);
|
||||
void *data;
|
||||
} m_gc_mempool_list_t;
|
||||
|
||||
/* Define the Garbage collector thread data */
|
||||
typedef struct m_gc_lfmp_thread_s {
|
||||
m_gc_atomic_ticket_t ticket;
|
||||
m_backoff_t bkoff;
|
||||
M_CACHELINE_ALIGN(align1, atomic_ulong, m_backoff_t);
|
||||
} m_gc_lfmp_thread_t;
|
||||
|
||||
/* Define the Garbage collector coordinator */
|
||||
typedef struct m_gc_s {
|
||||
m_gc_atomic_ticket_t ticket;
|
||||
m_gc_tid_t max_thread;
|
||||
genint_t thread_alloc;
|
||||
m_gc_lfmp_thread_t *thread_data;
|
||||
m_gc_mempool_list_t *mempool_list;
|
||||
} m_gc_t[1];
|
||||
|
||||
static inline void
|
||||
m_gc_init(m_gc_t gc_mem, unsigned long max_thread)
|
||||
{
|
||||
assert(gc_mem != NULL);
|
||||
assert(max_thread > 0 && max_thread < INT_MAX);
|
||||
|
||||
atomic_init(&gc_mem->ticket, 0UL);
|
||||
genint_init(gc_mem->thread_alloc, max_thread);
|
||||
gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_t, NULL, max_thread);
|
||||
if (gc_mem->thread_data == NULL) {
|
||||
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_t));
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX);
|
||||
m_backoff_init(gc_mem->thread_data[i].bkoff);
|
||||
}
|
||||
gc_mem->max_thread = max_thread;
|
||||
gc_mem->mempool_list = NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_clear(m_gc_t gc_mem)
|
||||
{
|
||||
assert(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
|
||||
for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) {
|
||||
m_backoff_clear(gc_mem->thread_data[i].bkoff);
|
||||
}
|
||||
M_MEMORY_FREE(gc_mem->thread_data);
|
||||
gc_mem->thread_data = NULL;
|
||||
genint_clear(gc_mem->thread_alloc);
|
||||
}
|
||||
|
||||
static inline m_gc_tid_t
|
||||
m_gc_attach_thread(m_gc_t gc_mem)
|
||||
{
|
||||
assert(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
|
||||
unsigned id = genint_pop(gc_mem->thread_alloc);
|
||||
return M_ASSIGN_CAST(m_gc_tid_t, id);
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
assert(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
assert(id >= 0 && id < gc_mem->max_thread);
|
||||
assert(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
|
||||
|
||||
genint_push(gc_mem->thread_alloc, id);
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
assert(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
assert(id >= 0 && id < gc_mem->max_thread);
|
||||
assert(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
|
||||
|
||||
m_gc_ticket_t t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1;
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, t);
|
||||
}
|
||||
|
||||
static inline m_gc_ticket_t
|
||||
m_gc_int_min_ticket(m_gc_t gc_mem)
|
||||
{
|
||||
m_gc_ticket_t min = atomic_load(&gc_mem->thread_data[0].ticket);
|
||||
for(m_gc_tid_t i = 1; i < gc_mem->max_thread; i++) {
|
||||
m_gc_ticket_t t = atomic_load(&gc_mem->thread_data[i].ticket);
|
||||
min = M_MIN(t, min);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
/* Increase life time of the thread */
|
||||
m_gc_ticket_t t = atomic_fetch_add(&gc_mem->ticket, 1UL);
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, t+1);
|
||||
const m_gc_ticket_t min_ticket = m_gc_int_min_ticket(gc_mem);
|
||||
/* Iterate over all registered mempools */
|
||||
m_gc_mempool_list_t *it = gc_mem->mempool_list;
|
||||
|
||||
while (it) {
|
||||
/* Perform a garbage collect of the mempool */
|
||||
it->gc_on_sleep(gc_mem, it, id, t, min_ticket);
|
||||
/* Next mempool to scan for GC */
|
||||
it = it->next;
|
||||
}
|
||||
/* Sleep the thread */
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, ULONG_MAX);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* */
|
||||
/* VLA MEMPOOL */
|
||||
/* */
|
||||
/***********************************************************************/
|
||||
|
||||
C_MEMPOOL_DEF_SINGLY_LIST(m_vlapool, char)
|
||||
C_MEMPOOL_DEF_LF_QUEUE(m_vlapool, char)
|
||||
C_MEMPOOL_DEF_SYSTEM_ALLOC(m_vlapool, char)
|
||||
|
||||
typedef struct m_vlapool_lfmp_thread_s {
|
||||
m_vlapool_slist_t to_be_reclaimed;
|
||||
M_CACHELINE_ALIGN(align1, m_vlapool_slist_t);
|
||||
} m_vlapool_lfmp_thread_t;
|
||||
|
||||
static inline void
|
||||
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_t *t)
|
||||
{
|
||||
m_vlapool_slist_init(t->to_be_reclaimed);
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_t *t)
|
||||
{
|
||||
assert(m_vlapool_slist_empty_p(t->to_be_reclaimed));
|
||||
m_vlapool_slist_clear(t->to_be_reclaimed);
|
||||
}
|
||||
|
||||
typedef struct m_vlapool_s {
|
||||
m_vlapool_lflist_t to_be_reclaimed;
|
||||
m_vlapool_lflist_t empty;
|
||||
m_vlapool_lfmp_thread_t *thread_data;
|
||||
m_gc_mempool_list_t mvla_node;
|
||||
struct m_gc_s *gc_mem;
|
||||
} m_vlapool_t[1];
|
||||
|
||||
/* Garbage collect of the nodes of the vla mempool on sleep */
|
||||
static inline void
|
||||
m_vlapool_int_gc_on_sleep(m_gc_t gc_mem, m_gc_mempool_list_t *data,
|
||||
m_gc_tid_t id, m_gc_ticket_t ticket, m_gc_ticket_t min_ticket)
|
||||
{
|
||||
/* Get back the mempool from the node */
|
||||
struct m_vlapool_s *vlapool =
|
||||
M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_gc_mempool_list_t, mvla_node);
|
||||
|
||||
/* Move the local nodes of the vlapool to be reclaimed to the thread into the global pool */
|
||||
if (!m_vlapool_slist_empty_p(vlapool->thread_data[id].to_be_reclaimed)) {
|
||||
m_vlapool_lf_node_t *node;
|
||||
/* Get a new empty group of nodes */
|
||||
node = m_vlapool_lflist_pop(vlapool->empty, gc_mem->thread_data[id].bkoff);
|
||||
if (M_UNLIKELY (node == NULL)) {
|
||||
/* Fail to get an empty group of node.
|
||||
Alloc a new one from the system */
|
||||
node = m_vlapool_alloc_node(0);
|
||||
assert(node != NULL);
|
||||
}
|
||||
assert(m_vlapool_slist_empty_p(node->list));
|
||||
m_vlapool_slist_move(node->list, vlapool->thread_data[id].to_be_reclaimed);
|
||||
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed);
|
||||
m_vlapool_lflist_push(vlapool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff);
|
||||
}
|
||||
|
||||
/* Perform a GC of the freelist of nodes */
|
||||
while (true) {
|
||||
m_vlapool_lf_node_t *node;
|
||||
node = m_vlapool_lflist_pop_if(vlapool->to_be_reclaimed,
|
||||
min_ticket, gc_mem->thread_data[id].bkoff);
|
||||
if (node == NULL) break;
|
||||
// No reuse of VLA nodes. Free physically the node back to the system
|
||||
m_vlapool_slist_clear(node->list);
|
||||
// Add back the empty group of nodes
|
||||
m_vlapool_slist_init(node->list);
|
||||
m_vlapool_lflist_push(vlapool->empty, node, gc_mem->thread_data[id].bkoff);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem)
|
||||
{
|
||||
const unsigned long max_thread = gc_mem->max_thread;
|
||||
|
||||
/* Initialize the thread data of the vlapool */
|
||||
mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_t, NULL, max_thread);
|
||||
if (mem->thread_data == NULL) {
|
||||
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_t));
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
m_vlapool_lfmp_thread_init(&mem->thread_data[i]);
|
||||
}
|
||||
|
||||
/* Initialize the lists */
|
||||
m_vlapool_lflist_init(mem->to_be_reclaimed, m_vlapool_alloc_node(0));
|
||||
m_vlapool_lflist_init(mem->empty, m_vlapool_alloc_node(0));
|
||||
|
||||
/* Register the mempool in the GC */
|
||||
mem->mvla_node.gc_on_sleep = m_vlapool_int_gc_on_sleep;
|
||||
mem->mvla_node.next = gc_mem->mempool_list;
|
||||
gc_mem->mempool_list = &mem->mvla_node;
|
||||
mem->gc_mem = gc_mem;
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_clear(m_vlapool_t mem)
|
||||
{
|
||||
const unsigned max_thread = mem->gc_mem->max_thread;
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
m_vlapool_lfmp_thread_clear(&mem->thread_data[i]);
|
||||
}
|
||||
M_MEMORY_FREE(mem->thread_data);
|
||||
mem->thread_data = NULL;
|
||||
m_vlapool_lflist_clear(mem->empty);
|
||||
assert(m_vlapool_lflist_empty_p(mem->to_be_reclaimed));
|
||||
m_vlapool_lflist_clear(mem->to_be_reclaimed);
|
||||
/* TODO: Unregister from the GC? */
|
||||
}
|
||||
|
||||
static inline void *
|
||||
m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size)
|
||||
{
|
||||
assert(mem != NULL && mem->gc_mem != NULL);
|
||||
assert(id >= 0 && id < mem->gc_mem->max_thread);
|
||||
assert( atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
|
||||
|
||||
// Nothing to do with theses parameters yet
|
||||
(void) mem;
|
||||
(void) id;
|
||||
|
||||
// Ensure the size is big enough to also represent a node
|
||||
size += offsetof(struct m_vlapool_slist_node_s, data);
|
||||
|
||||
// Simply wrap around a system call to get the memory
|
||||
char *ptr = M_MEMORY_REALLOC(char, NULL, size);
|
||||
return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data));
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_del(m_vlapool_t mem, void *d, m_gc_tid_t id)
|
||||
{
|
||||
assert(mem != NULL && mem->gc_mem != NULL);
|
||||
assert(id >= 0 && id < mem->gc_mem->max_thread);
|
||||
assert(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
|
||||
assert(d != NULL);
|
||||
|
||||
// Get back the pointer to a struct m_vlapool_slist_node_s.
|
||||
d = M_ASSIGN_CAST(void *, M_ASSIGN_CAST(char *, d) - offsetof(struct m_vlapool_slist_node_s, data));
|
||||
m_vlapool_slist_node_t *snode = M_ASSIGN_CAST(m_vlapool_slist_node_t *, d);
|
||||
// Push the logicaly free memory into the list of the nodes to be reclaimed.
|
||||
m_vlapool_slist_push(mem->thread_data[id].to_be_reclaimed, snode);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
+375
-102
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Basic Protected Concurrent module over container.
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,35 +27,47 @@
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-mutex.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
/* Define a protected concurrent container and its associated functions.
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on the given container.
|
||||
USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_DEF(name, ...) \
|
||||
CONCURRENTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
#define CONCURRENT_DEF(name, ...) \
|
||||
CONCURRENTI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t) )))
|
||||
|
||||
|
||||
/* Define the oplist of a protected concurrent container given its name and its oplist.
|
||||
USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */
|
||||
#define CONCURRENT_OPLIST(...) \
|
||||
CONCURRENTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
CONCURRENTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on its given container. Operations that perform only read of the container
|
||||
can be done in parallel.
|
||||
USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_RP_DEF(name, ...) \
|
||||
CONCURRENTI_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t) )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// Deferred evaluation for the oplist definition.
|
||||
#define CONCURRENTI_OPLIST(arg) CONCURRENTI_OPLIST2 arg
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define CONCURRENTI_OPLIST_P1(arg) CONCURRENTI_OPLIST_P2 arg
|
||||
|
||||
/* OPLIST definition
|
||||
GET_KEY is not present as its interface is not compatible with a concurrent
|
||||
container (_get returns a pointer to an internal data, data that may be
|
||||
destroyed by another thread).
|
||||
*/
|
||||
#define CONCURRENTI_OPLIST2(name, oplist) \
|
||||
#define CONCURRENTI_OPLIST_P2(name, oplist) \
|
||||
(M_IF_METHOD(INIT, oplist)(INIT(M_C(name, _init)),) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_C(name, _init_set)),) \
|
||||
,M_IF_METHOD(SET, oplist)(SET(M_C(name, _set)),) \
|
||||
@@ -67,6 +79,7 @@
|
||||
,SUBTYPE(M_C(name, _type_t)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(TEST_EMPTY, oplist)(TEST_EMPTY(M_C(name,_empty_p)),) \
|
||||
,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_C(name,_size)),) \
|
||||
,M_IF_METHOD(CLEAN, oplist)(CLEAN(M_C(name,_clean)),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \
|
||||
@@ -82,6 +95,8 @@
|
||||
,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),) \
|
||||
@@ -89,15 +104,24 @@
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
/* Internal contract
|
||||
Nothing notable. Can't check too much without locking the structure itself
|
||||
*/
|
||||
#define CONCURRENTI_CONTRACT(c) do { \
|
||||
assert ((c)->self == c); \
|
||||
assert ((c) != NULL); \
|
||||
} while (0)
|
||||
|
||||
// Deferred evaluation for the concurrent definition.
|
||||
#define CONCURRENTI_DEF(arg) CONCURRENTI_DEF2 arg
|
||||
/* Deferred evaluation for the concurrent definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define CONCURRENTI_DEF_P1(arg) CONCURRENTI_DEF_P2 arg
|
||||
|
||||
// Internal definition.
|
||||
#define CONCURRENTI_DEF2(name, type, oplist, concurrent_t, concurrent_it_t) \
|
||||
/* Internal concurrent definition
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define CONCURRENTI_DEF_P2(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *self; \
|
||||
@@ -111,14 +135,85 @@
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
/* Define the lock strategy (global & shared lock) */ \
|
||||
static inline void \
|
||||
M_C(name, _init)(concurrent_t out) \
|
||||
M_C(name, _internal_init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _internal_clear)(concurrent_t out) \
|
||||
{ \
|
||||
assert (out->self == out); \
|
||||
m_mutex_clear(out->lock); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
out->self = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_lock)(const concurrent_t out) \
|
||||
{ \
|
||||
assert (out->self == out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_unlock)(const concurrent_t out) \
|
||||
{ \
|
||||
assert (out->self == out); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
assert (out->self == out); \
|
||||
m_cond_wait(out->self->there_is_data, out->self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_lock)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_lock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_unlock)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
m_cond_wait(out->self->there_is_data, out->self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_signal)(concurrent_t out) \
|
||||
{ \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
} \
|
||||
\
|
||||
CONCURRENTI_DEF_FUNC_P3(name, type, oplist, concurrent_t)
|
||||
|
||||
|
||||
/* Internal definition of the functions commons to concurrent and rp-concurrent
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define CONCURRENTI_DEF_FUNC_P3(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init)(concurrent_t out) \
|
||||
{ \
|
||||
M_C(name, _internal_init)(out); \
|
||||
M_CALL_INIT(oplist, out->data); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
@@ -130,12 +225,10 @@
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
assert (out != src); \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
m_mutex_lock (src->self->lock); \
|
||||
M_C(name, _internal_init)(out); \
|
||||
M_C(name, _read_lock)(src); \
|
||||
M_CALL_INIT_SET(oplist, out->data, src->data); \
|
||||
m_mutex_unlock(src->self->lock); \
|
||||
M_C(name, _read_unlock)(src); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
@@ -147,19 +240,19 @@
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
if (out == src) return; \
|
||||
if (out < src) { \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_mutex_lock (src->self->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_C(name, _read_lock)(src); \
|
||||
} else { \
|
||||
m_mutex_lock (src->self->lock); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _read_lock)(src); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
} \
|
||||
M_CALL_SET(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
m_mutex_unlock (src->self->lock); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _read_lock)(src); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} else { \
|
||||
m_mutex_unlock (out->lock); \
|
||||
m_mutex_unlock (src->self->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
M_C(name, _read_unlock)(src); \
|
||||
} \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
@@ -172,8 +265,7 @@
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
/* No need to lock */ \
|
||||
M_CALL_CLEAR(oplist, out->data); \
|
||||
m_mutex_clear (out->lock); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
M_C(name, _internal_clear)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -184,12 +276,9 @@
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
assert (out != src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
m_mutex_init (out->lock); \
|
||||
m_cond_init (out->there_is_data); \
|
||||
out->self = out; \
|
||||
M_C(name, _internal_init)(out); \
|
||||
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
|
||||
m_mutex_clear (src->lock); \
|
||||
m_cond_clear (src->there_is_data); \
|
||||
M_C(name, _internal_clear)(src); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
@@ -201,11 +290,10 @@
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_MOVE(oplist, out->data, src->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
m_mutex_clear (src->lock); \
|
||||
m_cond_clear (src->there_is_data); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
M_C(name, _internal_clear)(src); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
@@ -217,19 +305,19 @@
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
if (out < src) { \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_mutex_lock (src->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_C(name, _write_lock)(src); \
|
||||
} else { \
|
||||
m_mutex_lock (src->lock); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(src); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
} \
|
||||
M_CALL_SWAP(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
m_mutex_unlock (src->lock); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(src); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} else { \
|
||||
m_mutex_unlock (out->lock); \
|
||||
m_mutex_unlock (src->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
M_C(name, _write_unlock)(src); \
|
||||
} \
|
||||
} \
|
||||
,) \
|
||||
@@ -239,9 +327,9 @@
|
||||
M_C(name, _clean)(concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_CLEAN(oplist, out->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -250,11 +338,23 @@
|
||||
M_C(name, _empty_p)(concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
bool b = M_CALL_TEST_EMPTY(oplist, out->data); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_SIZE, oplist)( \
|
||||
static inline size_t \
|
||||
M_C(name, _size)(concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
size_t r = M_CALL_GET_SIZE(oplist, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET_KEY, oplist)( \
|
||||
@@ -262,25 +362,25 @@
|
||||
M_C(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_SET_KEY(oplist, out->data, key, data); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
M_C(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (out_data != NULL); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return p != NULL; \
|
||||
} \
|
||||
,) \
|
||||
@@ -291,11 +391,11 @@
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (out_data != NULL); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_SET_KEY(oplist, out->data, key); \
|
||||
assert (p != NULL); \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -304,9 +404,9 @@
|
||||
M_C(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
@@ -316,10 +416,10 @@
|
||||
M_C(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_PUSH(oplist, out->data, data); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -328,9 +428,9 @@
|
||||
M_C(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -339,10 +439,10 @@
|
||||
M_C(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_PUSH_MOVE(oplist, out->data, data); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -351,9 +451,9 @@
|
||||
M_C(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -362,9 +462,9 @@
|
||||
M_C(name, _get_str)(string_t str, concurrent_t const out, bool a) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
M_CALL_GET_STR(oplist, str, out->data, a); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -373,9 +473,9 @@
|
||||
M_C(name, _out_str)(FILE *f, concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
M_CALL_OUT_STR(oplist, f, out->data); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -384,9 +484,10 @@
|
||||
M_C(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
@@ -396,11 +497,37 @@
|
||||
M_C(name, _in_str)(concurrent_t out, FILE *f) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
bool b = M_CALL_IN_STR(oplist, out->data, f); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(IN_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(concurrent_t out, m_serial_read_t f) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
@@ -411,19 +538,19 @@
|
||||
CONCURRENTI_CONTRACT(out2); \
|
||||
if (out1 == out2) return true; \
|
||||
if (out1 < out2) { \
|
||||
m_mutex_lock (out1->self->lock); \
|
||||
m_mutex_lock (out2->self->lock); \
|
||||
M_C(name, _read_lock)(out1); \
|
||||
M_C(name, _read_lock)(out2); \
|
||||
} else { \
|
||||
m_mutex_lock (out2->self->lock); \
|
||||
m_mutex_lock (out1->self->lock); \
|
||||
M_C(name, _read_lock)(out2); \
|
||||
M_C(name, _read_lock)(out1); \
|
||||
} \
|
||||
bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \
|
||||
if (out1 < out2) { \
|
||||
m_mutex_unlock (out2->self->lock); \
|
||||
m_mutex_unlock (out1->self->lock); \
|
||||
M_C(name, _read_unlock)(out2); \
|
||||
M_C(name, _read_unlock)(out1); \
|
||||
} else { \
|
||||
m_mutex_unlock (out1->self->lock); \
|
||||
m_mutex_unlock (out2->self->lock); \
|
||||
M_C(name, _read_unlock)(out1); \
|
||||
M_C(name, _read_unlock)(out2); \
|
||||
} \
|
||||
return b; \
|
||||
} \
|
||||
@@ -431,12 +558,12 @@
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
|
||||
M_C(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (out_data != NULL); \
|
||||
bool ret = false; \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
while (true) { \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
@@ -445,9 +572,9 @@
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
M_C(name, _read_wait)(out); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
@@ -459,7 +586,7 @@
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (p != NULL); \
|
||||
bool ret = false; \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
while (true) { \
|
||||
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
@@ -467,9 +594,9 @@
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
M_C(name, _write_wait)(out); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
@@ -481,7 +608,7 @@
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (p != NULL); \
|
||||
bool ret = false; \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
while (true) { \
|
||||
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
@@ -489,9 +616,9 @@
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
M_C(name, _write_wait)(out); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
@@ -501,11 +628,157 @@
|
||||
M_C(name, _hash)(concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
size_t h = M_CALL_HASH(oplist, out->data); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return h; \
|
||||
} \
|
||||
,) \
|
||||
|
||||
|
||||
|
||||
/* Deferred evaluation for the RP concurrent definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define CONCURRENTI_RP_DEF_P1(arg) CONCURRENTI_RP_DEF_P2 arg
|
||||
|
||||
/* Internal RP concurrent definition
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define CONCURRENTI_RP_DEF_P2(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *self; \
|
||||
m_mutex_t lock; \
|
||||
m_cond_t rw_done; \
|
||||
size_t read_count; \
|
||||
bool writer_waiting; \
|
||||
m_cond_t there_is_data; /* condition raised when there is data */ \
|
||||
type data; \
|
||||
} concurrent_t[1]; \
|
||||
\
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
/* Define the lock strategy (multi lock) */ \
|
||||
static inline void \
|
||||
M_C(name, _internal_init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->rw_done); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
out->read_count = 0; \
|
||||
out->writer_waiting = false; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _internal_clear)(concurrent_t out) \
|
||||
{ \
|
||||
assert (out->self == out); \
|
||||
m_mutex_clear(out->lock); \
|
||||
m_cond_clear(out->rw_done); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
out->self = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_lock)(const concurrent_t out) \
|
||||
{ \
|
||||
struct M_C(name, _s) *self = out->self; \
|
||||
assert (self == out); \
|
||||
m_mutex_lock (self->lock); \
|
||||
while (self->writer_waiting == true) { \
|
||||
m_cond_wait(self->rw_done, self->lock); \
|
||||
} \
|
||||
self->read_count ++; \
|
||||
m_mutex_unlock (self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_unlock)(const concurrent_t out) \
|
||||
{ \
|
||||
struct M_C(name, _s) *self = out->self; \
|
||||
assert (self == out); \
|
||||
m_mutex_lock (self->lock); \
|
||||
self->read_count --; \
|
||||
if (self->read_count == 0) { \
|
||||
m_cond_broadcast (self->rw_done); \
|
||||
} \
|
||||
m_mutex_unlock (self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_lock)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_lock (out->lock); \
|
||||
while (out->writer_waiting == true) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
out->writer_waiting = true; \
|
||||
while (out->read_count > 0) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_unlock)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_lock (out->lock); \
|
||||
out->writer_waiting = false; \
|
||||
m_cond_broadcast (out->rw_done); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
struct M_C(name, _s) *self = out->self; \
|
||||
assert (self == out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
self->read_count --; \
|
||||
if (self->read_count == 0) { \
|
||||
m_cond_broadcast (self->rw_done); \
|
||||
} \
|
||||
m_cond_wait(self->there_is_data, self->lock); \
|
||||
while (self->writer_waiting == true) { \
|
||||
m_cond_wait(self->rw_done, self->lock); \
|
||||
} \
|
||||
self->read_count ++; \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_wait)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_lock (out->lock); \
|
||||
out->writer_waiting = false; \
|
||||
m_cond_broadcast (out->rw_done); \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
while (out->writer_waiting == true) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
out->writer_waiting = true; \
|
||||
while (out->read_count > 0) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_signal)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
CONCURRENTI_DEF_FUNC_P3(name, type, oplist, concurrent_t)
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - DEQUE module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,30 +30,26 @@
|
||||
/* Define a deque of a given type and its associated functions.
|
||||
USAGE: DEQUE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define DEQUE_DEF(name, ...) \
|
||||
DEQUEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t))))
|
||||
DEQUEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t))))
|
||||
|
||||
|
||||
/* Define the oplist of a deque of a type.
|
||||
USAGE: DEQUE_OPLIST(name[, oplist of the type]) */
|
||||
#define DEQUE_OPLIST(...) \
|
||||
DEQUEI_OPLIST (M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
DEQUEI_OPLIST_P1 (M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
/* Default initial size of a bucket of items */
|
||||
#ifndef DEQUEUI_DEFAULT_SIZE
|
||||
#define DEQUEUI_DEFAULT_SIZE 8
|
||||
|
||||
/* C++ doesn't support flexible array within a structure.
|
||||
Let's define at least one element. */
|
||||
#ifdef __cplusplus
|
||||
# define DEQUEI_MIN_ARRAY_SIZE 1
|
||||
#else
|
||||
# define DEQUEI_MIN_ARRAY_SIZE
|
||||
#endif
|
||||
|
||||
/* Define the internal contract of a deque */
|
||||
#define DEQUEI_CONTRACT(d) do { \
|
||||
assert ((d) != NULL); \
|
||||
assert ((d)->default_size >= DEQUEUI_DEFAULT_SIZE); \
|
||||
@@ -67,29 +63,55 @@
|
||||
(d)->back->index - (d)->front->index == (d)->count); \
|
||||
} while (0)
|
||||
|
||||
#define DEQUEI_DEF(arg) DEQUEI_DEF2 arg
|
||||
/* Deferred evaluation for the deque definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define DEQUEI_DEF_P1(arg) DEQUEI_DEF_P2 arg
|
||||
|
||||
#define DEQUEI_DEF2(name, type, oplist, deque_t, it_t, node_t) \
|
||||
/* Internal deque definition
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- deque_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define DEQUEI_DEF_P2(name, type, oplist, deque_t, it_t, node_t) \
|
||||
\
|
||||
/* It is a linked list of buckets of the types, \
|
||||
each new created bucket size grows compared to the previous one \
|
||||
resulting in: \
|
||||
strict O(1) for push/pop \
|
||||
O(ln(n)) for random access. \
|
||||
No insert / delete operations are planned. \
|
||||
[Could be done in O(n) complexity if needed] \
|
||||
Define the bucket (aka node) structure. \
|
||||
*/ \
|
||||
typedef struct M_C(name, _node_s) { \
|
||||
ILIST_INTERFACE(M_C(name, _node_list), struct M_C(name, _node_s)); \
|
||||
size_t size; \
|
||||
type data[DEQUEI_MIN_ARRAY_SIZE]; \
|
||||
type data[M_MIN_FLEX_ARRAY_SIZE]; \
|
||||
} node_t; \
|
||||
\
|
||||
/* Each node is allocated with a variable size (so we use \
|
||||
M_GET_REALLOC for the allocation). But we need to delete the nodes \
|
||||
\
|
||||
/* Each node is allocated with a variable size bucket (so we use \
|
||||
M_GET_REALLOC for the allocation). But we want to delete the nodes \
|
||||
automatically with the intrusive list used for storing the nodes: \
|
||||
so we register as a DEL operator the FREE operator of the oplist. \
|
||||
The interfaces are compatible. \
|
||||
*/ \
|
||||
ILIST_DEF(M_C(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \
|
||||
\
|
||||
/* Define an internal iterator */ \
|
||||
typedef struct M_C(name, _it2_s) { \
|
||||
node_t *node; \
|
||||
size_t index; \
|
||||
} M_C(name, _it2_t)[1]; \
|
||||
\
|
||||
/* Define the deque type: \
|
||||
- 'list' if the list of buckets containing the objects. \
|
||||
- 'front' and 'back' are iterators to the first and last elements. \
|
||||
- 'default_size' is the size used for the creation of a new bucket \
|
||||
- 'count' is the number of elements in the container. \
|
||||
*/ \
|
||||
typedef struct M_C(name, _s) { \
|
||||
M_C(name, _node_list_t) list; \
|
||||
M_C(name, _it2_t) front; \
|
||||
@@ -97,6 +119,7 @@
|
||||
size_t default_size; \
|
||||
size_t count; \
|
||||
} deque_t[1]; \
|
||||
\
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
@@ -112,19 +135,23 @@
|
||||
M_C(name, _int_new_node)(deque_t d) \
|
||||
{ \
|
||||
size_t def = d->default_size; \
|
||||
/* Test for overflow of the size computation */ \
|
||||
if (M_UNLIKELY (def > SIZE_MAX / sizeof (type) - sizeof(M_C(name, _node_t)))) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
|
||||
return NULL; \
|
||||
} \
|
||||
/* Alloc a new node */ \
|
||||
M_C(name, _node_t)*n = (M_C(name, _node_t)*) (void*) \
|
||||
M_CALL_REALLOC(oplist, char, NULL, \
|
||||
M_CALL_REALLOC(oplist, char, NULL, \
|
||||
sizeof(M_C(name, _node_t)) + def * sizeof(type) ); \
|
||||
if (n==NULL) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
|
||||
return NULL; \
|
||||
} \
|
||||
/* Initialize the node */ \
|
||||
n->size = def; \
|
||||
M_C(name, _node_list_init_field)(n); \
|
||||
/* Increase the next bucket allocation */ \
|
||||
/* Do not increase it too much if there are few items */ \
|
||||
def = M_MIN(def, d->count); \
|
||||
d->default_size = M_CALL_INC_ALLOC(oplist, def); \
|
||||
@@ -192,8 +219,10 @@
|
||||
M_C(name, _node_t) *n = d->back->node; \
|
||||
size_t index = d->back->index; \
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
/* try to get an already allocated node */ \
|
||||
n = M_C(name, _node_list_next_obj)(d->list, n); \
|
||||
if (n == NULL) { \
|
||||
/* No node exists, allocate a new one */ \
|
||||
n = M_C(name, _int_new_node)(d); \
|
||||
if (M_UNLIKELY (n == NULL)) return NULL; \
|
||||
M_C(name, _node_list_push_back)(d->list, n); \
|
||||
@@ -859,11 +888,63 @@
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no IN_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, const deque_t deque) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
bool first_done = false; \
|
||||
ret = f->m_interface->write_array_start(local, f, deque->count); \
|
||||
M_C(name, _it_t) it; \
|
||||
for (M_C(name, _it)(it, deque) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
type const *item = M_C(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(deque_t deque, m_serial_read_t f) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
M_C(name,_clean)(deque); \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_C(name, _push_back)(deque, item); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no IN_SERIAL & INIT */ ) \
|
||||
\
|
||||
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define DEQUEI_OPLIST_P1(arg) DEQUEI_OPLIST_P2 arg
|
||||
|
||||
#define DEQUEI_OPLIST(arg) DEQUEI_OPLIST2 arg
|
||||
|
||||
#define DEQUEI_OPLIST2(name, oplist) \
|
||||
/* OPLIST definition of a deque */
|
||||
#define DEQUEI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
@@ -888,6 +969,7 @@
|
||||
,IT_REF(M_C(name,_ref)) \
|
||||
,IT_CREF(M_C(name,_cref)) \
|
||||
,CLEAN(M_C(name,_clean)) \
|
||||
,GET_SIZE(M_C(name, _size)) \
|
||||
,PUSH(M_C(name,_push_back)) \
|
||||
,POP(M_C(name,_pop_back)) \
|
||||
,OPLIST(oplist) \
|
||||
@@ -895,6 +977,8 @@
|
||||
,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),) \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - DICTIONARY Module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -29,7 +29,8 @@
|
||||
#include "m-array.h"
|
||||
#include "m-tuple.h"
|
||||
|
||||
/* Define a dictionary with the key key_type to the value value_type and its associated functions.
|
||||
|
||||
/* Define a dictionary associating the key key_type to the value value_type and its associated functions.
|
||||
USAGE:
|
||||
DICT_DEF2(name, key_type, key_oplist, value_type, value_oplist)
|
||||
OR
|
||||
@@ -41,7 +42,7 @@
|
||||
(name, key_type, __VA_ARGS__)))
|
||||
|
||||
|
||||
/* Define a dictionary with the key key_type to the value value_type and its associated functions.
|
||||
/* Define a dictionary asssociating the key key_type to the value value_type and its associated functions.
|
||||
It stores the computed hash value, avoiding the need of recomputing it but increasing memory
|
||||
consumption.
|
||||
USAGE:
|
||||
@@ -55,19 +56,9 @@
|
||||
(name, key_type, __VA_ARGS__)))
|
||||
|
||||
|
||||
/* Define a set with the key key_type and its associated functions.
|
||||
The set is unordered.
|
||||
USAGE: DICT_SET_DEF(name, key_type[, key_oplist])
|
||||
*/
|
||||
#define DICT_SET_DEF(name, ...) \
|
||||
DICTI_SET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), \
|
||||
(name, __VA_ARGS__)))
|
||||
|
||||
|
||||
/* Define a dictionary with the key key_type to the value value_type
|
||||
with an Open Addressing implementation and its associated functions.
|
||||
KEY_OPLIST needs the operators OOR_EQUAL & OOR_SET.
|
||||
/* Define a dictionary associating the key key_type to the value value_type
|
||||
with an Open Addressing implementation and its associated functions.
|
||||
KEY_OPLIST needs the operators OOR_EQUAL & OOR_SET.
|
||||
USAGE:
|
||||
DICT_OA_DEF2(name, key_type, key_oplist, value_type, value_oplist)
|
||||
OR
|
||||
@@ -79,6 +70,27 @@
|
||||
(name, key_type, __VA_ARGS__)))
|
||||
|
||||
|
||||
/* Define a set of the key key_type and its associated functions.
|
||||
The set is unordered.
|
||||
USAGE: DICT_SET_DEF(name, key_type[, key_oplist])
|
||||
*/
|
||||
#define DICT_SET_DEF(name, ...) \
|
||||
DICTI_SET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), \
|
||||
(name, __VA_ARGS__)))
|
||||
|
||||
|
||||
/* Define a set of the key key_type
|
||||
with an Open Addressing implementation and its associated functions.
|
||||
The set is unordered.
|
||||
USAGE: DICT_OASET_DEF(name, key_type[, key_oplist])
|
||||
*/
|
||||
#define DICT_OASET_DEF(name, ...) \
|
||||
DICTI_OASET_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), \
|
||||
(name, __VA_ARGS__)))
|
||||
|
||||
|
||||
/* Define the oplist of a dictionnary (DICT_DEF2, DICT_STOREHASH_DEF2 or DICT_OA_DEF2).
|
||||
USAGE:
|
||||
DICT_OPLIST(name, oplist of the key type, oplist of the value type)
|
||||
@@ -87,16 +99,16 @@
|
||||
*/
|
||||
#define DICT_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(DICTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST ), \
|
||||
DICTI_OPLIST(__VA_ARGS__ ))
|
||||
(DICTI_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST )), \
|
||||
DICTI_OPLIST_P1((__VA_ARGS__ )))
|
||||
|
||||
|
||||
/* Define the oplist of a dictionnary (DICT_SET_DEF).
|
||||
USAGE: DICT_SET_OPLIST(name[, oplist of the key type]) */
|
||||
#define DICT_SET_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(DICTI_SET_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
DICTI_SET_OPLIST(__VA_ARGS__ ))
|
||||
(DICTI_SET_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST)), \
|
||||
DICTI_SET_OPLIST_P1((__VA_ARGS__ )))
|
||||
|
||||
|
||||
|
||||
@@ -178,14 +190,15 @@
|
||||
LIST_OPLIST(M_C(name, _list_pair), TUPLE_OPLIST(M_C(name, _pair), key_oplist))) \
|
||||
\
|
||||
DICTI_FUNC_DEF2(name, key_type, key_oplist, key_type, \
|
||||
(INIT(M_EMPTY_DEFAULT), INIT_SET(M_EMPTY_DEFAULT), SET(M_EMPTY_DEFAULT), CLEAR(M_EMPTY_DEFAULT), \
|
||||
EQUAL(M_EMPTY_DEFAULT), GET_STR(M_EMPTY_DEFAULT), OUT_STR(M_EMPTY_DEFAULT), IN_STR(M_TRUE_DEFAULT), PARSE_STR(M_TRUE_DEFAULT)), \
|
||||
1, 0, M_C(name, _t), M_C(name, _it_t))
|
||||
M_EMPTY_OPLIST, 1, 0, M_C(name, _t), M_C(name, _it_t))
|
||||
|
||||
|
||||
/* Define the structure and a chained dictionnary */
|
||||
/* Define the structure of a chained dictionnary */
|
||||
#define DICTI_FUNC_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, isStoreHash, dict_t, dict_it_t) \
|
||||
\
|
||||
/* NOTE: \
|
||||
if isSet is true, all methods of value_oplist are NOP methods */ \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
size_t used, lower_limit, upper_limit; \
|
||||
M_C(name, _array_list_pair_t) table; \
|
||||
@@ -519,6 +532,13 @@
|
||||
M_C(name, _list_pair_it_set)(it->list_it, ref->list_it); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(dict_it_t it, const dict_t d) \
|
||||
{ \
|
||||
DICTI_CONTRACT(name, d); \
|
||||
M_C(name, _array_list_pair_it_end)(it->array_it, d->table); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const dict_it_t it) \
|
||||
{ \
|
||||
@@ -556,7 +576,7 @@
|
||||
{ \
|
||||
assert (it1 != NULL && it2 != NULL); \
|
||||
return M_C(name, _list_pair_it_equal_p)(it1->list_it, \
|
||||
it2->list_it); \
|
||||
it2->list_it); \
|
||||
} \
|
||||
\
|
||||
static inline M_C(name, _type_t) * \
|
||||
@@ -590,7 +610,7 @@
|
||||
assert (dict1 != NULL && dict2 != NULL); \
|
||||
/* NOTE: Key type has mandatory equal operator */ \
|
||||
/* First the easy cases */ \
|
||||
if (M_LIKELY (dict1->used != dict2-> used)) \
|
||||
if (M_LIKELY (dict1->used != dict2->used)) \
|
||||
return false; \
|
||||
if (M_UNLIKELY (dict1->used == 0)) \
|
||||
return true; \
|
||||
@@ -603,34 +623,23 @@
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)) { \
|
||||
const M_C(name, _type_t) *item = M_C(name, _cref)(it); \
|
||||
M_IF(isSet)( \
|
||||
value_type *ptr = M_C(name, _get)(dict2, *item); \
|
||||
if (ptr == NULL) \
|
||||
return false; \
|
||||
, \
|
||||
value_type *ptr = M_C(name, _get)(dict2, item->key); \
|
||||
if (ptr == NULL) \
|
||||
return false; \
|
||||
if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \
|
||||
return false; \
|
||||
) \
|
||||
} \
|
||||
value_type *ptr = M_C(name, _get)(dict2, M_IF(isSet)(*item, item->key)); \
|
||||
if (ptr == NULL) \
|
||||
return false; \
|
||||
if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \
|
||||
return false; \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
, /* no value equal */ ) \
|
||||
\
|
||||
DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t)
|
||||
|
||||
/* Define additional functions for dictionnary.
|
||||
|
||||
/* Define additional functions for dictionnary (Common for all kinds of dictionnary).
|
||||
Do not used any fields of the dictionnary but the public API */
|
||||
#define DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t) \
|
||||
\
|
||||
static inline bool M_ATTR_DEPRECATED \
|
||||
M_C(name, _remove)(dict_t map, key_type const key) \
|
||||
{ \
|
||||
return M_C(name, _erase)(map, key); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, const dict_t dict, const bool append) \
|
||||
@@ -704,7 +713,7 @@
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
M_CALL_INIT(value_oplist, value); \
|
||||
do { \
|
||||
while (isspace(*str)) str++; \
|
||||
while (isspace((int) *str)) str++; \
|
||||
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
|
||||
M_IF(isSet)( \
|
||||
if (b == false) { break; } \
|
||||
@@ -740,9 +749,11 @@
|
||||
if (M_UNLIKELY (c == EOF)) return true; \
|
||||
ungetc(c, file); \
|
||||
key_type key; \
|
||||
M_IF(isSet)( ,value_type value); \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
M_CALL_INIT(value_oplist, value); \
|
||||
M_IF(isSet)(, \
|
||||
value_type value; \
|
||||
M_CALL_INIT(value_oplist, value); \
|
||||
) \
|
||||
do { \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
ungetc(c, file); \
|
||||
@@ -760,11 +771,98 @@
|
||||
) \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
} while (c == ','); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_CALL_CLEAR(value_oplist, value); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_IF(isSet)(, \
|
||||
M_CALL_CLEAR(value_oplist, value); \
|
||||
) \
|
||||
return c == '}'; \
|
||||
} \
|
||||
, /* no IN_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, dict_t const t1) \
|
||||
{ \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
const M_C(name, _type_t) *item; \
|
||||
bool first_done = false; \
|
||||
dict_it_t it; \
|
||||
/* Format is different between associative container \
|
||||
& set container */ \
|
||||
M_IF(isSet)( \
|
||||
ret = f->m_interface->write_array_start(local, f, M_C(name, _size)(t1)); \
|
||||
for (M_C(name, _it)(it, t1) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
item = M_C(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
, \
|
||||
ret = f->m_interface->write_map_start(local, f, M_C(name, _size)(t1)); \
|
||||
for (M_C(name, _it)(it, t1) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
item = M_C(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_map_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(key_oplist, f, item->key); \
|
||||
ret |= f->m_interface->write_map_value(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(value_oplist, f, item->value); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_map_end(local, f); \
|
||||
) \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(dict_t t1, m_serial_read_t f) \
|
||||
{ \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
key_type key; \
|
||||
M_C(name,_clean)(t1); \
|
||||
M_IF(isSet)( \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_C(name, _push)(t1, key); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
, \
|
||||
value_type value; \
|
||||
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
M_CALL_INIT (value_oplist, value); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
||||
ret = f->m_interface->read_map_value(local, f); \
|
||||
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
|
||||
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
||||
M_C(name, _set_at)(t1, key, value); \
|
||||
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_CALL_CLEAR(value_oplist, value); \
|
||||
) /* End of IF isSet */ \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no in_serial */ ) \
|
||||
\
|
||||
M_IF(isSet)( \
|
||||
static inline void \
|
||||
@@ -813,8 +911,14 @@
|
||||
Or if the table of the dictionnary has different values (this may \
|
||||
be avoided). */
|
||||
|
||||
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define DICTI_OPLIST_P1(arg) DICTI_OPLIST_P2 arg
|
||||
|
||||
/* Define the oplist of a dictionnary */
|
||||
#define DICTI_OPLIST(name, key_oplist, value_oplist) \
|
||||
#define DICTI_OPLIST_P2(name, key_oplist, value_oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
SET(M_C(name, _set)), \
|
||||
@@ -829,6 +933,7 @@
|
||||
IT_TYPE(M_C(name, _it_t)), \
|
||||
IT_FIRST(M_C(name,_it)), \
|
||||
IT_SET(M_C(name, _it_set)), \
|
||||
IT_END(M_C(name,_it_end)), \
|
||||
IT_END_P(M_C(name,_end_p)), \
|
||||
IT_LAST_P(M_C(name,_last_p)), \
|
||||
IT_NEXT(M_C(name,_next)), \
|
||||
@@ -842,19 +947,25 @@
|
||||
,ERASE_KEY(M_C(name, _erase)) \
|
||||
,KEY_OPLIST(key_oplist) \
|
||||
,VALUE_OPLIST(value_oplist) \
|
||||
,GET_SIZE(M_C(name, _size)) \
|
||||
,M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)(GET_STR(M_C(name, _get_str)),) \
|
||||
,M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_C(name, _parse_str)),) \
|
||||
,M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_C(name, _out_str)),) \
|
||||
,M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_C(name, _in_str)),) \
|
||||
,M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
|
||||
,M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
|
||||
,M_IF_METHOD(EQUAL, value_oplist)(EQUAL(M_C(name, _equal_p)),) \
|
||||
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW key_oplist),) \
|
||||
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC key_oplist),) \
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL key_oplist),) \
|
||||
)
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define DICTI_SET_OPLIST_P1(arg) DICTI_SET_OPLIST_P2 arg
|
||||
|
||||
/* Define the oplist of a set */
|
||||
#define DICTI_SET_OPLIST(name, oplist) \
|
||||
#define DICTI_SET_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
INIT_WITH(API_1(M_INIT_VAI)), \
|
||||
@@ -875,9 +986,11 @@
|
||||
ERASE_KEY(M_C(name, _erase)), \
|
||||
KEY_OPLIST(oplist), \
|
||||
VALUE_OPLIST(oplist), \
|
||||
GET_SIZE(M_C(name, _size)), \
|
||||
IT_TYPE(M_C(name, _it_t)), \
|
||||
IT_FIRST(M_C(name,_it)), \
|
||||
IT_SET(M_C(name, _it_set)), \
|
||||
IT_END(M_C(name,_it_end)), \
|
||||
IT_END_P(M_C(name,_end_p)), \
|
||||
IT_LAST_P(M_C(name,_last_p)), \
|
||||
IT_NEXT(M_C(name,_next)), \
|
||||
@@ -888,15 +1001,28 @@
|
||||
,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)),) \
|
||||
,EQUAL(M_C(name, _equal_p)), \
|
||||
,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),) \
|
||||
)
|
||||
|
||||
/* Define Lower Bound for hash table (TODO: Common macro for both implementation) */
|
||||
#ifndef DICTI_LOWER_BOUND
|
||||
#define DICTI_LOWER_BOUND(s) ((s) <= DICTI_INITIAL_SIZE ? 0 : (s) * 1 / 4)
|
||||
#endif
|
||||
|
||||
/* Define Lower Bound for hash table (TODO: Common macro for both implementation) */
|
||||
#ifndef DICTI_UPPER_BOUND
|
||||
#define DICTI_UPPER_BOUND(s) ((s) * 2 / 3)
|
||||
#endif
|
||||
|
||||
/* Define initial size of the hash table */
|
||||
#ifndef DICTI_INITIAL_SIZE
|
||||
#define DICTI_INITIAL_SIZE 16
|
||||
#endif
|
||||
|
||||
#define DICTI_CONTRACT(name, map) do { \
|
||||
assert(map != NULL); \
|
||||
@@ -921,6 +1047,15 @@ typedef enum {
|
||||
#define DICTI_OA_PROBING(s) ((s)++)
|
||||
#endif
|
||||
|
||||
/* Lower Bound of the hash table (TODO: Common macro for both dictionnary) */
|
||||
#ifndef DICTI_OA_LOWER_BOUND
|
||||
#define DICTI_OA_LOWER_BOUND 0.2
|
||||
#endif
|
||||
/* Upper Bound of the hash table (TODO: Common macro for both dictionnary) */
|
||||
#ifndef DICTI_OA_UPPER_BOUND
|
||||
#define DICTI_OA_UPPER_BOUND 0.7
|
||||
#endif
|
||||
|
||||
#define DICTI_OA_CONTRACT(dict) do { \
|
||||
assert ( (dict) != NULL); \
|
||||
assert( (dict)->lower_limit <= (dict)->count && (dict)->count <= (dict)->upper_limit ); \
|
||||
@@ -932,18 +1067,32 @@ typedef enum {
|
||||
|
||||
#define DICTI_OA_DEF_P1(args) DICTI_OA_DEF_P2 args
|
||||
#define DICTI_OA_DEF_P2(name, key_type, key_oplist, value_type, value_oplist) \
|
||||
DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, \
|
||||
M_GET_OOR_EQUAL key_oplist, M_GET_OOR_SET key_oplist, \
|
||||
0.2, 0.7, M_C(name,_t), M_C(name, _it_t) )
|
||||
DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, 0, \
|
||||
DICTI_OA_LOWER_BOUND, DICTI_OA_UPPER_BOUND, M_C(name,_t), M_C(name, _it_t) )
|
||||
|
||||
#define DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, oor_equal_p, oor_set, coeff_down, coeff_up, dict_t, dict_it_t) \
|
||||
#define DICTI_OASET_DEF_P1(args) DICTI_OASET_DEF_P2 args
|
||||
#define DICTI_OASET_DEF_P2(name, key_type, key_oplist) \
|
||||
DICTI_OA_DEF_P3(name, key_type, key_oplist, key_type, M_EMPTY_OPLIST, 1, \
|
||||
DICTI_OA_LOWER_BOUND, DICTI_OA_UPPER_BOUND, M_C(name,_t), M_C(name, _it_t) )
|
||||
|
||||
#define DICTI_OA_DEF_P3(name, key_type, key_oplist, value_type, value_oplist, isSet, coeff_down, coeff_up, dict_t, dict_it_t) \
|
||||
\
|
||||
/* NOTE: \
|
||||
if isSet is true, all methods of value_oplist are NOP methods */ \
|
||||
\
|
||||
typedef struct M_C(name, _pair_s) { \
|
||||
key_type key; \
|
||||
value_type value; \
|
||||
M_IF(isSet)( , value_type value;) \
|
||||
} M_C(name, _pair_t); \
|
||||
\
|
||||
/* NOTE: We don't want a real oplist for this type */ \
|
||||
M_IF(isSet)( \
|
||||
typedef key_type M_C(name, _type_t); \
|
||||
, \
|
||||
typedef struct M_C(name, _pair_s) M_C(name, _type_t); ) \
|
||||
typedef key_type M_C(name, _key_type_t); \
|
||||
typedef value_type M_C(name, _value_type_t); \
|
||||
\
|
||||
/* NOTE: We don't want a real oplist for this sub type */ \
|
||||
ARRAY_DEF(M_C(name, _array_pair), M_C(name, _pair_t), \
|
||||
(INIT(M_NOTHING_DEFAULT), SET(M_MEMCPY_DEFAULT), \
|
||||
INIT_SET(M_MEMCPY_DEFAULT), CLEAR(M_NOTHING_DEFAULT))) \
|
||||
@@ -955,7 +1104,6 @@ typedef enum {
|
||||
} dict_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
typedef struct M_C(name, _pair_s) M_C(name, _type_t); \
|
||||
\
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
const struct M_C(name,_s) *dict; \
|
||||
@@ -979,11 +1127,11 @@ typedef enum {
|
||||
M_C(name,_int_limit)(dict, DICTI_INITIAL_SIZE); \
|
||||
dict->data = M_CALL_REALLOC(key_oplist, M_C(name, _pair_t), NULL, DICTI_INITIAL_SIZE); \
|
||||
if (dict->data == NULL) { \
|
||||
M_MEMORY_FULL(sizeof (M_C(name, _pair_t)) * DICTI_INITIAL_SIZE); \
|
||||
M_MEMORY_FULL(sizeof (M_C(name, _pair_t)) * DICTI_INITIAL_SIZE); \
|
||||
return ; \
|
||||
} \
|
||||
for(size_t i = 0; i < DICTI_INITIAL_SIZE; i++) { \
|
||||
oor_set(dict->data[i].key, DICTI_OA_EMPTY); \
|
||||
M_CALL_OOR_SET(key_oplist, dict->data[i].key, DICTI_OA_EMPTY); \
|
||||
} \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
} \
|
||||
@@ -993,8 +1141,8 @@ typedef enum {
|
||||
{ \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
for(size_t i = 0; i <= dict->mask; i++) { \
|
||||
if (!oor_equal_p(dict->data[i].key, DICTI_OA_EMPTY) \
|
||||
&& !oor_equal_p(dict->data[i].key, DICTI_OA_DELETED)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, DICTI_OA_EMPTY) \
|
||||
&& !M_CALL_OOR_EQUAL(key_oplist, dict->data[i].key, DICTI_OA_DELETED)) { \
|
||||
M_CALL_CLEAR(key_oplist, dict->data[i].key); \
|
||||
M_CALL_CLEAR(value_oplist, dict->data[i].value); \
|
||||
} \
|
||||
@@ -1010,8 +1158,8 @@ typedef enum {
|
||||
{ \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
/* NOTE: Key can not be the representation of empty or deleted */ \
|
||||
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
|
||||
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
|
||||
\
|
||||
M_C(name, _pair_t) *const data = dict->data; \
|
||||
const size_t mask = dict->mask; \
|
||||
@@ -1019,8 +1167,8 @@ typedef enum {
|
||||
\
|
||||
/* Random access, and probably cache miss */ \
|
||||
if (M_LIKELY (M_CALL_EQUAL(key_oplist, data[p].key, key)) ) \
|
||||
return &data[p].value; \
|
||||
else if (M_LIKELY (oor_equal_p (data[p].key, DICTI_OA_EMPTY)) ) \
|
||||
return &data[p].M_IF(isSet)(key, value); \
|
||||
else if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) ) \
|
||||
return NULL; \
|
||||
\
|
||||
/* Unlikely case */ \
|
||||
@@ -1028,9 +1176,9 @@ typedef enum {
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) \
|
||||
return &data[p].value; \
|
||||
return &data[p].M_IF(isSet)(key, value); \
|
||||
assert (s <= dict->mask); \
|
||||
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
|
||||
\
|
||||
return NULL; \
|
||||
} \
|
||||
@@ -1050,8 +1198,8 @@ typedef enum {
|
||||
size_t empty = 0; \
|
||||
size_t del = 0; \
|
||||
for(size_t i = 0 ; i <= h->mask ; i++) { \
|
||||
empty += oor_equal_p(data[i].key, DICTI_OA_EMPTY); \
|
||||
del += oor_equal_p(data[i].key, DICTI_OA_DELETED); \
|
||||
empty += M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY); \
|
||||
del += M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED); \
|
||||
} \
|
||||
assert(del == 0); \
|
||||
assert(empty + h->count == h->mask + 1); \
|
||||
@@ -1076,7 +1224,7 @@ typedef enum {
|
||||
\
|
||||
/* First mark the extended space as empty */ \
|
||||
for(size_t i = oldSize ; i < newSize; i++) \
|
||||
oor_set(data[i].key, DICTI_OA_EMPTY); \
|
||||
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
|
||||
} \
|
||||
\
|
||||
/* Then let's rehash all the entries in their **exact** position. \
|
||||
@@ -1089,10 +1237,10 @@ typedef enum {
|
||||
const size_t mask = (newSize -1); \
|
||||
\
|
||||
for(size_t i = 0 ; i < oldSize; i++) { \
|
||||
if (!oor_equal_p(data[i].key, DICTI_OA_DELETED) && !oor_equal_p(data[i].key, DICTI_OA_EMPTY)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED) && !M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) { \
|
||||
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
|
||||
if (p != i) { \
|
||||
if (M_LIKELY (oor_equal_p(data[p].key, DICTI_OA_EMPTY) || oor_equal_p(data[p].key, DICTI_OA_DELETED))) { \
|
||||
if (M_LIKELY (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) || M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED))) { \
|
||||
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
|
||||
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
|
||||
} else { \
|
||||
@@ -1100,30 +1248,28 @@ typedef enum {
|
||||
M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
|
||||
M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
|
||||
} \
|
||||
oor_set(data[i].key, DICTI_OA_EMPTY); \
|
||||
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
|
||||
} \
|
||||
} else { \
|
||||
oor_set(data[i].key, DICTI_OA_EMPTY); \
|
||||
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* Let's put back the entries in the tmp array in their right place */ \
|
||||
/* NOTE: There should be very few entries in this array \
|
||||
which contains what we weren't be able to fit in the first pass */ \
|
||||
for(size_t i = 0; i < M_C(name, _array_pair_size)(tmp); i++) { \
|
||||
M_C(name, _pair_t) *item = M_C(name, _array_pair_get)(tmp, i); \
|
||||
while (M_C(name, _array_pair_size)(tmp) > 0) { \
|
||||
M_C(name, _pair_t) const *item = M_C(name, _array_pair_back)(tmp); \
|
||||
size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \
|
||||
/* NOTE: since the first pass, the bucket might be free now */ \
|
||||
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
|
||||
size_t s = 1; \
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
assert (s <= h->mask); \
|
||||
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} \
|
||||
/* FIXME: How can I use INIT_MOVE without garbaging the array? ==> POP_INIT_MOVE ! */ \
|
||||
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
|
||||
M_CALL_INIT_SET(value_oplist, data[p].value, item->value); \
|
||||
M_C(name, _array_pair_pop_move)(&data[p], tmp); \
|
||||
} \
|
||||
\
|
||||
M_C(name, _array_pair_clear) (tmp); \
|
||||
@@ -1138,13 +1284,14 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name,_set_at)(dict_t dict, key_type const key, \
|
||||
value_type const value) \
|
||||
M_IF(isSet)(M_C(name, _push), M_C(name,_set_at)) \
|
||||
(dict_t dict, key_type const key \
|
||||
M_IF(isSet)(, M_DEFERRED_COMMA value_type const value) ) \
|
||||
{ \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
/* NOTE: key can not be the representation of empty or deleted */ \
|
||||
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
|
||||
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
|
||||
\
|
||||
M_C(name, _pair_t) *const data = dict->data; \
|
||||
const size_t mask = dict->mask; \
|
||||
@@ -1155,9 +1302,9 @@ typedef enum {
|
||||
M_CALL_SET(value_oplist, data[p].value, value); \
|
||||
return; \
|
||||
} \
|
||||
if (M_UNLIKELY (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) ) { \
|
||||
if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) ) { \
|
||||
size_t delPos = -1; \
|
||||
if (oor_equal_p(data[p].key, DICTI_OA_DELETED)) delPos = p; \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) delPos = p; \
|
||||
size_t s = 1; \
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
@@ -1166,8 +1313,8 @@ typedef enum {
|
||||
return; \
|
||||
} \
|
||||
assert (s <= dict->mask); \
|
||||
if (oor_equal_p(data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
|
||||
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
|
||||
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
|
||||
if (delPos != (size_t) -1) { \
|
||||
p = delPos; \
|
||||
dict->count_delete --; \
|
||||
@@ -1197,28 +1344,28 @@ typedef enum {
|
||||
{ \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
/* NOTE: key can not be the representation of empty or deleted */ \
|
||||
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
|
||||
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
|
||||
\
|
||||
M_C(name, _pair_t) *const data = dict->data; \
|
||||
const size_t mask = dict->mask; \
|
||||
size_t p = M_CALL_HASH(key_oplist, key) & mask; \
|
||||
\
|
||||
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \
|
||||
return &data[p].value; \
|
||||
return &data[p].M_IF(isSet)(key, value); \
|
||||
} \
|
||||
if (M_UNLIKELY (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) ) { \
|
||||
if (M_UNLIKELY (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) ) { \
|
||||
size_t delPos = -1; \
|
||||
if (oor_equal_p(data[p].key, DICTI_OA_DELETED)) delPos = p; \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) delPos = p; \
|
||||
size_t s = 1; \
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
if (M_CALL_EQUAL(key_oplist, data[p].key, key)) { \
|
||||
return &data[p].value; \
|
||||
return &data[p].M_IF(isSet)(key, value); \
|
||||
} \
|
||||
assert (s <= dict->mask); \
|
||||
if (oor_equal_p(data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
|
||||
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED) && delPos == (size_t)-1) delPos = p; \
|
||||
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
|
||||
if (delPos != (size_t) -1) { \
|
||||
p = delPos; \
|
||||
dict->count_delete --; \
|
||||
@@ -1243,7 +1390,7 @@ typedef enum {
|
||||
return M_C(name, _get)(dict, key); \
|
||||
} \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
return &data[p].value; \
|
||||
return &data[p].M_IF(isSet)(key, value); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -1260,15 +1407,15 @@ typedef enum {
|
||||
\
|
||||
/* Pass 1: scan lower entries, and move them if needed */ \
|
||||
for(size_t i = 0; i < newSize; i++) { \
|
||||
if (oor_equal_p(data[i].key, DICTI_OA_EMPTY)) \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) \
|
||||
continue; \
|
||||
if (oor_equal_p(data[i].key, DICTI_OA_DELETED)) { \
|
||||
oor_set(data[i].key, DICTI_OA_EMPTY); \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED)) { \
|
||||
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
|
||||
continue; \
|
||||
} \
|
||||
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
|
||||
if (p != i) { \
|
||||
if (oor_equal_p(data[p].key, DICTI_OA_EMPTY) || oor_equal_p(data[p].key, DICTI_OA_DELETED)) { \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) || M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_DELETED)) { \
|
||||
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
|
||||
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
|
||||
} else { \
|
||||
@@ -1276,39 +1423,38 @@ typedef enum {
|
||||
M_DO_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
|
||||
M_DO_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
|
||||
} \
|
||||
oor_set(data[i].key, DICTI_OA_EMPTY); \
|
||||
M_CALL_OOR_SET(key_oplist, data[i].key, DICTI_OA_EMPTY); \
|
||||
} \
|
||||
} \
|
||||
/* Pass 2: scan upper entries and move them back */ \
|
||||
for(size_t i = newSize; i < oldSize; i++) { \
|
||||
if (!oor_equal_p(data[i].key, DICTI_OA_DELETED) \
|
||||
&& !oor_equal_p(data[i].key, DICTI_OA_EMPTY)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_DELETED) \
|
||||
&& !M_CALL_OOR_EQUAL(key_oplist, data[i].key, DICTI_OA_EMPTY)) { \
|
||||
size_t p = M_CALL_HASH(key_oplist, data[i].key) & mask; \
|
||||
assert (p < i); \
|
||||
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
|
||||
size_t s = 1; \
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
assert (s <= h->mask); \
|
||||
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} \
|
||||
M_DO_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
|
||||
M_DO_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
|
||||
} \
|
||||
} \
|
||||
/* Pass 3: scan moved entries and move them back */ \
|
||||
for(size_t i = 0; i < M_C(name, _array_pair_size)(tmp); i++) { \
|
||||
M_C(name, _pair_t) *item = M_C(name, _array_pair_get)(tmp, i); \
|
||||
while (M_C(name, _array_pair_size)(tmp) > 0) { \
|
||||
M_C(name, _pair_t) const *item = M_C(name, _array_pair_back)(tmp); \
|
||||
size_t p = M_CALL_HASH(key_oplist, item->key) & mask; \
|
||||
if (!oor_equal_p(data[p].key, DICTI_OA_EMPTY)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) { \
|
||||
size_t s = 1; \
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
assert (s <= h->mask); \
|
||||
} while (!oor_equal_p(data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} while (!M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ); \
|
||||
} \
|
||||
M_CALL_INIT_SET(key_oplist, data[p].key, item->key); \
|
||||
M_CALL_INIT_SET(value_oplist, data[p].value, item->value); \
|
||||
M_C(name, _array_pair_pop_move)(&data[p], tmp); \
|
||||
} \
|
||||
\
|
||||
M_C(name, _array_pair_clear) (tmp); \
|
||||
@@ -1329,28 +1475,28 @@ typedef enum {
|
||||
{ \
|
||||
DICTI_OA_CONTRACT(dict); \
|
||||
/* NOTE: key can't be the representation of empty or deleted */ \
|
||||
assert (!oor_equal_p(key, DICTI_OA_EMPTY)); \
|
||||
assert (!oor_equal_p(key, DICTI_OA_DELETED)); \
|
||||
\
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_EMPTY)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, key, DICTI_OA_DELETED)); \
|
||||
\
|
||||
M_C(name, _pair_t) *const data = dict->data; \
|
||||
const size_t mask = dict->mask; \
|
||||
size_t p = M_CALL_HASH(key_oplist, key) & mask; \
|
||||
\
|
||||
/* Random access, and probably cache miss */ \
|
||||
if (M_UNLIKELY (!M_CALL_EQUAL(key_oplist, data[p].key, key)) ) { \
|
||||
if (oor_equal_p (data[p].key, DICTI_OA_EMPTY)) \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY)) \
|
||||
return false; \
|
||||
size_t s = 1; \
|
||||
do { \
|
||||
p = (p + DICTI_OA_PROBING(s)) & mask; \
|
||||
if (oor_equal_p(data[p].key, DICTI_OA_EMPTY) ) \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, data[p].key, DICTI_OA_EMPTY) ) \
|
||||
return false; \
|
||||
assert (s <= dict->mask); \
|
||||
} while (!M_CALL_EQUAL(key_oplist, data[p].key, key)); \
|
||||
} \
|
||||
M_CALL_CLEAR(key_oplist, data[p].key); \
|
||||
M_CALL_CLEAR(value_oplist, data[p].value); \
|
||||
oor_set (data[p].key, DICTI_OA_DELETED); \
|
||||
M_CALL_OOR_SET(key_oplist, data[p].key, DICTI_OA_DELETED); \
|
||||
assert (dict->count >= 1); \
|
||||
dict->count--; \
|
||||
if (M_UNLIKELY (dict->count < dict->lower_limit)) { \
|
||||
@@ -1390,10 +1536,10 @@ typedef enum {
|
||||
return ; \
|
||||
} \
|
||||
for(size_t i = 0; i <= org->mask; i++) { \
|
||||
if (oor_equal_p(org->data[i].key, DICTI_OA_EMPTY)) { \
|
||||
oor_set(map->data[i].key, DICTI_OA_EMPTY); \
|
||||
} else if (oor_equal_p(org->data[i].key, DICTI_OA_DELETED)) { \
|
||||
oor_set(map->data[i].key, DICTI_OA_DELETED); \
|
||||
if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, DICTI_OA_EMPTY)) { \
|
||||
M_CALL_OOR_SET(key_oplist, map->data[i].key, DICTI_OA_EMPTY); \
|
||||
} else if (M_CALL_OOR_EQUAL(key_oplist, org->data[i].key, DICTI_OA_DELETED)) { \
|
||||
M_CALL_OOR_SET(key_oplist, map->data[i].key, DICTI_OA_DELETED); \
|
||||
} else { \
|
||||
M_CALL_INIT_SET(key_oplist, map->data[i].key, org->data[i].key); \
|
||||
M_CALL_INIT_SET(value_oplist, map->data[i].value, org->data[i].value); \
|
||||
@@ -1463,8 +1609,8 @@ typedef enum {
|
||||
{ \
|
||||
DICTI_OA_CONTRACT(d); \
|
||||
for(size_t i = 0; i <= d->mask; i++) { \
|
||||
if (!oor_equal_p(d->data[i].key, DICTI_OA_EMPTY) \
|
||||
&& !oor_equal_p(d->data[i].key, DICTI_OA_DELETED)) { \
|
||||
if (!M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|
||||
&& !M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) { \
|
||||
M_CALL_CLEAR(key_oplist, d->data[i].key); \
|
||||
M_CALL_CLEAR(value_oplist, d->data[i].value); \
|
||||
} \
|
||||
@@ -1477,7 +1623,7 @@ typedef enum {
|
||||
d->data, DICTI_INITIAL_SIZE); \
|
||||
assert(d->data != NULL); \
|
||||
for(size_t i = 0; i <= d->mask; i++) { \
|
||||
oor_set(d->data[i].key, DICTI_OA_EMPTY); \
|
||||
M_CALL_OOR_SET(key_oplist, d->data[i].key, DICTI_OA_EMPTY); \
|
||||
} \
|
||||
DICTI_OA_CONTRACT(d); \
|
||||
} \
|
||||
@@ -1489,8 +1635,8 @@ typedef enum {
|
||||
assert (it != NULL); \
|
||||
it->dict = d; \
|
||||
size_t i = 0; \
|
||||
while ((oor_equal_p (d->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| oor_equal_p (d->data[i].key, DICTI_OA_DELETED)) \
|
||||
while ((M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) \
|
||||
&& i <= d->mask) { \
|
||||
i++; \
|
||||
} \
|
||||
@@ -1514,8 +1660,8 @@ typedef enum {
|
||||
assert (it != NULL); \
|
||||
it->dict = d; \
|
||||
size_t i = d->mask; \
|
||||
while ((oor_equal_p (d->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| oor_equal_p (d->data[i].key, DICTI_OA_DELETED)) \
|
||||
while ((M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| M_CALL_OOR_EQUAL(key_oplist, d->data[i].key, DICTI_OA_DELETED)) \
|
||||
&& i <= d->mask) { \
|
||||
i--; \
|
||||
} \
|
||||
@@ -1546,8 +1692,8 @@ typedef enum {
|
||||
DICTI_OA_CONTRACT (it->dict); \
|
||||
size_t i = it->index + 1; \
|
||||
while (i <= it->dict->mask && \
|
||||
(oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED))) { \
|
||||
(M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED))) { \
|
||||
i++; \
|
||||
} \
|
||||
it->index = i; \
|
||||
@@ -1561,8 +1707,8 @@ typedef enum {
|
||||
/* if index was 0, the operation will overflow, and stops the loop */ \
|
||||
size_t i = it->index - 1; \
|
||||
while (i <= it->dict->mask && \
|
||||
(oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED))) { \
|
||||
(M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY) \
|
||||
|| M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED))) { \
|
||||
i--; \
|
||||
} \
|
||||
it->index = i; \
|
||||
@@ -1587,22 +1733,22 @@ typedef enum {
|
||||
return it1->dict == it2->dict && it1->index == it2->index; \
|
||||
} \
|
||||
\
|
||||
static inline struct M_C(name, _pair_s) * \
|
||||
static inline M_C(name, _type_t) * \
|
||||
M_C(name, _ref)(const dict_it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
DICTI_OA_CONTRACT (it -> dict); \
|
||||
const size_t i = it->index; \
|
||||
assert (i <= it->dict->mask); \
|
||||
assert (!oor_equal_p (it->dict->data[i].key, DICTI_OA_EMPTY)); \
|
||||
assert (!oor_equal_p (it->dict->data[i].key, DICTI_OA_DELETED)); \
|
||||
return &it->dict->data[i]; \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_EMPTY)); \
|
||||
assert (!M_CALL_OOR_EQUAL(key_oplist, it->dict->data[i].key, DICTI_OA_DELETED)); \
|
||||
return &it->dict->data[i] M_IF(isSet)(.key, ); \
|
||||
} \
|
||||
\
|
||||
static inline const struct M_C(name, _pair_s) * \
|
||||
static inline const M_C(name, _type_t) * \
|
||||
M_C(name, _cref)(const dict_it_t it) \
|
||||
{ \
|
||||
return M_CONST_CAST(struct M_C(name, _pair_s), M_C(name, _ref)(it)); \
|
||||
return M_CONST_CAST(M_C(name, _type_t), M_C(name, _ref)(it)); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -1612,6 +1758,7 @@ typedef enum {
|
||||
size_t size; \
|
||||
/* Get the size which will allow to fit this capacity */ \
|
||||
size = m_core_roundpow2 (capacity * (1.0 / coeff_up)); \
|
||||
/* Test for overflow of the computation */ \
|
||||
if (M_UNLIKELY (size < capacity)) { \
|
||||
M_MEMORY_FULL((size_t)-1); \
|
||||
} \
|
||||
@@ -1640,20 +1787,18 @@ typedef enum {
|
||||
for(M_C(name, _it)(it, dict1) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)) { \
|
||||
const struct M_C(name, _pair_s) *item = M_C(name, _cref)(it); \
|
||||
value_type *ptr = M_C(name, _get)(dict2, item->key); \
|
||||
const M_C(name, _type_t) *item = M_C(name, _cref)(it); \
|
||||
value_type *ptr = M_C(name, _get)(dict2, M_IF(isSet)(*item, item->key)); \
|
||||
if (ptr == NULL) \
|
||||
return false; \
|
||||
M_IF(isSet)(, \
|
||||
if (M_CALL_EQUAL(value_oplist, item->value, *ptr) == false) \
|
||||
return false; \
|
||||
) \
|
||||
return false; \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
, /* no value equal */ ) \
|
||||
\
|
||||
DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, 0, dict_t, dict_it_t)
|
||||
DICTI_FUNC_ADDITIONAL_DEF2(name, key_type, key_oplist, value_type, value_oplist, isSet, dict_t, dict_it_t)
|
||||
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - GENINT module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
|
||||
+32
-32
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Intrusive List module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,15 +46,15 @@
|
||||
USAGE:
|
||||
ILIST_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define ILIST_DEF(name, ...) \
|
||||
ILISTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name, _t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name, _t), M_C(name, _it_t) )))
|
||||
ILISTI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name, _t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name, _t), M_C(name, _it_t) )))
|
||||
|
||||
/* Define the oplist of a doubly-linked instrusive list of type.
|
||||
USAGE:
|
||||
ILIST_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define ILIST_OPLIST(...) \
|
||||
ILISTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
ILISTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
@@ -68,10 +68,10 @@ typedef struct ilist_head_s {
|
||||
} ilist_head_t;
|
||||
|
||||
/* Indirection call to allow expanding all arguments */
|
||||
#define ILISTI_OPLIST(arg) ILISTI_OPLIST2 arg
|
||||
#define ILISTI_OPLIST_P1(arg) ILISTI_OPLIST_P2 arg
|
||||
|
||||
/* Define the oplist of an ilist of type */
|
||||
#define ILISTI_OPLIST2(name,oplist) \
|
||||
#define ILISTI_OPLIST_P2(name,oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
INIT_MOVE(M_C(name, _init_move)), \
|
||||
@@ -120,7 +120,7 @@ typedef struct ilist_head_s {
|
||||
} while (0)
|
||||
|
||||
/* Indirection call to allow expanding all arguments (TBC) */
|
||||
#define ILISTI_DEF(arg) ILISTI_DEF2 arg
|
||||
#define ILISTI_DEF_P1(arg) ILISTI_DEF_P2 arg
|
||||
|
||||
/* Definition of the type and function for an intrusive doubly-linked list.
|
||||
USAGE:
|
||||
@@ -128,9 +128,9 @@ typedef struct ilist_head_s {
|
||||
type: type of the object
|
||||
oplist: oplist of the type
|
||||
list_t: type of the intrusive list (name##_t)
|
||||
list_it_t: iterator of the intrusive list (name##_it_t)
|
||||
it_t: iterator of the intrusive list (name##_it_t)
|
||||
*/
|
||||
#define ILISTI_DEF2(name, type, oplist, list_t, list_it_t) \
|
||||
#define ILISTI_DEF_P2(name, type, oplist, list_t, it_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct ilist_head_s name; \
|
||||
@@ -145,7 +145,7 @@ typedef struct ilist_head_s {
|
||||
struct ilist_head_s *previous; \
|
||||
struct ilist_head_s *current; \
|
||||
struct ilist_head_s *next; \
|
||||
} list_it_t[1]; \
|
||||
} it_t[1]; \
|
||||
\
|
||||
static inline void M_C(name, _init)(list_t list) \
|
||||
{ \
|
||||
@@ -316,7 +316,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(list_it_t it, const list_t list) \
|
||||
M_C(name, _it)(it_t it, const list_t list) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
assert (it != NULL); \
|
||||
@@ -328,7 +328,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(list_it_t it, const list_it_t cit) \
|
||||
M_C(name, _it_set)(it_t it, const it_t cit) \
|
||||
{ \
|
||||
assert (it != NULL && cit != NULL); \
|
||||
it->head = cit->head; \
|
||||
@@ -339,30 +339,30 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_last)(list_it_t it, list_t list) \
|
||||
M_C(name, _it_last)(it_t it, list_t const list) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
assert (it != NULL); \
|
||||
it->head = &list->name; \
|
||||
it->head = list->name.next->prev; \
|
||||
it->current = list->name.prev; \
|
||||
it->next = &list->name; \
|
||||
it->next = it->head; \
|
||||
it->previous = list->name.prev->prev; \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(list_it_t it, list_t list) \
|
||||
M_C(name, _it_end)(it_t it, list_t const list) \
|
||||
{ \
|
||||
assert (it != NULL && list != NULL); \
|
||||
it->head = &list->name; \
|
||||
it->current = &list->name; \
|
||||
it->head = list->name.next->prev; \
|
||||
it->current = it->head; \
|
||||
it->next = list->name.next; \
|
||||
it->previous = list->name.prev; \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const list_it_t it) \
|
||||
M_C(name, _end_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
@@ -370,7 +370,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _last_p)(const list_it_t it) \
|
||||
M_C(name, _last_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
@@ -378,7 +378,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _next)(list_it_t it) \
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
/* Cannot check node for it->current: it may have been deleted! */ \
|
||||
@@ -393,7 +393,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _previous)(list_it_t it) \
|
||||
M_C(name, _previous)(it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
/* Cannot check node for it->current: it may have been deleted! */ \
|
||||
@@ -408,7 +408,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2 ) \
|
||||
M_C(name, _it_equal_p)(const it_t it1, const it_t it2 ) \
|
||||
{ \
|
||||
assert (it1 != NULL && it2 != NULL); \
|
||||
/* No need to check for next & previous */ \
|
||||
@@ -416,7 +416,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _ref)(const list_it_t it) \
|
||||
M_C(name, _ref)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL && it->current != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
@@ -429,14 +429,14 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const list_it_t it) \
|
||||
M_C(name, _cref)(const it_t it) \
|
||||
{ \
|
||||
type *ptr = M_C(name, _ref)(it); \
|
||||
return M_CONST_CAST(type, ptr); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove)(list_t list, list_it_t it) \
|
||||
M_C(name, _remove)(list_t list, it_t it) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
@@ -451,7 +451,7 @@ typedef struct ilist_head_s {
|
||||
\
|
||||
M_IF_METHOD(NEW, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _insert)(list_t list, list_it_t it, type x) \
|
||||
M_C(name, _insert)(list_t list, it_t it, type x) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
@@ -508,7 +508,7 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_back)(list_t nv, list_t ov, list_it_t it) \
|
||||
M_C(name, _splice_back)(list_t nv, list_t ov, it_t it) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, nv); \
|
||||
ILISTI_CONTRACT(name, ov); \
|
||||
@@ -524,8 +524,8 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
|
||||
list_t olist, list_it_t opos) \
|
||||
M_C(name, _splice_at)(list_t nlist, it_t npos, \
|
||||
list_t olist, it_t opos) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, nlist); \
|
||||
ILISTI_CONTRACT(name, olist); \
|
||||
@@ -590,7 +590,7 @@ typedef struct ilist_head_s {
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
} \
|
||||
\
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
|
||||
|
||||
|
||||
#endif
|
||||
+14
-11
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - INTRUSIVE SHARED PTR Module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,9 +31,9 @@
|
||||
/* Define the oplist of a intrusive shared pointer.
|
||||
USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define ISHARED_PTR_OPLIST(...) \
|
||||
ISHAREDI_PTR_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
ISHAREDI_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
/* Interface to add to a structure to allow intrusive support.
|
||||
name: name of the intrusive shared pointer.
|
||||
@@ -45,16 +45,18 @@
|
||||
/* Define the intrusive shared pointer type and its static inline functions.
|
||||
USAGE: ISHARED_PTR_DEF(name[, oplist]) */
|
||||
#define ISHARED_PTR_DEF(name, ...) \
|
||||
ISHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
ISHAREDI_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define ISHAREDI_PTR_OPLIST(arg) ISHAREDI_PTR_OPLIST2 arg
|
||||
// Deferred evaluation
|
||||
#define ISHAREDI_PTR_OPLIST_P1(arg) ISHAREDI_PTR_OPLIST_P2 arg
|
||||
|
||||
#define ISHAREDI_PTR_OPLIST2(name, oplist) ( \
|
||||
// Define the oplist
|
||||
#define ISHAREDI_PTR_OPLIST_P2(name, oplist) ( \
|
||||
INIT(M_INIT_DEFAULT), \
|
||||
INIT_SET(M_C(name, _init_set2) M_IPTR), \
|
||||
SET(M_C(name, _set) M_IPTR), \
|
||||
@@ -68,9 +70,10 @@
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
#define ISHAREDI_PTR_DEF(arg) ISHAREDI_PTR_DEF2 arg
|
||||
// Deferred evaluatioin
|
||||
#define ISHAREDI_PTR_DEF_P1(arg) ISHAREDI_PTR_DEF_P2 arg
|
||||
|
||||
#define ISHAREDI_PTR_DEF2(name, type, oplist) \
|
||||
#define ISHAREDI_PTR_DEF_P2(name, type, oplist) \
|
||||
\
|
||||
typedef type *M_C(name,_t); \
|
||||
typedef type M_C(name, _type_t); \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - LIST module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,30 +30,33 @@
|
||||
/* Define a singly linked list of a given type.
|
||||
USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define LIST_DEF(name, ...) \
|
||||
LISTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
|
||||
LISTI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
|
||||
|
||||
/* Define a dual-push singly linked list of a given type.
|
||||
USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define LIST_DUAL_PUSH_DEF(name, ...) \
|
||||
LISTI_DUAL_PUSH_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
|
||||
LISTI_DUAL_PUSH_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) )))
|
||||
|
||||
/* Define the oplist of a list of type.
|
||||
USAGE: LIST_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define LIST_OPLIST(...) \
|
||||
LISTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
(__VA_ARGS__ )))
|
||||
LISTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define LISTI_OPLIST(arg) LISTI_OPLIST2 arg
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define LISTI_OPLIST_P1(arg) LISTI_OPLIST_P2 arg
|
||||
|
||||
#define LISTI_OPLIST2(name, oplist) \
|
||||
/* OPLIST definition of a list and list_dual_push */
|
||||
#define LISTI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
INIT_WITH(API_1(M_INIT_VAI)), \
|
||||
@@ -90,6 +93,8 @@
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_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),) \
|
||||
@@ -97,7 +102,9 @@
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
#define LISTI_DEF(arg) LISTI_DEF2 arg
|
||||
/* Deferred evaluation for the list definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define LISTI_DEF_P1(arg) LISTI_DEF_P2 arg
|
||||
|
||||
/* Define allocation functions. If MEMPOOL, we need to define it */
|
||||
#define LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
|
||||
@@ -122,7 +129,16 @@
|
||||
} \
|
||||
) \
|
||||
|
||||
#define LISTI_DEF2(name, type, oplist, list_t, list_it_t) \
|
||||
|
||||
/* Internal list definition
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- list_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define LISTI_DEF_P2(name, type, oplist, list_t, it_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *next; \
|
||||
@@ -136,17 +152,29 @@
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
struct M_C(name, _s) *previous; \
|
||||
struct M_C(name, _s) *current; \
|
||||
} list_it_t[1]; \
|
||||
} it_t[1]; \
|
||||
\
|
||||
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
|
||||
LISTI_DEF3(name, type, oplist, list_t, list_it_t) \
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
|
||||
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \
|
||||
LISTI_DEF_P3(name, type, oplist, list_t, it_t) \
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
|
||||
|
||||
|
||||
/* Define the internal contract of a list
|
||||
(there is nothing worthy to be checked) */
|
||||
#define LISTI_CONTRACT(v) do { \
|
||||
assert (v != NULL); \
|
||||
} while (0)
|
||||
|
||||
#define LISTI_DEF3(name, type, oplist, list_t, list_it_t) \
|
||||
|
||||
/* Internal list function definition
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- list_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define LISTI_DEF_P3(name, type, oplist, list_t, it_t) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(list_t v) \
|
||||
@@ -280,7 +308,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(list_it_t it, const list_t v) \
|
||||
M_C(name, _it)(it_t it, const list_t v) \
|
||||
{ \
|
||||
LISTI_CONTRACT(v); \
|
||||
assert (it != NULL); \
|
||||
@@ -289,7 +317,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(list_it_t it1, const list_it_t it2) \
|
||||
M_C(name, _it_set)(it_t it1, const it_t it2) \
|
||||
{ \
|
||||
assert (it1 != NULL && it2 != NULL); \
|
||||
it1->current = it2->current; \
|
||||
@@ -297,7 +325,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(list_it_t it1, const list_t v) \
|
||||
M_C(name, _it_end)(it_t it1, const list_t v) \
|
||||
{ \
|
||||
LISTI_CONTRACT(v); \
|
||||
assert (it1 != NULL); \
|
||||
@@ -307,21 +335,21 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const list_it_t it) \
|
||||
M_C(name, _end_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
return it->current == NULL; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _last_p)(const list_it_t it) \
|
||||
M_C(name, _last_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
return it->current == NULL || it->current->next == NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _next)(list_it_t it) \
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->current != NULL); \
|
||||
it->previous = it->current; \
|
||||
@@ -329,21 +357,21 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2) \
|
||||
M_C(name, _it_equal_p)(const it_t it1, const it_t it2) \
|
||||
{ \
|
||||
assert(it1 != NULL && it2 != NULL); \
|
||||
return it1->current == it2->current; \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _ref)(const list_it_t it) \
|
||||
M_C(name, _ref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->current != NULL); \
|
||||
return &(it->current->data); \
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const list_it_t it) \
|
||||
M_C(name, _cref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->current != NULL); \
|
||||
return M_CONST_CAST(type, &(it->current->data)); \
|
||||
@@ -363,7 +391,7 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _sublist_p)(const list_t list, const list_it_t itsub) \
|
||||
M_C(name, _sublist_p)(const list_t list, const it_t itsub) \
|
||||
{ \
|
||||
LISTI_CONTRACT(list); \
|
||||
assert (itsub != NULL); \
|
||||
@@ -399,7 +427,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _insert)(list_t list, list_it_t insertion_point, \
|
||||
M_C(name, _insert)(list_t list, it_t insertion_point, \
|
||||
type const x) \
|
||||
{ \
|
||||
LISTI_CONTRACT(list); \
|
||||
@@ -423,7 +451,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove)(list_t list, list_it_t removing_point) \
|
||||
M_C(name, _remove)(list_t list, it_t removing_point) \
|
||||
{ \
|
||||
LISTI_CONTRACT(list); \
|
||||
assert (removing_point != NULL); \
|
||||
@@ -491,7 +519,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_back)(list_t nv, list_t ov, list_it_t it) \
|
||||
M_C(name, _splice_back)(list_t nv, list_t ov, it_t it) \
|
||||
{ \
|
||||
LISTI_CONTRACT(nv); \
|
||||
LISTI_CONTRACT(ov); \
|
||||
@@ -515,8 +543,8 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
|
||||
list_t olist, list_it_t opos) \
|
||||
M_C(name, _splice_at)(list_t nlist, it_t npos, \
|
||||
list_t olist, it_t opos) \
|
||||
{ \
|
||||
LISTI_CONTRACT(nlist); \
|
||||
LISTI_CONTRACT(olist); \
|
||||
@@ -580,7 +608,18 @@
|
||||
*list = previous; \
|
||||
} \
|
||||
|
||||
#define LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t) \
|
||||
|
||||
/* Internal list function definition based on iterator functions
|
||||
(common for all kind of lists)
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- list_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, const list_t list, \
|
||||
@@ -676,6 +715,56 @@
|
||||
} \
|
||||
, /* no IN_STR & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, const list_t list) \
|
||||
{ \
|
||||
assert (list != NULL); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
bool first_done = false; \
|
||||
ret = f->m_interface->write_array_start(local, f, 0); \
|
||||
M_C(name, _it_t) it; \
|
||||
for (M_C(name, _it)(it, list) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
type const *item = M_C(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(list_t list, m_serial_read_t f) \
|
||||
{ \
|
||||
assert (list != NULL); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
size_t estimated_size = 0; \
|
||||
M_C(name,_clean)(list); \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_C(name, _push_back)(list, item); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
M_C(name, _reverse)(list); \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no IN_SERIAL & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _equal_p)(const list_t list1, const list_t list2) \
|
||||
@@ -718,20 +807,31 @@
|
||||
} \
|
||||
, /* no hash */ ) \
|
||||
|
||||
/* Dual Push singly linked list.
|
||||
Support Push Back / Push Front / Pop back in O(1).
|
||||
Doesn't support Pop front.
|
||||
This is done by keeping a pointer to both back & front
|
||||
*/
|
||||
#define LISTI_DUAL_PUSH_DEF(arg) LISTI_DUAL_PUSH_DEF2 arg
|
||||
|
||||
#define LISTI_DUAL_PUSH_DEF2(name, type, oplist, list_t, list_it_t) \
|
||||
/* Deferred evaluation for the dual-push list definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define LISTI_DUAL_PUSH_DEF_P1(arg) LISTI_DUAL_PUSH_DEF_P2 arg
|
||||
|
||||
/* Internal dual-push list definition
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- list_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define LISTI_DUAL_PUSH_DEF_P2(name, type, oplist, list_t, it_t) \
|
||||
\
|
||||
struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *next; \
|
||||
type data; \
|
||||
}; \
|
||||
\
|
||||
/* Dual Push singly linked list. \
|
||||
Support Push Back / Push Front / Pop back in O(1). \
|
||||
Doesn't support Pop front. \
|
||||
This is done by keeping a pointer to both back & front \
|
||||
*/ \
|
||||
typedef struct M_C(name, _head_s) { \
|
||||
struct M_C(name, _s) *front; \
|
||||
struct M_C(name, _s) *back; \
|
||||
@@ -744,19 +844,28 @@
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
struct M_C(name, _s) *previous; \
|
||||
struct M_C(name, _s) *current; \
|
||||
} list_it_t[1]; \
|
||||
} it_t[1]; \
|
||||
\
|
||||
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \
|
||||
LISTI_DUAL_PUSH_DEF3(name, type, oplist, list_t, list_it_t) \
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
|
||||
LISTI_MEMPOOL_DEF(name, type, oplist, list_t, it_t) \
|
||||
LISTI_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, it_t)
|
||||
|
||||
/* Define the internal contract of an dual-push list */
|
||||
#define LISTI_DUAL_PUSH_CONTRACT(l) do { \
|
||||
assert (l != NULL); \
|
||||
assert ( (l->back == NULL && l->front == NULL) \
|
||||
|| (l->back != NULL && l->front != NULL)); \
|
||||
} while (0)
|
||||
|
||||
#define LISTI_DUAL_PUSH_DEF3(name, type, oplist, list_t, list_it_t) \
|
||||
/* Internal dual-push list definition
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- list_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define LISTI_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(list_t v) \
|
||||
@@ -972,7 +1081,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(list_it_t it, const list_t v) \
|
||||
M_C(name, _it)(it_t it, const list_t v) \
|
||||
{ \
|
||||
LISTI_DUAL_PUSH_CONTRACT(v); \
|
||||
assert (it != NULL); \
|
||||
@@ -981,7 +1090,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(list_it_t it1, const list_it_t it2) \
|
||||
M_C(name, _it_set)(it_t it1, const it_t it2) \
|
||||
{ \
|
||||
assert (it1 != NULL && it2 != NULL); \
|
||||
it1->current = it2->current; \
|
||||
@@ -989,7 +1098,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(list_it_t it1, const list_t v) \
|
||||
M_C(name, _it_end)(it_t it1, const list_t v) \
|
||||
{ \
|
||||
assert (it1 != NULL); \
|
||||
LISTI_DUAL_PUSH_CONTRACT(v); \
|
||||
@@ -999,21 +1108,21 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const list_it_t it) \
|
||||
M_C(name, _end_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
return it->current == NULL; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _last_p)(const list_it_t it) \
|
||||
M_C(name, _last_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
return it->current == NULL || it->current->next == NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _next)(list_it_t it) \
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->current != NULL); \
|
||||
it->previous = it->current; \
|
||||
@@ -1021,21 +1130,21 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2) \
|
||||
M_C(name, _it_equal_p)(const it_t it1, const it_t it2) \
|
||||
{ \
|
||||
assert(it1 != NULL && it2 != NULL); \
|
||||
return it1->current == it2->current; \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _ref)(const list_it_t it) \
|
||||
M_C(name, _ref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->current != NULL); \
|
||||
return &(it->current->data); \
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const list_it_t it) \
|
||||
M_C(name, _cref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->current != NULL); \
|
||||
return M_CONST_CAST(type, &(it->current->data)); \
|
||||
@@ -1055,7 +1164,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _insert)(list_t list, list_it_t insertion_point, \
|
||||
M_C(name, _insert)(list_t list, it_t insertion_point, \
|
||||
type const x) \
|
||||
{ \
|
||||
LISTI_DUAL_PUSH_CONTRACT(list); \
|
||||
@@ -1084,7 +1193,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove)(list_t list, list_it_t removing_point) \
|
||||
M_C(name, _remove)(list_t list, it_t removing_point) \
|
||||
{ \
|
||||
LISTI_DUAL_PUSH_CONTRACT(list); \
|
||||
assert (removing_point != NULL); \
|
||||
@@ -1159,7 +1268,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_back)(list_t list1, list_t list2, list_it_t it) \
|
||||
M_C(name, _splice_back)(list_t list1, list_t list2, it_t it) \
|
||||
{ \
|
||||
LISTI_DUAL_PUSH_CONTRACT(list1); \
|
||||
LISTI_DUAL_PUSH_CONTRACT(list2); \
|
||||
@@ -1191,8 +1300,8 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
|
||||
list_t olist, list_it_t opos) \
|
||||
M_C(name, _splice_at)(list_t nlist, it_t npos, \
|
||||
list_t olist, it_t opos) \
|
||||
{ \
|
||||
LISTI_DUAL_PUSH_CONTRACT(nlist); \
|
||||
LISTI_DUAL_PUSH_CONTRACT(olist); \
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - MEMPOOL module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Thin Mutex & Thread wrapper
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -42,7 +42,7 @@ typedef thrd_t m_thread_t[1];
|
||||
static inline void m_mutex_init(m_mutex_t m)
|
||||
{
|
||||
int rc = mtx_init(m, mtx_plain);
|
||||
M_ASSERT_INIT (rc == 0);
|
||||
M_ASSERT_INIT (rc == 0, "mutex");
|
||||
}
|
||||
|
||||
static inline void m_mutex_clear(m_mutex_t m)
|
||||
@@ -63,7 +63,7 @@ static inline void m_mutex_unlock(m_mutex_t m)
|
||||
static inline void m_cond_init(m_cond_t c)
|
||||
{
|
||||
int rc = cnd_init(c);
|
||||
M_ASSERT_INIT (rc == 0);
|
||||
M_ASSERT_INIT (rc == 0, "conditional variable");
|
||||
}
|
||||
|
||||
static inline void m_cond_clear(m_cond_t c)
|
||||
@@ -89,13 +89,15 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
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);
|
||||
M_ASSERT_INIT (rc == thrd_success);
|
||||
M_ASSERT_INIT (rc == thrd_success, "thread");
|
||||
}
|
||||
|
||||
static inline void m_thread_join(m_thread_t t)
|
||||
{
|
||||
int rc = thrd_join(*t, NULL);
|
||||
assert (rc == thrd_success);
|
||||
// Avoid warning about variable unused.
|
||||
(void) rc;
|
||||
}
|
||||
|
||||
static inline void m_thread_yield(void)
|
||||
@@ -120,6 +122,7 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
|
||||
call_once(o,func);
|
||||
}
|
||||
|
||||
#define M_THREAD_ATTR _Thread_local
|
||||
|
||||
|
||||
// MSYS2 doesn't define _WIN32 by default.
|
||||
@@ -190,8 +193,8 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
|
||||
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
|
||||
{
|
||||
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, arg, 0, NULL);
|
||||
M_ASSERT_INIT (*t != NULL);
|
||||
*t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (uintptr_t) func, arg, 0, NULL);
|
||||
M_ASSERT_INIT (*t != NULL, "thread");
|
||||
}
|
||||
|
||||
static inline void m_thread_join(m_thread_t t)
|
||||
@@ -212,7 +215,7 @@ static inline bool m_thread_sleep(unsigned long long usec)
|
||||
LARGE_INTEGER ft;
|
||||
ft.QuadPart = -(10ULL*usec);
|
||||
HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
M_ASSERT_INIT (hd != NULL);
|
||||
M_ASSERT_INIT (hd != NULL, "timer");
|
||||
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
|
||||
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
|
||||
CloseHandle(hd);
|
||||
@@ -237,6 +240,12 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
|
||||
InitOnceExecuteOnce(o, m_oncei_callback, (void*)(intptr_t)func, NULL);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define M_THREAD_ATTR __declspec( thread )
|
||||
#else
|
||||
# define M_THREAD_ATTR __thread
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/**************************** PTHREAD version ******************************/
|
||||
@@ -259,7 +268,7 @@ typedef pthread_t m_thread_t[1];
|
||||
static inline void m_mutex_init(m_mutex_t m)
|
||||
{
|
||||
int _rc = pthread_mutex_init(m, NULL);
|
||||
M_ASSERT_INIT (_rc == 0);
|
||||
M_ASSERT_INIT (_rc == 0, "mutex");
|
||||
}
|
||||
|
||||
static inline void m_mutex_clear(m_mutex_t m)
|
||||
@@ -286,7 +295,7 @@ static inline void m_mutexi_lazy_lock(m_mutex_t m)
|
||||
static inline void m_cond_init(m_cond_t c)
|
||||
{
|
||||
int _rc = pthread_cond_init(c, NULL);
|
||||
M_ASSERT_INIT (_rc == 0);
|
||||
M_ASSERT_INIT (_rc == 0, "conditional variable");
|
||||
}
|
||||
|
||||
static inline void m_cond_clear(m_cond_t c)
|
||||
@@ -312,7 +321,7 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
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);
|
||||
M_ASSERT_INIT (_rc == 0, "thread");
|
||||
}
|
||||
|
||||
static inline void m_thread_join(m_thread_t t)
|
||||
@@ -348,8 +357,13 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
|
||||
pthread_once(o,func);
|
||||
}
|
||||
|
||||
#define M_THREAD_ATTR __thread
|
||||
|
||||
#endif
|
||||
|
||||
// TODO: m_thread_sleep doesn't yield thread during waiting, making it a poor choice for active waiting.
|
||||
// TODO: Obsolete M_LOCK macro.
|
||||
|
||||
/* M_LOCK macro. Allow simple locking encapsulation.
|
||||
USAGE:
|
||||
static M_LOCK_DECL(name);
|
||||
|
||||
+35
-19
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - dynamic priority queue module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,24 +33,29 @@
|
||||
/* Define a prioqueue of a given type and its associated functions.
|
||||
USAGE: PRIOQUEUE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define PRIOQUEUE_DEF(name, ...) \
|
||||
PRIOQUEUEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
PRIOQUEUEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
|
||||
|
||||
/* Define the oplist of a prioqueue of type.
|
||||
USAGE: PRIOQUEUE_OPLIST(name[, oplist of the type]) */
|
||||
#define PRIOQUEUE_OPLIST(...) \
|
||||
PRIOQUEUEI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
PRIOQUEUEI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define PRIOQUEUEI_OPLIST(arg) PRIOQUEUEI_OPLIST2 arg
|
||||
#define PRIOQUEUEI_DEF(arg) PRIOQUEUEI_DEF2 arg
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define PRIOQUEUEI_OPLIST_P1(arg) PRIOQUEUEI_OPLIST_P2 arg
|
||||
#define PRIOQUEUEI_DEF_P1(arg) PRIOQUEUEI_DEF_P2 arg
|
||||
|
||||
#define PRIOQUEUEI_OPLIST2(name, oplist) \
|
||||
/* Define oplist of a priority queue */
|
||||
#define PRIOQUEUEI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
@@ -65,9 +70,18 @@
|
||||
,PUSH(M_C(name,_push)) \
|
||||
,POP(M_C(name,_pop)) \
|
||||
,OPLIST(oplist) \
|
||||
,TEST_EMPTY(M_C(name, _empty_p)), \
|
||||
,GET_SIZE(M_C(name, _size)) \
|
||||
)
|
||||
|
||||
#define PRIOQUEUEI_DEF2(name, type, oplist, prioqueue_t, it_t) \
|
||||
/* Define the priority queue:
|
||||
- name: prefix to use,
|
||||
- type: type of the contained objects,
|
||||
- oplist: oplist of the contained objects,
|
||||
- prioqueue_t: type of the container,
|
||||
- it_t: iterator of the container
|
||||
*/
|
||||
#define PRIOQUEUEI_DEF_P2(name, type, oplist, prioqueue_t, it_t) \
|
||||
ARRAY_DEF(M_C(name, _array), type, oplist) \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
@@ -87,13 +101,13 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init_set)(prioqueue_t p, prioqueue_t o) \
|
||||
M_C(name, _init_set)(prioqueue_t p, prioqueue_t const o) \
|
||||
{ \
|
||||
M_C(name, _array_init_set)(p->array, o->array); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _set)(prioqueue_t p, prioqueue_t o) \
|
||||
M_C(name, _set)(prioqueue_t p, prioqueue_t const o) \
|
||||
{ \
|
||||
M_C(name, _array_set)(p->array, o->array); \
|
||||
} \
|
||||
@@ -157,13 +171,13 @@
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _empty_p)(const prioqueue_t p) \
|
||||
M_C(name, _empty_p)(prioqueue_t const p) \
|
||||
{ \
|
||||
return M_C(name, _array_empty_p)(p->array); \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _size)(const prioqueue_t p) \
|
||||
M_C(name, _size)(prioqueue_t const p) \
|
||||
{ \
|
||||
return M_C(name, _array_size)(p->array); \
|
||||
} \
|
||||
@@ -184,7 +198,7 @@
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _front)(prioqueue_t p) \
|
||||
M_C(name, _front)(prioqueue_t const p) \
|
||||
{ \
|
||||
return M_C(name, _array_cget)(p->array, 0); \
|
||||
} \
|
||||
@@ -212,19 +226,19 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(it_t it, const prioqueue_t v) \
|
||||
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, const prioqueue_t v) \
|
||||
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, const prioqueue_t v) \
|
||||
M_C(name, _it_end)(it_t it, prioqueue_t const v) \
|
||||
{ \
|
||||
M_C(name, _array_it_end)(it, v->array); \
|
||||
} \
|
||||
@@ -279,6 +293,7 @@
|
||||
M_C(name, _erase)(prioqueue_t p, type x) \
|
||||
{ \
|
||||
/* First pass: search for an item EQUAL to x */ \
|
||||
/* NOTE: An HASHMAP may be a good idea to optimize this pass. */ \
|
||||
size_t size = M_C(name, _array_size)(p->array); \
|
||||
size_t i = 0; \
|
||||
for(i = 0; i < size; i++) { \
|
||||
@@ -342,5 +357,6 @@
|
||||
, /* No EQUAL */ \) \
|
||||
|
||||
|
||||
// TODO: set all & remove all function
|
||||
|
||||
#endif
|
||||
+126
-43
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - RED BLACK TREE module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,26 +30,28 @@
|
||||
/* Define a Red/Black binary tree of a given type.
|
||||
USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define RBTREE_DEF(name, ...) \
|
||||
RBTREEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
RBTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), \
|
||||
M_C(name, _t), struct M_C(name, _node_s), M_C(name, _it_t)), \
|
||||
M_C(name, _t), M_C(name, _node_t), M_C(name, _it_t)), \
|
||||
(name, __VA_ARGS__ , \
|
||||
M_C(name, _t), struct M_C(name, _node_s), M_C(name, _it_t))))
|
||||
M_C(name, _t), M_C(name, _node_t), M_C(name, _it_t))))
|
||||
|
||||
/* Define the oplist of a rbtree of type.
|
||||
USAGE: RBTREE_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define RBTREE_OPLIST(...) \
|
||||
RBTREEI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
RBTREEI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// deferred evaluation
|
||||
#define RBTREEI_OPLIST(arg) RBTREEI_OPLIST2 arg
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define RBTREEI_OPLIST_P1(arg) RBTREEI_OPLIST_P2 arg
|
||||
|
||||
#define RBTREEI_OPLIST2(name, oplist) \
|
||||
/* OPLIST definition of a rbtree */
|
||||
#define RBTREEI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
INIT_WITH(API_1(M_INIT_VAI)), \
|
||||
@@ -61,6 +63,7 @@
|
||||
TYPE(M_C(name,_t)), \
|
||||
SUBTYPE(M_C(name, _type_t)), \
|
||||
TEST_EMPTY(M_C(name,_empty_p)), \
|
||||
GET_SIZE(M_C(name, _size)), \
|
||||
IT_TYPE(M_C(name, _it_t)), \
|
||||
IT_FIRST(M_C(name,_it)), \
|
||||
IT_SET(M_C(name,_it_set)), \
|
||||
@@ -80,6 +83,8 @@
|
||||
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),) \
|
||||
@@ -127,16 +132,25 @@ typedef enum {
|
||||
|
||||
//TODO: UPDATE shall use a separate method than push
|
||||
|
||||
// deferred evaluation
|
||||
#define RBTREEI_DEF(arg) RBTREEI_DEF2 arg
|
||||
/* Deferred evaluation for the rbtree definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define RBTREEI_DEF_P1(arg) RBTREEI_DEF_P2 arg
|
||||
|
||||
#define RBTREEI_DEF2(name, type, oplist, tree_t, node_t, tree_it_t) \
|
||||
/* Internal rbtree definition
|
||||
- name: prefix to be used
|
||||
- type: type of the elements of the array
|
||||
- oplist: oplist of the type of the elements of the container
|
||||
- tree_t: alias for M_C(name, _t) [ type of the container ]
|
||||
- it_t: alias for M_C(name, _it_t) [ iterator of the container ]
|
||||
- node_t: alias for M_C(name, _node_t) [ node ]
|
||||
*/
|
||||
#define RBTREEI_DEF_P2(name, type, oplist, tree_t, node_t, it_t) \
|
||||
\
|
||||
node_t { \
|
||||
node_t *child[2]; \
|
||||
typedef struct M_C(name, _node_s) { \
|
||||
struct M_C(name, _node_s) *child[2]; \
|
||||
type data; \
|
||||
rbtreei_color_e color; \
|
||||
}; \
|
||||
} node_t; \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
size_t size; \
|
||||
@@ -147,9 +161,9 @@ typedef enum {
|
||||
\
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
node_t *stack[RBTREEI_MAX_STACK]; \
|
||||
char which[RBTREEI_MAX_STACK]; \
|
||||
int8_t which[RBTREEI_MAX_STACK]; \
|
||||
unsigned int cpt; \
|
||||
} tree_it_t[1]; \
|
||||
} it_t[1]; \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
@@ -172,9 +186,9 @@ typedef enum {
|
||||
M_CALL_DEL(oplist, ptr); \
|
||||
} ) \
|
||||
\
|
||||
RBTREEI_DEF3(name, type, oplist, tree_t, node_t, tree_it_t)
|
||||
RBTREEI_DEF_P3(name, type, oplist, tree_t, node_t, it_t)
|
||||
|
||||
#define RBTREEI_DEF3(name, type, oplist, tree_t, node_t, tree_it_t) \
|
||||
#define RBTREEI_DEF_P3(name, type, oplist, tree_t, node_t, it_t) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(tree_t tree) \
|
||||
@@ -231,7 +245,7 @@ typedef enum {
|
||||
{ \
|
||||
RBTREEI_CONTRACT(tree); \
|
||||
node_t *tab[RBTREEI_MAX_STACK]; \
|
||||
char which[RBTREEI_MAX_STACK]; \
|
||||
int8_t which[RBTREEI_MAX_STACK]; \
|
||||
unsigned int cpt = 0; \
|
||||
node_t *n = tree->node; \
|
||||
/* If nothing, create new node */ \
|
||||
@@ -342,7 +356,7 @@ typedef enum {
|
||||
tree->node = p; \
|
||||
} else { \
|
||||
assert (cpt >= 3); \
|
||||
tab[cpt-3]->child[(int) which[cpt-3]] = p; \
|
||||
tab[cpt-3]->child[which[cpt-3]] = p; \
|
||||
} \
|
||||
/* Done */ \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
@@ -356,7 +370,7 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _int_it)(tree_it_t it, const tree_t tree, int child) \
|
||||
M_C(name, _int_it)(it_t it, const tree_t tree, int child) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
assert (it != NULL); \
|
||||
@@ -377,19 +391,19 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(tree_it_t it, const tree_t tree) \
|
||||
M_C(name, _it)(it_t it, const tree_t tree) \
|
||||
{ \
|
||||
M_C(name, _int_it)(it, tree, 0); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_last)(tree_it_t it, const tree_t tree) \
|
||||
M_C(name, _it_last)(it_t it, const tree_t tree) \
|
||||
{ \
|
||||
M_C(name,_int_it)(it, tree, 1); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(tree_it_t it, const tree_t tree) \
|
||||
M_C(name, _it_end)(it_t it, const tree_t tree) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
assert (it != NULL); \
|
||||
@@ -397,21 +411,21 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(tree_it_t it, const tree_it_t ref) \
|
||||
M_C(name, _it_set)(it_t it, const it_t ref) \
|
||||
{ \
|
||||
assert (it != NULL && ref != NULL); \
|
||||
*it = *ref; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const tree_it_t it) \
|
||||
M_C(name, _end_p)(const it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
return it->cpt == 0; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _int_next)(tree_it_t it, int child) \
|
||||
M_C(name, _int_next)(it_t it, int child) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
assert (child == 0 || child == 1); \
|
||||
@@ -442,19 +456,19 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _next)(tree_it_t it) \
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
M_C(name, _int_next)(it, 0); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _previous)(tree_it_t it) \
|
||||
M_C(name, _previous)(it_t it) \
|
||||
{ \
|
||||
M_C(name, _int_next)(it, 1); \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _ref)(const tree_it_t it) \
|
||||
M_C(name, _ref)(const it_t it) \
|
||||
{ \
|
||||
assert(it != NULL && it->cpt > 0); \
|
||||
/* NOTE: partially unsafe if the user modify the order of the el */ \
|
||||
@@ -462,14 +476,14 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const tree_it_t it) \
|
||||
M_C(name, _cref)(const it_t it) \
|
||||
{ \
|
||||
return M_CONST_CAST(type, M_C(name, _ref)(it)); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_equal_p)(const tree_it_t it1, \
|
||||
const tree_it_t it2) \
|
||||
M_C(name, _it_equal_p)(const it_t it1, \
|
||||
const it_t it2) \
|
||||
{ \
|
||||
return it1->cpt == it2->cpt \
|
||||
&& it1->stack[it1->cpt-1] == it2->stack[it2->cpt-1]; \
|
||||
@@ -477,7 +491,7 @@ typedef enum {
|
||||
\
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_from)(tree_it_t it, \
|
||||
M_C(name, _it_from)(it_t it, \
|
||||
const tree_t tree, type const data) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
@@ -499,7 +513,7 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_to_p)(tree_it_t it, type const data) \
|
||||
M_C(name, _it_until_p)(it_t it, type const data) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
if (it->cpt == 0) return true; \
|
||||
@@ -509,6 +523,23 @@ typedef enum {
|
||||
return (cmp >= 0); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_while_p)(it_t it, type const data) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
if (it->cpt == 0) return true; \
|
||||
assert (it->cpt > 0 && it->cpt < RBTREEI_MAX_STACK); \
|
||||
node_t *n = it->stack[it->cpt-1]; \
|
||||
int cmp = M_CALL_CMP(oplist, n->data, data); \
|
||||
return (cmp <= 0); \
|
||||
} \
|
||||
\
|
||||
static inline bool M_ATTR_DEPRECATED \
|
||||
M_C(name, _it_to_p)(it_t it, type const data) \
|
||||
{ \
|
||||
return M_C(name, _it_until_p)(it, data); \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _min)(const tree_t tree) \
|
||||
{ \
|
||||
@@ -649,7 +680,7 @@ typedef enum {
|
||||
return tree->size == 0; \
|
||||
} \
|
||||
\
|
||||
/* Take care of the case n == NULL too */ \
|
||||
/* Take care of the case n == NULL too */ \
|
||||
static inline bool \
|
||||
M_C(name, _int_is_black)(const node_t *n) \
|
||||
{ \
|
||||
@@ -692,7 +723,7 @@ typedef enum {
|
||||
{ \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
node_t *tab[RBTREEI_MAX_STACK]; \
|
||||
unsigned char which[RBTREEI_MAX_STACK]; \
|
||||
int8_t which[RBTREEI_MAX_STACK]; \
|
||||
unsigned int cpt = 0; \
|
||||
node_t *n = tree->node; \
|
||||
which[0] = 0; \
|
||||
@@ -849,8 +880,8 @@ typedef enum {
|
||||
RBTREEI_CONTRACT(t1); \
|
||||
RBTREEI_CONTRACT(t2); \
|
||||
if (t1->size != t2->size) return false; \
|
||||
tree_it_t it1; \
|
||||
tree_it_t it2; \
|
||||
it_t it1; \
|
||||
it_t it2; \
|
||||
/* NOTE: We can't compare two tree directly as they can be \
|
||||
structuraly different but functionnaly equal (you get this by \
|
||||
constructing the tree in a different way). We have to \
|
||||
@@ -877,7 +908,7 @@ typedef enum {
|
||||
M_HASH_DECL(hash); \
|
||||
/* NOTE: We can't compute the hash directly for the same reason \
|
||||
than for EQUAL operator. */ \
|
||||
tree_it_t it1; \
|
||||
it_t it1; \
|
||||
M_C(name, _it)(it1, t1); \
|
||||
while (!M_C(name, _end_p)(it1)) { \
|
||||
type const *ref1 = M_C(name, _cref)(it1); \
|
||||
@@ -896,7 +927,7 @@ typedef enum {
|
||||
(append ? string_cat_str : string_set_str) (str, "["); \
|
||||
/* NOTE: The print is really naive, and not really efficient */ \
|
||||
bool commaToPrint = false; \
|
||||
tree_it_t it1; \
|
||||
it_t it1; \
|
||||
M_C(name, _it)(it1, t1); \
|
||||
while (!M_C(name, _end_p)(it1)) { \
|
||||
if (commaToPrint) \
|
||||
@@ -917,7 +948,7 @@ typedef enum {
|
||||
RBTREEI_CONTRACT(rbtree); \
|
||||
assert (file != NULL); \
|
||||
fputc ('[', file); \
|
||||
tree_it_t it; \
|
||||
it_t it; \
|
||||
bool commaToPrint = false; \
|
||||
for (M_C(name, _it)(it, rbtree) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
@@ -978,7 +1009,7 @@ typedef enum {
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) break; \
|
||||
M_C(name, _push)(rbtree, item); \
|
||||
@@ -988,6 +1019,58 @@ 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) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT(t1); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
const M_C(name, _type_t) *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); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD(IN_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(tree_t t1, m_serial_read_t f) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT(t1); \
|
||||
assert (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
type key; \
|
||||
M_C(name,_clean)(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); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(oplist, key); \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no in_serial */ ) \
|
||||
\
|
||||
\
|
||||
|
||||
// TODO: specialized _sort shall do nothing, but shall check the requested order. How ?
|
||||
|
||||
|
||||
+506
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* M*LIB - Serial BIN
|
||||
*
|
||||
* Copyright (c) 2017-2019, 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_SERIAL_BIN_H
|
||||
#define MSTARLIB_SERIAL_BIN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* 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_serial_bin_write_boolean(m_serial_write_t serial, const bool data)
|
||||
{
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
size_t n = fwrite (M_ASSIGN_CAST(const void*, &data), sizeof (bool), 1, f);
|
||||
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
static inline m_serial_return_code_t
|
||||
m_serial_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 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &i8), sizeof i8, 1, f);
|
||||
break;
|
||||
case 2:
|
||||
i16 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &i16), sizeof i16, 1, f);
|
||||
break;
|
||||
case 4:
|
||||
i32 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &i32), sizeof i32, 1, f);
|
||||
break;
|
||||
case 8:
|
||||
i64 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &i64), sizeof i64, 1, f);
|
||||
break;
|
||||
default:
|
||||
M_ASSERT_INIT(false, "an integer of suitable size");
|
||||
break;
|
||||
}
|
||||
return n == 1 ? M_SERIAL_OK_DONE : M_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_serial_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;
|
||||
switch (size_of_type) {
|
||||
case sizeof f1:
|
||||
f1 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &f1), sizeof f1, 1, f);
|
||||
break;
|
||||
case sizeof f2:
|
||||
f2 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &f2), sizeof f2, 1, f);
|
||||
break;
|
||||
case sizeof f3:
|
||||
f3 = data;
|
||||
n = fwrite (M_ASSIGN_CAST(const void*, &f3), sizeof f3, 1, f);
|
||||
break;
|
||||
default:
|
||||
M_ASSERT_INIT(false, "a float of suitable size");
|
||||
break;
|
||||
}
|
||||
return n == 1 ? M_SERIAL_OK_DONE : M_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_serial_bin_write_string(m_serial_write_t serial, const char data[])
|
||||
{
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
size_t l = strlen(data);
|
||||
size_t n = fwrite (M_ASSIGN_CAST(const void*, data), 1, l+1, f);
|
||||
return n == l+1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'.
|
||||
If 'number_of_elements' is 0, then either the array has no data,
|
||||
or the number of elements of the array is unkown.
|
||||
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_serial_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
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_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_serial_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_SERIAL_FAIL;
|
||||
} else {
|
||||
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_serial_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_SERIAL_FAIL;
|
||||
} else {
|
||||
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_serial_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Start writing a tuple into the serial stream '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_serial_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
static inline m_serial_return_code_t
|
||||
m_serial_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
|
||||
(void) serial;
|
||||
(void) field_name;
|
||||
(void) max;
|
||||
(void) index; // Assume index are write in order from 0 to max.
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
/* Start writing a variant into the serial stream 'serial'.
|
||||
If index <= 0, the variant is empty.
|
||||
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_serial_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;
|
||||
(void) max;
|
||||
(void) local;
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
size_t n = fwrite (M_ASSIGN_CAST(const void*, &index), sizeof index, 1, f);
|
||||
return n == 1 ? ((index < 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE) : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
/* The exported interface. */
|
||||
static const m_serial_write_interface_t m_serial_write_bin_interface = {
|
||||
m_serial_bin_write_boolean,
|
||||
m_serial_bin_write_integer,
|
||||
m_serial_bin_write_float,
|
||||
m_serial_bin_write_string,
|
||||
m_serial_bin_write_array_start,
|
||||
m_serial_bin_write_array_next,
|
||||
m_serial_bin_write_array_end,
|
||||
m_serial_bin_write_array_start,
|
||||
m_serial_bin_write_map_value,
|
||||
m_serial_bin_write_array_next,
|
||||
m_serial_bin_write_array_end,
|
||||
m_serial_bin_write_tuple_start,
|
||||
m_serial_bin_write_tuple_id,
|
||||
m_serial_bin_write_tuple_end,
|
||||
m_serial_bin_write_variant_start,
|
||||
m_serial_bin_write_variant_end
|
||||
};
|
||||
|
||||
static inline void m_serial_bin_write_init(m_serial_write_t serial, FILE *f)
|
||||
{
|
||||
serial->m_interface = &m_serial_write_bin_interface;
|
||||
serial->data[0].p = M_ASSIGN_CAST(void*, f);
|
||||
}
|
||||
|
||||
static inline void m_serial_bin_write_clear(m_serial_write_t serial)
|
||||
{
|
||||
(void) serial; // Nothing to do
|
||||
}
|
||||
|
||||
|
||||
/* 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_serial_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);
|
||||
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_read_integer(m_serial_read_t serial, long long *i, 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:
|
||||
n = fread (M_ASSIGN_CAST(void*, &i8), sizeof i8, 1, f);
|
||||
*i = i8;
|
||||
break;
|
||||
case 2:
|
||||
n = fread (M_ASSIGN_CAST(void*, &i16), sizeof i16, 1, f);
|
||||
*i = i16;
|
||||
break;
|
||||
case 4:
|
||||
n = fread (M_ASSIGN_CAST(void*, &i32), sizeof i32, 1, f);
|
||||
*i = i32;
|
||||
break;
|
||||
case 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_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){
|
||||
float f1;
|
||||
double f2;
|
||||
long double f3;
|
||||
size_t n;
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
switch (size_of_type) {
|
||||
case sizeof f1:
|
||||
n = fread (M_ASSIGN_CAST(void*, &f1), sizeof f1, 1, f);
|
||||
*r = f1;
|
||||
break;
|
||||
case sizeof f2:
|
||||
n = fread (M_ASSIGN_CAST(void*, &f2), sizeof f2, 1, f);
|
||||
*r = f2;
|
||||
break;
|
||||
case sizeof f3:
|
||||
n = fread (M_ASSIGN_CAST(void*, &f3), sizeof f3, 1, f);
|
||||
*r = f3;
|
||||
break;
|
||||
default:
|
||||
M_ASSERT_INIT(false, "a float of suitable size");
|
||||
break;
|
||||
}
|
||||
return n == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_read_string(m_serial_read_t serial, struct string_s *s){
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
string_clean(s);
|
||||
int c;
|
||||
c = fgetc(f);
|
||||
while (c != 0 && c != EOF) {
|
||||
string_push_back(s, c);
|
||||
c = fgetc(f);
|
||||
}
|
||||
return c != EOF ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start reading from the stream 'serial' an array.
|
||||
Set '*num' with the number of elements, or 0 if it is not known.
|
||||
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 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_serial_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_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_SERIAL_FAIL;
|
||||
return p == 0xABCDEF ? M_SERIAL_OK_CONTINUE : p == 0x12345678 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
return 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_serial_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_SERIAL_FAIL;
|
||||
return p == 0xABCDEF ? M_SERIAL_OK_CONTINUE : p == 0x12345678 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
} else {
|
||||
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_serial_bin_read_map_value(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) serial;
|
||||
local->data[1].s = 0;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Continue reading a tuple from the stream 'serial'.
|
||||
Set '*id' with the corresponding index of the table 'field_name[max]'
|
||||
associated to the parsed field in the stream.
|
||||
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_serial_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;
|
||||
(void) field_name;
|
||||
(void) max;
|
||||
*id = local->data[1].s;
|
||||
local->data[1].s ++;
|
||||
return (*id == max) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Start reading a variant from the stream 'serial'.
|
||||
Set '*id' with the corresponding index of the table 'field_name[max]'
|
||||
associated to the parsed field in the stream.
|
||||
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_serial_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;
|
||||
(void) max;
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
size_t n = fread (M_ASSIGN_CAST(void*, id), sizeof *id, 1, f);
|
||||
return n == 1 ? ((*id < 0) ? M_SERIAL_OK_DONE : M_SERIAL_OK_CONTINUE) : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_bin_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
static const m_serial_read_interface_t m_serial_bin_read_interface = {
|
||||
m_serial_bin_read_boolean,
|
||||
m_serial_bin_read_integer,
|
||||
m_serial_bin_read_float,
|
||||
m_serial_bin_read_string,
|
||||
m_serial_bin_read_array_start,
|
||||
m_serial_bin_read_array_next,
|
||||
m_serial_bin_read_array_start,
|
||||
m_serial_bin_read_map_value,
|
||||
m_serial_bin_read_array_next,
|
||||
m_serial_bin_read_tuple_start,
|
||||
m_serial_bin_read_tuple_id,
|
||||
m_serial_bin_read_variant_start,
|
||||
m_serial_bin_read_variant_end
|
||||
};
|
||||
|
||||
static inline void m_serial_bin_read_init(m_serial_read_t serial, FILE *f)
|
||||
{
|
||||
serial->m_interface = &m_serial_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)
|
||||
{
|
||||
(void) serial; // Nothing to do
|
||||
}
|
||||
|
||||
#endif
|
||||
+542
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* M*LIB - Serial JSON
|
||||
*
|
||||
* Copyright (c) 2017-2019, 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_SERIAL_JSON_H
|
||||
#define MSTARLIB_SERIAL_JSON_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* 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_serial_json_write_boolean(m_serial_write_t serial, const bool data)
|
||||
{
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%s", data ? "true" : "false");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
static inline m_serial_return_code_t
|
||||
m_serial_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
|
||||
{
|
||||
(void) size_of_type; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%lld", data);
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_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_serial_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
{
|
||||
(void) size_of_type; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%Lf", data);
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_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_serial_json_write_string(m_serial_write_t serial, const char data[])
|
||||
{
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
/* Build dummy string to reuse string_out_str */
|
||||
string_t v2;
|
||||
uintptr_t ptr = (uintptr_t) data;
|
||||
v2->u.heap.size = strlen(data);
|
||||
v2->u.heap.alloc = v2->u.heap.size + 1;
|
||||
v2->ptr = (char*)ptr;
|
||||
string_out_str(f, v2);
|
||||
return M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'.
|
||||
If 'number_of_elements' is 0, then either the array has no data,
|
||||
or the number of elements of the array is unkown.
|
||||
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_serial_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) number_of_elements; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "[");
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_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_serial_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, ",");
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "]");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start writing a map of 'number_of_elements' pairs of objects into the serial stream 'serial'.
|
||||
If 'number_of_elements' is 0, then either the map has no data,
|
||||
or the number of elements is unkown.
|
||||
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_serial_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) number_of_elements; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "{");
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, ":");
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, ",");
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "}");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start writing a tuple into the serial stream '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_serial_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "{");
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) max; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]);
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* End the write of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
static inline m_serial_return_code_t
|
||||
m_serial_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "}");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start writing a variant into the serial stream 'serial'.
|
||||
If index <= 0, the variant is empty.
|
||||
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_serial_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) max; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n;
|
||||
if (index >= 0) {
|
||||
assert (index < max);
|
||||
n = fprintf(f, "{\"%s\":", field_name[index]);
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
} else {
|
||||
n = fprintf(f, "{}");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_serial_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "}");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* The exported interface. */
|
||||
static const m_serial_write_interface_t m_serial_write_json_interface = {
|
||||
m_serial_json_write_boolean,
|
||||
m_serial_json_write_integer,
|
||||
m_serial_json_write_float,
|
||||
m_serial_json_write_string,
|
||||
m_serial_json_write_array_start,
|
||||
m_serial_json_write_array_next,
|
||||
m_serial_json_write_array_end,
|
||||
m_serial_json_write_map_start,
|
||||
m_serial_json_write_map_value,
|
||||
m_serial_json_write_map_next,
|
||||
m_serial_json_write_map_end,
|
||||
m_serial_json_write_tuple_start,
|
||||
m_serial_json_write_tuple_id,
|
||||
m_serial_json_write_tuple_end,
|
||||
m_serial_json_write_variant_start,
|
||||
m_serial_json_write_variant_end
|
||||
};
|
||||
|
||||
static inline void m_serial_json_write_init(m_serial_write_t serial, FILE *f)
|
||||
{
|
||||
serial->m_interface = &m_serial_write_json_interface;
|
||||
serial->data[0].p = M_ASSIGN_CAST(void*, f);
|
||||
}
|
||||
|
||||
static inline void m_serial_json_write_clear(m_serial_write_t serial)
|
||||
{
|
||||
(void) serial; // Nothing to do
|
||||
}
|
||||
|
||||
|
||||
/* Helper function */
|
||||
static inline int
|
||||
m_serial_json_read_skip (FILE *f)
|
||||
{
|
||||
int c;
|
||||
do {
|
||||
c = fgetc(f);
|
||||
} while (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_serial_json_read_boolean(m_serial_read_t serial, bool *b){
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int c = m_serial_json_read_skip(f);
|
||||
if (c == 't') {
|
||||
*b = true;
|
||||
c = fgetc(f);
|
||||
if (c != 'r') return M_SERIAL_FAIL;
|
||||
c = fgetc(f);
|
||||
if (c != 'u') return M_SERIAL_FAIL;
|
||||
c = fgetc(f);
|
||||
if (c != 'e') return M_SERIAL_FAIL;
|
||||
return M_SERIAL_OK_DONE;
|
||||
} else if (c == 'f') {
|
||||
*b = false;
|
||||
c = fgetc(f);
|
||||
if (c != 'a') return M_SERIAL_FAIL;
|
||||
c = fgetc(f);
|
||||
if (c != 'l') return M_SERIAL_FAIL;
|
||||
c = fgetc(f);
|
||||
if (c != 's') return M_SERIAL_FAIL;
|
||||
c = fgetc(f);
|
||||
if (c != 'e') return M_SERIAL_FAIL;
|
||||
return M_SERIAL_OK_DONE;
|
||||
} else {
|
||||
return M_SERIAL_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_serial_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;
|
||||
return fscanf(f, " %lld", i) == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_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;
|
||||
return fscanf(f, " %Lf", r) == 1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_read_string(m_serial_read_t serial, struct string_s *s){
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
fscanf(f, " "); // Skip any leading spaces.
|
||||
return string_in_str(s, f) ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start reading from the stream 'serial' an array.
|
||||
Set '*num' with the number of elements, or 0 if it is not known.
|
||||
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 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_serial_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int final1 = -1, final2 = -1;
|
||||
fscanf(f, " [%n ]%n", &final1, &final2);
|
||||
*num = 0; // don't know the size of the array.
|
||||
// Test how much the parsing succeed.
|
||||
return (final1 < 0) ? M_SERIAL_FAIL : (final2 < 0) ? M_SERIAL_OK_CONTINUE : M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
/* 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_serial_json_read_array_next(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
char c = 0;
|
||||
fscanf(f, " %c", &c);
|
||||
return c == ',' ? M_SERIAL_OK_CONTINUE : c == ']' ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start reading from the stream 'serial' a map.
|
||||
Set '*num' with the number of elements, or 0 if it is not known.
|
||||
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 and the map continue,
|
||||
M_SERIAL_OK_DONE if it succeeds and the map ends (the map is empty),
|
||||
M_SERIAL_FAIL otherwise */
|
||||
static inline m_serial_return_code_t
|
||||
m_serial_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int final1 = -1, final2 = -1;
|
||||
fscanf(f, " {%n }%n", &final1, &final2);
|
||||
*num = 0; // don't know the size of the map.
|
||||
// Test how much the parsing succeed.
|
||||
return (final1 < 0) ? M_SERIAL_FAIL : (final2 < 0) ? M_SERIAL_OK_CONTINUE : M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
/* 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_serial_json_read_map_value(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int final = -1;
|
||||
fscanf(f, " :%n", &final);
|
||||
return final > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Continue reading from the stream 'serial' a map.
|
||||
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_serial_json_read_map_next(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
char c = 0;
|
||||
fscanf(f, " %c", &c);
|
||||
return c == ',' ? M_SERIAL_OK_CONTINUE : c == '}' ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int final = -1;
|
||||
fscanf(f, " {%n", &final);
|
||||
return final > 0 ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Continue reading a tuple from the stream 'serial'.
|
||||
Set '*id' with the corresponding index of the table 'field_name[max]'
|
||||
associated to the parsed field in the stream.
|
||||
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_serial_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
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int c = m_serial_json_read_skip(f);
|
||||
if (c == EOF) return M_SERIAL_FAIL;
|
||||
if (c == '}') return M_SERIAL_OK_DONE;
|
||||
if (c == ',') {
|
||||
// If first call of read_tuple_id, it is a failure
|
||||
if (*id == -1) return M_SERIAL_FAIL;
|
||||
} else {
|
||||
// c should be \" but let fscanf parse it.
|
||||
ungetc(c, f);
|
||||
}
|
||||
/* Read the field in the JSON */
|
||||
c = -1;
|
||||
char field[M_MAX_IDENTIFIER_LENGTH+1]; // TODO: fix error detection below.
|
||||
fscanf(f, " \"%" M_APPLY(M_AS_STR, M_MAX_IDENTIFIER_LENGTH) "[^ \t\n\"]\":%n", field, &c);
|
||||
if (c <= 0)
|
||||
return M_SERIAL_FAIL;
|
||||
/* Search for field in field_name */
|
||||
for(int n = 0; n < max; n++) {
|
||||
if (strcmp(field, field_name[n]) == 0) {
|
||||
*id = n;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
}
|
||||
return M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* Start reading a variant from the stream 'serial'.
|
||||
Set '*id' with the corresponding index of the table 'field_name[max]'
|
||||
associated to the parsed field in the stream.
|
||||
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_serial_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
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
|
||||
int final1 = -1, final2 = -1;
|
||||
fscanf(f, " {%n }%n", &final1, &final2);
|
||||
// Test how much the parsing succeed.
|
||||
if (final1 <= 0) return M_SERIAL_FAIL;
|
||||
if (final2 > 0) return M_SERIAL_OK_DONE;
|
||||
|
||||
/* Read the field in the JSON */
|
||||
char field[M_MAX_IDENTIFIER_LENGTH+1];
|
||||
fscanf(f, " \"%" M_APPLY(M_AS_STR, M_MAX_IDENTIFIER_LENGTH) "[^ \t\n\"]\":%n", field, &final2);
|
||||
if (final2 <= 0) return M_SERIAL_FAIL;
|
||||
|
||||
/* Search for field in field_name */
|
||||
for(int n = 0; n < max; n++) {
|
||||
if (strcmp(field, field_name[n]) == 0) {
|
||||
*id = n;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
}
|
||||
return M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
/* 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_serial_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int final = -1;
|
||||
fscanf(f, " }%n", &final);
|
||||
return final > 0 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL;
|
||||
}
|
||||
|
||||
static const m_serial_read_interface_t m_serial_json_read_interface = {
|
||||
m_serial_json_read_boolean,
|
||||
m_serial_json_read_integer,
|
||||
m_serial_json_read_float,
|
||||
m_serial_json_read_string,
|
||||
m_serial_json_read_array_start,
|
||||
m_serial_json_read_array_next,
|
||||
m_serial_json_read_map_start,
|
||||
m_serial_json_read_map_value,
|
||||
m_serial_json_read_map_next,
|
||||
m_serial_json_read_tuple_start,
|
||||
m_serial_json_read_tuple_id,
|
||||
m_serial_json_read_variant_start,
|
||||
m_serial_json_read_variant_end
|
||||
};
|
||||
|
||||
static inline void m_serial_json_read_init(m_serial_read_t serial, FILE *f)
|
||||
{
|
||||
serial->m_interface = &m_serial_json_read_interface;
|
||||
serial->data[0].p = M_ASSIGN_CAST(void*, f);
|
||||
}
|
||||
|
||||
static inline void m_serial_json_read_clear(m_serial_read_t serial)
|
||||
{
|
||||
(void) serial; // Nothing to do
|
||||
}
|
||||
|
||||
#endif
|
||||
+20
-20
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - SHARED Module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -32,40 +32,40 @@
|
||||
/* Define the oplist of a shared pointer.
|
||||
USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define SHARED_PTR_OPLIST(...) \
|
||||
SHAREDI_PTR_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
_OPLIST(__VA_ARGS__ )))
|
||||
SHAREDI_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
_OPLIST(__VA_ARGS__ )))
|
||||
|
||||
/* Define shared pointer and its function.
|
||||
USAGE: SHARED_PTR_DEF(name, type, [, oplist]) */
|
||||
#define SHARED_PTR_DEF(name, ...) \
|
||||
SHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_ATOMIC_OPLIST ), \
|
||||
(name, __VA_ARGS__ , SHAREDI_ATOMIC_OPLIST)))
|
||||
SHAREDI_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_ATOMIC_OPLIST ), \
|
||||
(name, __VA_ARGS__ , SHAREDI_ATOMIC_OPLIST)))
|
||||
|
||||
/* Define relaxed shared pointer and its function (thread unsafe).
|
||||
USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
|
||||
#define SHARED_PTR_RELAXED_DEF(name, ...) \
|
||||
SHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_INTEGER_OPLIST ), \
|
||||
(name, __VA_ARGS__ , SHAREDI_INTEGER_OPLIST)))
|
||||
SHAREDI_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_INTEGER_OPLIST ), \
|
||||
(name, __VA_ARGS__ , SHAREDI_INTEGER_OPLIST)))
|
||||
|
||||
/* Define shared resource and its function.
|
||||
This is a bounded pool of resource shared by multiple owners.
|
||||
USAGE: SHARED_RESOURCE_DEF(name, type, [, oplist]) */
|
||||
#define SHARED_RESOURCE_DEF(name, ...) \
|
||||
SHAREDI_RESOURCE_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
(name, __VA_ARGS__)))
|
||||
SHAREDI_RESOURCE_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
(name, __VA_ARGS__)))
|
||||
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// deferred
|
||||
#define SHAREDI_PTR_OPLIST(arg) SHAREDI_PTR_OPLIST2 arg
|
||||
#define SHAREDI_PTR_OPLIST_P1(arg) SHAREDI_PTR_OPLIST_P2 arg
|
||||
|
||||
#define SHAREDI_PTR_OPLIST2(name, oplist) ( \
|
||||
#define SHAREDI_PTR_OPLIST_P2(name, oplist) ( \
|
||||
INIT(M_C(name, _init)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
@@ -80,7 +80,7 @@
|
||||
)
|
||||
|
||||
// deferred
|
||||
#define SHAREDI_PTR_DEF(arg) SHAREDI_PTR_DEF2 arg
|
||||
#define SHAREDI_PTR_DEF_P1(arg) SHAREDI_PTR_DEF_P2 arg
|
||||
|
||||
// OPLIST to handle a counter of atomic type
|
||||
#define SHAREDI_ATOMIC_OPLIST (TYPE(atomic_int), \
|
||||
@@ -105,7 +105,7 @@ static inline int sharedi_integer_cref(int *p) { return *p; }
|
||||
assert(*shared == NULL || M_CALL_IT_CREF(cpt_oplist, &(*shared)->cpt) >= 1); \
|
||||
} while (0)
|
||||
|
||||
#define SHAREDI_PTR_DEF2(name, type, oplist, cpt_oplist) \
|
||||
#define SHAREDI_PTR_DEF_P2(name, type, oplist, cpt_oplist) \
|
||||
\
|
||||
typedef struct M_C(name, _s){ \
|
||||
type *data; /* Pointer to the data */ \
|
||||
@@ -297,15 +297,15 @@ static inline int sharedi_integer_cref(int *p) { return *p; }
|
||||
} while (0)
|
||||
|
||||
// deferred
|
||||
#define SHAREDI_RESOURCE_DEF(arg) SHAREDI_RESOURCE_DEF2 arg
|
||||
#define SHAREDI_RESOURCE_DEF_P1(arg) SHAREDI_RESOURCE_DEF_P2 arg
|
||||
|
||||
#define SHAREDI_RESOURCE_DEF2(name, type, oplist) \
|
||||
#define SHAREDI_RESOURCE_DEF_P2(name, type, oplist) \
|
||||
\
|
||||
/* Create an aligned type to avoid false sharing between threads */ \
|
||||
typedef struct M_C(name, _atype_s) { \
|
||||
atomic_uint cpt; \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) +sizeof(atomic_uint) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) -sizeof(atomic_uint) : 1]; \
|
||||
M_CACHELINE_ALIGN(align, type, atomic_uint); \
|
||||
} M_C(name, _atype_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
|
||||
+40
-31
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - SNAPSHOT Module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -32,39 +32,39 @@
|
||||
/* Define a Single Producer Single Consummer snapshot and its functions
|
||||
USAGE: SNAPSHOT_SPSC_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_SPSC_DEF(name, ...) \
|
||||
SNAPSHOTI_SPSC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
SNAPSHOTI_SPSC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
/* Define a Single Producer Multiple Consummer snapshot and its functions
|
||||
USAGE: SNAPSHOT_SPMC_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_SPMC_DEF(name, ...) \
|
||||
SNAPSHOTI_SPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
SNAPSHOTI_SPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
/* Define a Multiple Producer Multiple Consummer snapshot and its functions
|
||||
USAGE: SNAPSHOT_MPMC_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_MPMC_DEF(name, ...) \
|
||||
SNAPSHOTI_MPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
SNAPSHOTI_MPMC_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
/* Define the oplist of a snapshot (SPSC, SPMC or MPMC).
|
||||
USAGE: SNAPSHOT_OPLIST(name[, oplist]) */
|
||||
#define SNAPSHOT_OPLIST(...) \
|
||||
SNAPSHOTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(__VA_ARGS__ )))
|
||||
SNAPSHOTI_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// deferred evaluation of the input
|
||||
#define SNAPSHOTI_OPLIST(arg) SNAPSHOTI_OPLIST2 arg
|
||||
#define SNAPSHOTI_OPLIST_P1(arg) SNAPSHOTI_OPLIST_P2 arg
|
||||
|
||||
/* Define the oplist of a snapshot */
|
||||
#define SNAPSHOTI_OPLIST2(name, oplist) \
|
||||
#define SNAPSHOTI_OPLIST_P2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
@@ -84,6 +84,7 @@
|
||||
* - w: Index of the write buffer Range [0..2]
|
||||
* - f: Next index of the write buffer when a shot is taken Range [0..2]
|
||||
* - b: Boolean indicating that the read buffer shall be updated
|
||||
* all fields packed in an unsigned char type.
|
||||
*/
|
||||
#define SNAPSHOTI_SPSC_FLAG(r, w, f, b) \
|
||||
((unsigned char)( ( (r) << 4) | ((w) << 2) | ((f)) | ((b) << 6)))
|
||||
@@ -108,19 +109,19 @@
|
||||
SNAPSHOTI_SPSC_FLAGS_CONTRACT(f); \
|
||||
} while (0)
|
||||
|
||||
// Defered evaluation (TBC if it really helps).
|
||||
#define SNAPSHOTI_SPSC_DEF(arg) SNAPSHOTI_SPSC_DEF2 arg
|
||||
// Defered evaluation of the arguments.
|
||||
#define SNAPSHOTI_SPSC_DEF_P1(arg) SNAPSHOTI_SPSC_DEF_P2 arg
|
||||
|
||||
// This is basically an atomic triple buffer (Lock Free)
|
||||
// between a produced thread and a consummer thread.
|
||||
// A snapshot is basically an atomic triple buffer (Lock Free)
|
||||
// between a single producer thread and a single consummer thread.
|
||||
#define SNAPSHOTI_SPSC_MAX_BUFFER 3
|
||||
|
||||
#define SNAPSHOTI_SPSC_DEF2(name, type, oplist) \
|
||||
#define SNAPSHOTI_SPSC_DEF_P2(name, type, oplist) \
|
||||
\
|
||||
/* Create an aligned type to avoid false sharing between threads */ \
|
||||
typedef struct M_C(name, _aligned_type_s) { \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
|
||||
M_CACHELINE_ALIGN(align, type); \
|
||||
} M_C(name, _aligned_type_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
@@ -269,6 +270,7 @@
|
||||
} \
|
||||
|
||||
|
||||
|
||||
/******************************** INTERNAL SPMC **********************************/
|
||||
|
||||
#define SNAPSHOTI_SPMC_INT_FLAG(w, n) ( ((w) << 1) | (n) )
|
||||
@@ -280,7 +282,13 @@
|
||||
|
||||
#define SNAPSHOTI_SPMC_MAX_READER (GENINT_MAX_ALLOC-SNAPSHOTI_SPMC_EXTRA_BUFFER)
|
||||
|
||||
// structure to handle SPMC snapshot but return a unique index in a buffer array.
|
||||
/* structure to handle SPMC snapshot but return an unique index in the buffer array.
|
||||
- lastNext: last published written index + next flag (format SNAPSHOTI_SPMC_INT_FLAG)
|
||||
- currentWrite: the index being currently written.
|
||||
- n : number of readers
|
||||
- cptTab: ref counter array
|
||||
- freeList: a pool of free integers.
|
||||
*/
|
||||
typedef struct snapshot_mrsw_int_s {
|
||||
atomic_uint lastNext;
|
||||
unsigned int currentWrite;
|
||||
@@ -343,7 +351,7 @@ static inline unsigned int snapshot_mrsw_int_get_write_idx(snapshot_mrsw_int_t s
|
||||
static inline unsigned int snapshot_mrsw_int_size(snapshot_mrsw_int_t s)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return s->n - SNAPSHOTI_SPMC_EXTRA_BUFFER;
|
||||
return s->n;
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_write_idx(snapshot_mrsw_int_t s, unsigned int idx)
|
||||
@@ -479,15 +487,15 @@ static inline void snapshot_mrsw_int_read_end(snapshot_mrsw_int_t s, unsigned in
|
||||
} while (0)
|
||||
|
||||
|
||||
// Defered evaluation (TBC if it really helps).
|
||||
#define SNAPSHOTI_SPMC_DEF(arg) SNAPSHOTI_SPMC_DEF2 arg
|
||||
// Defered evaluation
|
||||
#define SNAPSHOTI_SPMC_DEF_P1(arg) SNAPSHOTI_SPMC_DEF_P2 arg
|
||||
|
||||
#define SNAPSHOTI_SPMC_DEF2(name, type, oplist) \
|
||||
#define SNAPSHOTI_SPMC_DEF_P2(name, type, oplist) \
|
||||
\
|
||||
/* Create an aligned type to avoid false sharing between threads */ \
|
||||
typedef struct M_C(name, _aligned_type_s) { \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
|
||||
M_CACHELINE_ALIGN(align, type); \
|
||||
} M_C(name, _aligned_type_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
@@ -563,16 +571,17 @@ static inline void snapshot_mrsw_int_read_end(snapshot_mrsw_int_t s, unsigned in
|
||||
//FIXME: Evaluate the needs for the methods _set_, _init_set.
|
||||
|
||||
|
||||
|
||||
/******************************** INTERNAL MPMC **********************************/
|
||||
|
||||
// MPMC is built upon SPMC
|
||||
|
||||
// Defered evaluation (TBC if it really helps).
|
||||
#define SNAPSHOTI_MPMC_DEF(arg) SNAPSHOTI_MPMC_DEF2 arg
|
||||
// Defered evaluation
|
||||
#define SNAPSHOTI_MPMC_DEF_P1(arg) SNAPSHOTI_MPMC_DEF_P2 arg
|
||||
|
||||
#define SNAPSHOTI_MPMC_DEF2(name, type, oplist) \
|
||||
#define SNAPSHOTI_MPMC_DEF_P2(name, type, oplist) \
|
||||
\
|
||||
SNAPSHOTI_SPMC_DEF((M_C(name, _mrsw), type, oplist)) \
|
||||
SNAPSHOTI_SPMC_DEF_P1((M_C(name, _mrsw), type, oplist)) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
M_C(name, _mrsw_t) core; \
|
||||
|
||||
+567
-293
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* M*LIB - TUPLE module
|
||||
*
|
||||
* Copyright (c) 2017-2019, 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,14 +33,14 @@
|
||||
USAGE:
|
||||
TUPLE_DEF2(name, [(field1, type1[, oplist1]), (field2, type2[, oplist2]), ...] ) */
|
||||
#define TUPLE_DEF2(name, ...) \
|
||||
TUPLEI_DEF2_A( (name, TUPLEI_INJECT_GLOBAL(__VA_ARGS__)) )
|
||||
TUPLEI_DEF2_P1( (name, TUPLEI_INJECT_GLOBAL(__VA_ARGS__)) )
|
||||
|
||||
/* Define the oplist of a tuple.
|
||||
USAGE: TUPLE_OPLIST(name[, oplist of the first type, ...]) */
|
||||
#define TUPLE_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(TUPLEI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
TUPLEI_OPLIST(__VA_ARGS__ ))
|
||||
(TUPLEI_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST )), \
|
||||
TUPLEI_OPLIST_P1((__VA_ARGS__ )))
|
||||
|
||||
/* Return an array suitable for the _cmp_order function.
|
||||
As compound literals are not supported in C++,
|
||||
@@ -78,10 +80,10 @@ namespace m_tuple {
|
||||
#define TUPLEI_INJECT_OPLIST_B( f, ... ) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
|
||||
|
||||
#define TUPLEI_DEF2_A(...) \
|
||||
TUPLEI_DEF2_B __VA_ARGS__
|
||||
// Deferred evaluation
|
||||
#define TUPLEI_DEF2_P1(...) TUPLEI_DEF2_P2 __VA_ARGS__
|
||||
|
||||
#define TUPLEI_DEF2_B(name, ...) \
|
||||
#define TUPLEI_DEF2_P2(name, ...) \
|
||||
TUPLE_DEFINE_TYPE(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_ENUM(name, __VA_ARGS__) \
|
||||
TUPLEI_IF_ALL(INIT, __VA_ARGS__) \
|
||||
@@ -110,6 +112,10 @@ namespace m_tuple {
|
||||
(TUPLE_DEFINE_IN_STR(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(PARSE_STR, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_PARSE_STR(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(OUT_SERIAL, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_OUT_SERIAL(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(IN_SERIAL, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_IN_SERIAL(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(MOVE, __VA_ARGS__) \
|
||||
@@ -140,6 +146,8 @@ namespace m_tuple {
|
||||
#define TUPLE_GET_STR(f,t,o) M_GET_GET_STR o
|
||||
#define TUPLE_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
|
||||
#define TUPLE_GET_IN_STR(f,t,o) M_GET_IN_STR o
|
||||
#define TUPLE_GET_OUT_SERIAL(f,t,o) M_GET_OUT_SERIAL o
|
||||
#define TUPLE_GET_IN_SERIAL(f,t,o) M_GET_IN_SERIAL o
|
||||
#define TUPLE_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
|
||||
#define TUPLE_GET_SWAP(f,t,o) M_GET_SWAP o
|
||||
#define TUPLE_GET_CLEAN(f,t,o) M_GET_CLEAN o
|
||||
@@ -157,6 +165,8 @@ namespace m_tuple {
|
||||
#define TUPLE_CALL_OUT_STR(t, ...) M_APPLY_API(TUPLE_GET_OUT_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_IN_STR(t, ...) M_APPLY_API(TUPLE_GET_IN_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_PARSE_STR(t, ...) M_APPLY_API(TUPLE_GET_PARSE_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_OUT_SERIAL(t, ...) M_APPLY_API(TUPLE_GET_OUT_SERIAL t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_IN_SERIAL(t, ...) M_APPLY_API(TUPLE_GET_IN_SERIAL t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_SWAP(t, ...) M_APPLY_API(TUPLE_GET_SWAP t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_CLEAN(t, ...) M_APPLY_API(TUPLE_GET_CLEAN t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
|
||||
@@ -399,6 +409,60 @@ namespace m_tuple {
|
||||
if (TUPLE_CALL_PARSE_STR(a, el -> TUPLE_GET_FIELD a, str, &str) == false) \
|
||||
goto exit ; \
|
||||
|
||||
#define TUPLE_STRINGIFY_NAME(a) \
|
||||
M_APPLY(M_AS_STR, TUPLE_GET_FIELD a)
|
||||
|
||||
#define TUPLE_DEFINE_OUT_SERIAL(name, ...) \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, \
|
||||
M_C(name,_t) const el) { \
|
||||
assert (f != NULL && f->m_interface != NULL && el != NULL); \
|
||||
const int field_max = M_NARGS(__VA_ARGS__); \
|
||||
static const char *const field_name[] = \
|
||||
{ M_REDUCE(TUPLE_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
|
||||
int index = 0; \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
ret = f->m_interface->write_tuple_start(local, f); \
|
||||
M_MAP(TUPLE_DEFINE_OUT_SERIAL_FUNC , __VA_ARGS__) \
|
||||
assert( index == field_max); \
|
||||
ret |= f->m_interface->write_tuple_end(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
}
|
||||
#define TUPLE_DEFINE_OUT_SERIAL_FUNC(a) \
|
||||
f->m_interface->write_tuple_id(local, f, field_name, field_max, index); \
|
||||
TUPLE_CALL_OUT_SERIAL(a, f, el -> TUPLE_GET_FIELD a); \
|
||||
index++; \
|
||||
|
||||
#define TUPLE_DEFINE_IN_SERIAL(name, ...) \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(M_C(name,_t) el, m_serial_read_t f) { \
|
||||
assert (f != NULL && el != NULL); \
|
||||
int index = -1; \
|
||||
const int field_max = M_NARGS(__VA_ARGS__); \
|
||||
static const char *const field_name[] = \
|
||||
{ M_REDUCE(TUPLE_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
ret = f->m_interface->read_tuple_start(local, f); \
|
||||
while (ret == M_SERIAL_OK_CONTINUE) { \
|
||||
ret = f->m_interface->read_tuple_id(local, f, field_name, field_max, &index); \
|
||||
if (ret == M_SERIAL_OK_CONTINUE) { \
|
||||
assert (index >= 0 && index < field_max); \
|
||||
switch (1+index) { \
|
||||
M_MAP2(TUPLE_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \
|
||||
default: assert(0); \
|
||||
} \
|
||||
ret = (ret == M_SERIAL_OK_DONE) ? M_SERIAL_OK_CONTINUE : M_SERIAL_FAIL; \
|
||||
} \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
#define TUPLE_DEFINE_IN_SERIAL_FUNC(name, a) \
|
||||
case M_C4(name, _, TUPLE_GET_FIELD a, _value): \
|
||||
ret = TUPLE_CALL_IN_SERIAL(a, el -> TUPLE_GET_FIELD a, f); \
|
||||
break; \
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_INIT_MOVE(name, ...) \
|
||||
static inline void M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
|
||||
@@ -441,7 +505,10 @@ namespace m_tuple {
|
||||
#define TUPLEI_IF_ALL(method, ...) \
|
||||
M_IF(M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, method, __VA_ARGS__))
|
||||
|
||||
#define TUPLEI_OPLIST(name, ...) \
|
||||
// deferred
|
||||
#define TUPLEI_OPLIST_P1(arg) TUPLEI_OPLIST_P2 arg
|
||||
|
||||
#define TUPLEI_OPLIST_P2(name, ...) \
|
||||
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_C(name,_init)),), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
INIT_WITH(M_C(name, _init_set2)), \
|
||||
@@ -455,6 +522,8 @@ namespace m_tuple {
|
||||
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)),), \
|
||||
|
||||
+157
-71
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - VARIANT module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,14 +34,14 @@
|
||||
USAGE:
|
||||
VARIANT_DEF2(name, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */
|
||||
#define VARIANT_DEF2(name, ...) \
|
||||
VARIANTI_DEF2_A( (name, VARIANTI_INJECT_GLOBAL(__VA_ARGS__)) )
|
||||
VARIANTI_DEF2_P1( (name, VARIANTI_INJECT_GLOBAL(__VA_ARGS__)) )
|
||||
|
||||
/* Define the oplist of a variant.
|
||||
USAGE: VARIANT_OPLIST(name[, oplist of the first type, ...]) */
|
||||
#define VARIANT_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(VARIANTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
VARIANTI_OPLIST(__VA_ARGS__ ))
|
||||
(VARIANTI_OPLIST_P1((__VA_ARGS__, M_DEFAULT_OPLIST)), \
|
||||
VARIANTI_OPLIST_P1((__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
@@ -55,10 +55,10 @@
|
||||
#define VARIANTI_INJECT_OPLIST_B( f, ... ) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
|
||||
|
||||
#define VARIANTI_DEF2_A(...) \
|
||||
VARIANTI_DEF2_B __VA_ARGS__
|
||||
// Deferred evaluation
|
||||
#define VARIANTI_DEF2_P1(...) VARIANTI_DEF2_P2 __VA_ARGS__
|
||||
|
||||
#define VARIANTI_DEF2_B(name, ...) \
|
||||
#define VARIANTI_DEF2_P2(name, ...) \
|
||||
VARIANTI_DEFINE_TYPE(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_INIT(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_CLEAR(name, __VA_ARGS__) \
|
||||
@@ -83,6 +83,10 @@
|
||||
(VARIANTI_DEFINE_OUT_STR(name, __VA_ARGS__),) \
|
||||
VARIANTI_IF_ALL2(IN_STR, INIT, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_IN_STR(name, __VA_ARGS__),) \
|
||||
VARIANTI_IF_ALL(OUT_SERIAL, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_OUT_SERIAL(name, __VA_ARGS__),) \
|
||||
VARIANTI_IF_ALL2(IN_SERIAL, INIT, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_IN_SERIAL(name, __VA_ARGS__),) \
|
||||
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
|
||||
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
@@ -107,11 +111,14 @@
|
||||
#define VARIANTI_GET_HASH(f,t,o) M_GET_HASH o
|
||||
#define VARIANTI_GET_EQUAL(f,t,o) M_GET_EQUAL o
|
||||
#define VARIANTI_GET_STR(f,t,o) M_GET_GET_STR o
|
||||
#define VARIANTI_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
|
||||
#define VARIANTI_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
|
||||
#define VARIANTI_GET_IN_STR(f,t,o) M_GET_IN_STR o
|
||||
#define VARIANTI_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
|
||||
#define VARIANTI_GET_OUT_SERIAL(f,t,o) M_GET_OUT_SERIAL o
|
||||
#define VARIANTI_GET_IN_SERIAL(f,t,o) M_GET_IN_SERIAL o
|
||||
#define VARIANTI_GET_SWAP(f,t,o) M_GET_SWAP o
|
||||
|
||||
/* Call the methods through API */
|
||||
#define VARIANTI_CALL_INIT(t, ...) M_APPLY_API(VARIANTI_GET_INIT t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_INIT_SET(t, ...) M_APPLY_API(VARIANTI_GET_INIT_SET t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_INIT_MOVE(t, ...) M_APPLY_API(VARIANTI_GET_INIT_MOVE t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
@@ -122,11 +129,14 @@
|
||||
#define VARIANTI_CALL_HASH(t, ...) M_APPLY_API(VARIANTI_GET_HASH t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_EQUAL(t, ...) M_APPLY_API(VARIANTI_GET_EQUAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_GET_STR(t, ...) M_APPLY_API(VARIANTI_GET_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_PARSE_STR(t, ...) M_APPLY_API(VARIANTI_GET_PARSE_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_OUT_STR(t, ...) M_APPLY_API(VARIANTI_GET_OUT_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_IN_STR(t, ...) M_APPLY_API(VARIANTI_GET_IN_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_PARSE_STR(t, ...) M_APPLY_API(VARIANTI_GET_PARSE_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_OUT_SERIAL(t, ...) M_APPLY_API(VARIANTI_GET_OUT_SERIAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_IN_SERIAL(t, ...) M_APPLY_API(VARIANTI_GET_IN_SERIAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_SWAP(t, ...) M_APPLY_API(VARIANTI_GET_SWAP t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
|
||||
/* Define the type */
|
||||
#define VARIANTI_DEFINE_TYPE(name, ...) \
|
||||
enum M_C(name, _enum) { M_C(name, _EMPTY) \
|
||||
M_MAP2(VARIANTI_DEFINE_UNION_ELE, name, __VA_ARGS__) \
|
||||
@@ -139,20 +149,21 @@
|
||||
} M_C(name,_t)[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr);
|
||||
|
||||
#define VARIANTI_DEFINE_UNION_ELE(name, a) \
|
||||
, M_C4(name, _, VARIANTI_GET_FIELD a, _value)
|
||||
#define VARIANTI_DEFINE_TYPE_ELE(a) \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ;
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_INIT(name, ...) \
|
||||
static inline void M_C(name, _init)(M_C(name,_t) my) { \
|
||||
my->type = M_C(name, _EMPTY); \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_INIT_SET(name, ...) \
|
||||
static inline void M_C(name, _init_set)(M_C(name,_t) my , M_C(name,_t) const org) { \
|
||||
#define VARIANTI_DEFINE_INIT_SET(name, ...) \
|
||||
static inline void M_C(name, _init_set)(M_C(name,_t) my , \
|
||||
M_C(name,_t) const org) { \
|
||||
my->type = org->type; \
|
||||
switch (org->type) { \
|
||||
case M_C(name, _EMPTY): break; \
|
||||
@@ -168,13 +179,14 @@
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_SET(name, ...) \
|
||||
static inline void M_C(name, _set)(M_C(name,_t) my , M_C(name,_t) const org) { \
|
||||
static inline void M_C(name, _set)(M_C(name,_t) my , \
|
||||
M_C(name,_t) const org) { \
|
||||
if (my->type != org->type) { \
|
||||
/* Different types: clear previous, create new */ \
|
||||
/* Different types: clear previous one and create new */ \
|
||||
M_C(name, _clear)(my); \
|
||||
M_C(name, _init_set)(my, org); \
|
||||
} else { \
|
||||
/* Same type: optimize the settings */ \
|
||||
/* Same type: optimize the set */ \
|
||||
switch (org->type) { \
|
||||
case M_C(name, _EMPTY): break; \
|
||||
M_MAP2(VARIANTI_DEFINE_SET_FUNC, name, __VA_ARGS__) \
|
||||
@@ -205,7 +217,7 @@
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_TEST_P(name, ...) \
|
||||
static inline bool M_C(name, _empty_p)(M_C(name,_t) my) { \
|
||||
static inline bool M_C(name, _empty_p)(M_C(name,_t) const my) { \
|
||||
return my->type == M_C(name, _EMPTY); \
|
||||
} \
|
||||
static inline enum M_C(name, _enum) \
|
||||
@@ -214,7 +226,8 @@
|
||||
} \
|
||||
M_MAP2(VARIANTI_DEFINE_TEST_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_TEST_FUNC(name, a) \
|
||||
static inline bool M_C4(name, _, VARIANTI_GET_FIELD a, _p)(M_C(name,_t) my) { \
|
||||
static inline bool \
|
||||
M_C4(name, _, VARIANTI_GET_FIELD a, _p)(M_C(name,_t) const my) { \
|
||||
return my->type == M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
}
|
||||
|
||||
@@ -222,7 +235,8 @@
|
||||
#define VARIANTI_DEFINE_INIT_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_INIT_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_INIT_FIELD_FUNC(name, a) \
|
||||
static inline void M_C3(name, _init_, VARIANTI_GET_FIELD a)(M_C(name,_t) my) { \
|
||||
static inline void \
|
||||
M_C3(name, _init_, VARIANTI_GET_FIELD a)(M_C(name,_t) my) { \
|
||||
/* Reinit variable with the given value */ \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT(a, my -> value. VARIANTI_GET_FIELD a); \
|
||||
@@ -232,8 +246,9 @@
|
||||
#define VARIANTI_DEFINE_INIT_SETTER_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_INIT_SETTER_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_INIT_SETTER_FIELD_FUNC(name, a) \
|
||||
static inline void M_C3(name, _init_set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
static inline void \
|
||||
M_C3(name, _init_set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a const VARIANTI_GET_FIELD a ) { \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT_SET(a, my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
@@ -243,8 +258,9 @@
|
||||
#define VARIANTI_DEFINE_SETTER_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_SETTER_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_SETTER_FIELD_FUNC(name, a) \
|
||||
static inline void M_C3(name, _set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
static inline void \
|
||||
M_C3(name, _set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a const VARIANTI_GET_FIELD a ) { \
|
||||
if (my->type == M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
|
||||
VARIANTI_CALL_SET(a, my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
@@ -257,6 +273,7 @@
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_GETTER_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_GETTER_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_GETTER_FIELD_FUNC(name, a) \
|
||||
@@ -267,6 +284,7 @@
|
||||
} \
|
||||
return &my -> value . VARIANTI_GET_FIELD a; \
|
||||
} \
|
||||
\
|
||||
static inline VARIANTI_GET_TYPE a const * \
|
||||
M_C3(name, _cget_, VARIANTI_GET_FIELD a)(M_C(name,_t) const my) { \
|
||||
if (my->type != M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
|
||||
@@ -314,7 +332,8 @@
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_INIT_MOVE(name, ...) \
|
||||
static inline void M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
|
||||
static inline void \
|
||||
M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
|
||||
el -> type = org -> type; \
|
||||
switch (el->type) { \
|
||||
case M_C(name, _EMPTY): break; \
|
||||
@@ -331,7 +350,8 @@
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_MOVE(name, ...) \
|
||||
static inline void M_C(name, _move)(M_C(name,_t) el, M_C(name,_t) org) { \
|
||||
static inline void \
|
||||
M_C(name, _move)(M_C(name,_t) el, M_C(name,_t) org) { \
|
||||
M_C(name, _clear)(el); \
|
||||
M_C(name, _init_move)(el , org); \
|
||||
}
|
||||
@@ -340,8 +360,9 @@
|
||||
#define VARIANTI_DEFINE_MOVER(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_MOVER_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_MOVER_FUNC(name, a) \
|
||||
static inline void M_C3(name, _move_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
static inline void \
|
||||
M_C3(name, _move_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
M_C(name, _clear)(my); \
|
||||
/* Reinit variable with the given value */ \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
@@ -351,7 +372,8 @@
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_SWAP(name, ...) \
|
||||
static inline void M_C(name, _swap)(M_C(name,_t) el1, M_C(name,_t) el2) { \
|
||||
static inline void \
|
||||
M_C(name, _swap)(M_C(name,_t) el1, M_C(name,_t) el2) { \
|
||||
if (el1->type == el2->type) { \
|
||||
switch (el1->type) { \
|
||||
case M_C(name, _EMPTY): break; \
|
||||
@@ -402,6 +424,46 @@
|
||||
break;
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_PARSE_STR(name, ...) \
|
||||
static inline bool M_C(name, _parse_str)(M_C(name,_t) el, \
|
||||
const char str[], \
|
||||
const char **endp) { \
|
||||
assert (str != NULL && el != NULL); \
|
||||
bool success = false; \
|
||||
char variantTypeBuf[M_MAX_IDENTIFIER_LENGTH+1]; \
|
||||
int c = *str++; \
|
||||
unsigned int i = 0; \
|
||||
M_C(name, _clean)(el); \
|
||||
if (c != '@') goto exit; \
|
||||
/* First read the name of the type */ \
|
||||
c = *str++; \
|
||||
while (c != '@' && c != 0 && i < sizeof(variantTypeBuf) - 1) { \
|
||||
variantTypeBuf[i++] = c; \
|
||||
c = *str++; \
|
||||
} \
|
||||
if (c != '@') goto exit; \
|
||||
variantTypeBuf[i++] = 0; \
|
||||
assert(i < sizeof(variantTypeBuf)); \
|
||||
/* In function of the type */ \
|
||||
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
|
||||
el->type = M_C(name, _EMPTY); \
|
||||
} \
|
||||
M_MAP2(VARIANTI_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \
|
||||
else goto exit; \
|
||||
success = (*str++ == '@'); \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
}
|
||||
#define VARIANTI_DEFINE_PARSE_STR_FUNC(name, a) \
|
||||
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
|
||||
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
|
||||
bool b = VARIANTI_CALL_PARSE_STR(a, el -> value . VARIANTI_GET_FIELD a, str, &str); \
|
||||
if (!b) goto exit; \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_OUT_STR(name, ...) \
|
||||
static inline void M_C(name, _out_str)(FILE *f, \
|
||||
M_C(name,_t) const el) { \
|
||||
@@ -424,10 +486,11 @@
|
||||
static inline bool M_C(name, _in_str)(M_C(name,_t) el, \
|
||||
FILE *f) { \
|
||||
assert (f != NULL && el != NULL); \
|
||||
/* A buffer of 400 bytes should be more than enough for all variant names... */ \
|
||||
char variantTypeBuf[400]; \
|
||||
char variantTypeBuf[M_MAX_IDENTIFIER_LENGTH+1]; \
|
||||
M_C(name, _clean)(el); \
|
||||
if (fgetc(f) != '@') return false; \
|
||||
/* First read the name of the type */ \
|
||||
bool b = true; \
|
||||
int c = fgetc(f); \
|
||||
unsigned int i = 0; \
|
||||
while (c != '@' && c != EOF && i < sizeof(variantTypeBuf) - 1) { \
|
||||
@@ -437,63 +500,81 @@
|
||||
if (c != '@') return false; \
|
||||
variantTypeBuf[i++] = 0; \
|
||||
assert(i < sizeof(variantTypeBuf)); \
|
||||
M_C(name, _clear)(el); \
|
||||
/* In function of the type */ \
|
||||
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
|
||||
el->type = M_C(name, _EMPTY); \
|
||||
} \
|
||||
M_MAP2(VARIANTI_DEFINE_IN_STR_FUNC , name, __VA_ARGS__) \
|
||||
else return false; \
|
||||
return fgetc(f) == '@'; \
|
||||
else { b = false; } \
|
||||
return b && (fgetc(f) == '@'); \
|
||||
}
|
||||
#define VARIANTI_DEFINE_IN_STR_FUNC(name, a) \
|
||||
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
|
||||
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
|
||||
bool b = VARIANTI_CALL_IN_STR(a, el -> value . VARIANTI_GET_FIELD a, f); \
|
||||
if (!b) return false; \
|
||||
b = VARIANTI_CALL_IN_STR(a, el -> value . VARIANTI_GET_FIELD a, f); \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_PARSE_STR(name, ...) \
|
||||
static inline bool M_C(name, _parse_str)(M_C(name,_t) el, \
|
||||
const char str[], \
|
||||
const char **endp) { \
|
||||
assert (str != NULL && el != NULL); \
|
||||
bool success = false; \
|
||||
/* A buffer of 400 bytes should be more than enough for all variant names... */ \
|
||||
char variantTypeBuf[400]; \
|
||||
int c = *str++; \
|
||||
unsigned int i = 0; \
|
||||
if (c != '@') goto exit; \
|
||||
/* First read the name of the type */ \
|
||||
c = *str++; \
|
||||
while (c != '@' && c != 0 && i < sizeof(variantTypeBuf) - 1) { \
|
||||
variantTypeBuf[i++] = c; \
|
||||
c = *str++; \
|
||||
#define VARIANTI_STRINGIFY_NAME(a) \
|
||||
M_APPLY(M_AS_STR, VARIANTI_GET_FIELD a)
|
||||
#define VARIANTI_DEFINE_OUT_SERIAL(name, ...) \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, \
|
||||
M_C(name,_t) const el) { \
|
||||
const int field_max = M_NARGS(__VA_ARGS__); \
|
||||
static const char *const field_name[] = \
|
||||
{ M_REDUCE(VARIANTI_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
|
||||
assert (f != NULL && f->m_interface != NULL && el != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
switch (el->type) { \
|
||||
case M_C(name, _EMPTY): \
|
||||
return f->m_interface->write_variant_start(local, f, field_name, field_max, -1); \
|
||||
break; \
|
||||
M_MAP2(VARIANTI_DEFINE_OUT_SERIAL_FUNC , name, __VA_ARGS__) \
|
||||
default: assert(false); break; \
|
||||
} \
|
||||
if (c != '@') goto exit; \
|
||||
variantTypeBuf[i++] = 0; \
|
||||
assert(i < sizeof(variantTypeBuf)); \
|
||||
M_C(name, _clear)(el); \
|
||||
/* In function of the type */ \
|
||||
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
|
||||
el->type = M_C(name, _EMPTY); \
|
||||
} \
|
||||
M_MAP2(VARIANTI_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \
|
||||
else goto exit; \
|
||||
success = (*str++ == '@'); \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
ret |= f->m_interface->write_variant_end(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
}
|
||||
#define VARIANTI_DEFINE_PARSE_STR_FUNC(name, a) \
|
||||
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
|
||||
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
#define VARIANTI_DEFINE_OUT_SERIAL_FUNC(name, a) \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
ret = f->m_interface->write_variant_start(local, f, field_name, field_max, \
|
||||
M_C4(name, _, VARIANTI_GET_FIELD a, _value) -1); \
|
||||
VARIANTI_CALL_OUT_SERIAL(a, f, el -> value . VARIANTI_GET_FIELD a); \
|
||||
break;
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_IN_SERIAL(name, ...) \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(M_C(name,_t) el, \
|
||||
m_serial_read_t f) { \
|
||||
const int field_max = M_NARGS(__VA_ARGS__); \
|
||||
static const char *const field_name[] = \
|
||||
{ M_REDUCE(VARIANTI_STRINGIFY_NAME, M_ID, __VA_ARGS__) }; \
|
||||
assert (f != NULL && f->m_interface != NULL && el != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
int id = -1; \
|
||||
M_C(name, _clean)(el); \
|
||||
ret = f->m_interface->read_variant_start(local, f, field_name, field_max, &id); \
|
||||
if (ret != M_SERIAL_OK_CONTINUE) return ret; \
|
||||
assert (id >= 0 && id < field_max); \
|
||||
el->type = (enum M_C(name, _enum))(id+1); \
|
||||
switch (id+1) { \
|
||||
M_MAP2(VARIANTI_DEFINE_IN_SERIAL_FUNC , name, __VA_ARGS__) \
|
||||
default: assert(false); break; \
|
||||
} \
|
||||
if (ret == M_SERIAL_OK_DONE) \
|
||||
ret = f->m_interface->read_variant_end(local, f); \
|
||||
return ret; \
|
||||
}
|
||||
#define VARIANTI_DEFINE_IN_SERIAL_FUNC(name, a) \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
|
||||
bool b = VARIANTI_CALL_PARSE_STR(a, el -> value . VARIANTI_GET_FIELD a, str, &str); \
|
||||
if (!b) goto exit; \
|
||||
}
|
||||
ret = VARIANTI_CALL_IN_SERIAL(a, el -> value . VARIANTI_GET_FIELD a, f); \
|
||||
break; \
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_CLEAN_FUNC(name, ...) \
|
||||
@@ -504,7 +585,10 @@
|
||||
} \
|
||||
|
||||
|
||||
#define VARIANTI_OPLIST(name, ...) \
|
||||
// deferred
|
||||
#define VARIANTI_OPLIST_P1(arg) VARIANTI_OPLIST_P2 arg
|
||||
|
||||
#define VARIANTI_OPLIST_P2(name, ...) \
|
||||
(INIT(M_C(name,_init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
SET(M_C(name,_set)), \
|
||||
@@ -518,6 +602,8 @@
|
||||
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)),), \
|
||||
|
||||
+59
-33
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB / WORKER - Extra worker interface
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,13 +25,22 @@
|
||||
#ifndef MSTARLIB_WORKER_H
|
||||
#define MSTARLIB_WORKER_H
|
||||
|
||||
#ifndef WORKER_DISABLE
|
||||
/* The User Code can define M_USE_WORKER to 0 to disable the use of workers.
|
||||
The macros / functions are then defined to only use one core.
|
||||
By default, the behavior is to use workers.
|
||||
*/
|
||||
#ifndef M_USE_WORKER
|
||||
# define M_USE_WORKER 1
|
||||
#endif
|
||||
|
||||
|
||||
#if M_USE_WORKER
|
||||
|
||||
#include "m-atomic.h"
|
||||
#include "m-buffer.h"
|
||||
#include "m-mutex.h"
|
||||
|
||||
/* Needed header for detection of how many core on the system */
|
||||
/* Include needed system header for detection of how many core on the system */
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) \
|
||||
@@ -39,7 +48,7 @@
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define WORKER_USE_SYSCTL
|
||||
# define M_USE_WORKER_SYSCTL 1
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
@@ -53,29 +62,35 @@
|
||||
if C++, it will use Lambda function (and std::function) instead
|
||||
(It doesn't support pre-C++11 compiler).
|
||||
|
||||
Otherwise go with nested function for the MACRO version.
|
||||
Otherwise go with nested function (GCC) for the MACRO version.
|
||||
|
||||
This behavior can be overriden by User Code by defining to 1 or 0 the
|
||||
following macros:
|
||||
* M_USE_WORKER_CPP_FUNCTION
|
||||
* M_USE_WORKER_CLANG_BLOCK
|
||||
*/
|
||||
#if defined(__cplusplus) && !defined(WORKER_USE_CPP_FUNCTION)
|
||||
# define WORKER_USE_CPP_FUNCTION 1
|
||||
|
||||
#if defined(__cplusplus) && !defined(M_USE_WORKER_CPP_FUNCTION)
|
||||
# define M_USE_WORKER_CPP_FUNCTION 1
|
||||
# include <functional>
|
||||
#elif defined(__has_extension) && !defined(WORKER_USE_CLANG_BLOCK)
|
||||
#elif defined(__has_extension) && !defined(M_USE_WORKER_CLANG_BLOCK)
|
||||
# if __has_extension(blocks)
|
||||
# define WORKER_USE_CLANG_BLOCK 1
|
||||
# define M_USE_WORKER_CLANG_BLOCK 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef WORKER_USE_CLANG_BLOCK
|
||||
# define WORKER_USE_CLANG_BLOCK 0
|
||||
#ifndef M_USE_WORKER_CLANG_BLOCK
|
||||
# define M_USE_WORKER_CLANG_BLOCK 0
|
||||
#endif
|
||||
#ifndef WORKER_USE_CPP_FUNCTION
|
||||
# define WORKER_USE_CPP_FUNCTION 0
|
||||
#ifndef M_USE_WORKER_CPP_FUNCTION
|
||||
# define M_USE_WORKER_CPP_FUNCTION 0
|
||||
#endif
|
||||
|
||||
/* Control that not both options are selected at the same time.
|
||||
Note: there are not really incompatible, but if we use C++ we shall go to
|
||||
lambda directly (there is no need to support blocks)! */
|
||||
#if WORKER_USE_CLANG_BLOCK && WORKER_USE_CPP_FUNCTION
|
||||
# error WORKER_USE_CPP_FUNCTION and WORKER_USE_CLANG_BLOCK are both defined (not supported).
|
||||
lambda directly (there is no need to support blocks). */
|
||||
#if M_USE_WORKER_CLANG_BLOCK && M_USE_WORKER_CPP_FUNCTION
|
||||
# error M_USE_WORKER_CPP_FUNCTION and M_USE_WORKER_CLANG_BLOCK are both defined. This is not supported.
|
||||
#endif
|
||||
|
||||
/* This type defines a work order */
|
||||
@@ -83,10 +98,10 @@ typedef struct work_order_s {
|
||||
struct worker_sync_s *block;
|
||||
void * data;
|
||||
void (*func) (void *data);
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
#if M_USE_WORKER_CLANG_BLOCK
|
||||
void (^blockFunc)(void *data);
|
||||
#endif
|
||||
#if WORKER_USE_CPP_FUNCTION
|
||||
#if M_USE_WORKER_CPP_FUNCTION
|
||||
std::function<void(void*)> function;
|
||||
#endif
|
||||
} worker_order_t;
|
||||
@@ -95,7 +110,7 @@ typedef struct work_order_s {
|
||||
* * MACRO to be used to send an empty order to stop the thread
|
||||
* * MACRO to complete the not-used fields
|
||||
*/
|
||||
#if WORKER_USE_CLANG_BLOCK || WORKER_USE_CPP_FUNCTION
|
||||
#if M_USE_WORKER_CLANG_BLOCK || M_USE_WORKER_CPP_FUNCTION
|
||||
# define WORKER_EMPTY_ORDER { NULL, NULL, NULL, NULL }
|
||||
# define WORKER_EXTRA_ORDER , NULL
|
||||
#else
|
||||
@@ -103,13 +118,27 @@ typedef struct work_order_s {
|
||||
# define WORKER_EXTRA_ORDER
|
||||
#endif
|
||||
|
||||
/* As in C++, it uses std::function, M_POD_OPLIST
|
||||
is not sufficient for initilization of the structure.
|
||||
So let's use C++ constructor, destructor and copy constructor */
|
||||
#if M_USE_WORKER_CPP_FUNCTION
|
||||
# define WORKER_CPP_INIT(x) (new (&(x)) worker_order_t())
|
||||
# define WORKER_CPP_INIT_SET(x, y) (new (&(x)) worker_order_t(y))
|
||||
# define WORKER_CPP_SET(x, y) ((x) = (y))
|
||||
# define WORKER_CPP_CLEAR(x) ((&(x))->~worker_order_t())
|
||||
# define WORKER_CPP_INIT_MOVE(x,y) (new (&(x)) worker_order_t(y), ((&(y))->~worker_order_t()))
|
||||
# define WORKER_OPLIST (INIT(WORKER_CPP_INIT), INIT_SET(WORKER_CPP_INIT_SET), SET(WORKER_CPP_SET), CLEAR(WORKER_CPP_CLEAR),INIT_MOVE(WORKER_CPP_INIT_MOVE) )
|
||||
#else
|
||||
# define WORKER_OPLIST M_POD_OPLIST
|
||||
#endif
|
||||
|
||||
/* This type defines a worker represented as a thread */
|
||||
typedef struct worker_thread_s {
|
||||
m_thread_t id;
|
||||
} worker_thread_t;
|
||||
|
||||
/* Let's define the queue which will manage all work orders */
|
||||
BUFFER_DEF(worker_queue, worker_order_t, 0, BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, M_POD_OPLIST)
|
||||
BUFFER_DEF(worker_queue, worker_order_t, 0, BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, WORKER_OPLIST)
|
||||
|
||||
/* This type defines the state of the workers */
|
||||
typedef struct worker_s {
|
||||
@@ -146,7 +175,7 @@ workeri_get_cpu_count(void)
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#elif defined(WORKER_USE_SYSCTL)
|
||||
#elif defined(M_USE_WORKER_SYSCTL)
|
||||
int nm[2];
|
||||
int count = 0;
|
||||
size_t len = sizeof (count);
|
||||
@@ -167,13 +196,13 @@ workeri_exec(worker_order_t *w)
|
||||
{
|
||||
assert (w!= NULL && w->block != NULL);
|
||||
//printf ("Starting thread with data %p\n", w->data);
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
#if M_USE_WORKER_CLANG_BLOCK
|
||||
//printf ("Running %s f=%p b=%p\n", (w->func == NULL) ? "Blocks" : "Function", w->func, w->blockFunc);
|
||||
if (w->func == NULL)
|
||||
w->blockFunc(w->data);
|
||||
else
|
||||
#endif
|
||||
#if WORKER_USE_CPP_FUNCTION
|
||||
#if M_USE_WORKER_CPP_FUNCTION
|
||||
//printf ("Running %s f=%p b=%p\n", (w->function == NULL) ? "Lambda" : "Function", w->func, w->blockFunc);
|
||||
if (w->function)
|
||||
w->function(w->data);
|
||||
@@ -291,7 +320,7 @@ worker_spawn(worker_sync_t block, void (*func)(void *data), void *data)
|
||||
(*func) (data);
|
||||
}
|
||||
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
#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
|
||||
@@ -310,7 +339,7 @@ worker_spawn_block(worker_sync_t block, void (^func)(void *data), void *data)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WORKER_USE_CPP_FUNCTION
|
||||
#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
|
||||
@@ -377,12 +406,12 @@ worker_count(worker_t g)
|
||||
'input' is the list of input variables of the 'core' block within "( )"
|
||||
'output' is the list of output variables of the 'core' block within "( )"
|
||||
Output variables are only available after a synchronisation block. */
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
#if M_USE_WORKER_CLANG_BLOCK
|
||||
#define WORKER_SPAWN(_block, _input, _core, _output) \
|
||||
WORKER_DEF_DATA(_input, _output) \
|
||||
WORKER_DEF_SUBBLOCK(_input, _output, _core) \
|
||||
worker_spawn_block ((_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
|
||||
#elif WORKER_USE_CPP_FUNCTION
|
||||
#elif M_USE_WORKER_CPP_FUNCTION
|
||||
// TODO: Explicit pass all arguments by reference.
|
||||
#define WORKER_SPAWN(_block, _input, _core, _output) \
|
||||
worker_spawn_function ((_block), [&](void *param) {(void)param ; _core } , NULL)
|
||||
@@ -452,12 +481,9 @@ worker_count(worker_t g)
|
||||
M_MAP(WORKER_PROPAGATE_SINGLE_OUTPUT, __VA_ARGS__)
|
||||
|
||||
|
||||
#else /* WORKER_DISABLE */
|
||||
#else /* M_USE_WORKER */
|
||||
|
||||
|
||||
/* User has defined WORKER_DISABLE to disable the use of workers.
|
||||
Define empty types and empty functions
|
||||
*/
|
||||
/* Define empty types and empty functions to not use any worker */
|
||||
|
||||
typedef struct worker_block_s {
|
||||
int x;
|
||||
@@ -475,6 +501,6 @@ typedef struct worker_s {
|
||||
#define worker_count(w) 1
|
||||
#define WORKER_SPAWN(b, i, c, o) do { c } while (0)
|
||||
|
||||
#endif /* WORKER_DISABLE */
|
||||
#endif /* M_USE_WORKER */
|
||||
|
||||
#endif
|
||||
+30
-17
@@ -1,15 +1,24 @@
|
||||
CC=cc -std=c99
|
||||
WCFLAGS=-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 -ftrapv
|
||||
WCFLAGS_GCC=-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 -ftrapv
|
||||
WCFLAGS_CLANG=-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 -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
|
||||
WCFLAGS_CPP=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wno-unused-function -ftrapv
|
||||
XCFLAGS=
|
||||
CFLAGS=$(WCFLAGS) $(XCFLAGS)
|
||||
CFLAGS=$(WCFLAGS_GCC) $(XCFLAGS)
|
||||
CPPFLAGS=-I..
|
||||
LDFLAGS=-pthread
|
||||
RM=rm -f
|
||||
RMDIR=rm -rf
|
||||
LOG_COMPILER=
|
||||
SED=sed -e
|
||||
|
||||
all:
|
||||
@echo "Nothing to be done."
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Rules
|
||||
#####################################################################################################
|
||||
|
||||
.POSIX:
|
||||
.PHONY: valgrind scan-build sanitize check clean *.log coverage synthesis
|
||||
.SUFFIXES:
|
||||
@@ -20,7 +29,7 @@ all:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -g $(LDFLAGS) $< -o $@
|
||||
|
||||
.c.depend:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -MM -MT `echo $< |sed -e 's/\.c/\.exe/g'` $< > $@
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -MM -MT `echo $< |$(SED) 's/\.c/\.exe/g'` $< > $@
|
||||
|
||||
.exe.log:
|
||||
$(LOG_COMPILER) ./$<
|
||||
@@ -42,28 +51,30 @@ all:
|
||||
|
||||
-include depend
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Test suite
|
||||
#####################################################################################################
|
||||
|
||||
depend:
|
||||
$(RM) depend
|
||||
echo "" > depend
|
||||
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.depend/g'`
|
||||
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.depend/g'`
|
||||
cat *.depend > depend
|
||||
$(RM) *.depend
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Clean
|
||||
#####################################################################################################
|
||||
|
||||
check: depend
|
||||
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.log/g'`
|
||||
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.log/g'`
|
||||
|
||||
clean:
|
||||
$(RM) *.exe *.s *~ *.o *.log *.valgrind ./a*.dat clang_output_* *.c.c *.end.c *.first.c *.middle.c
|
||||
$(RM) -rf coverage *.gcda *.gcno *.gcov all.info test.tar coverage-dir mlib-report *.synt
|
||||
$(RM) *.exe *.s *~ *.o *.log a*.dat
|
||||
$(RM) *.valgrind *.gcda *.gcno *.gcov all.info test.tar *.synt clang_output_* *.c.c *.end.c *.first.c *.middle.c
|
||||
$(RMDIR) coverage coverage-dir mlib-report
|
||||
|
||||
distclean: clean
|
||||
$(RM) *.depend depend
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Code generation analysis
|
||||
#####################################################################################################
|
||||
@@ -89,12 +100,13 @@ gene-queue:
|
||||
gene-string:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mstring.c && less tgen-mstring.s
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Coverage
|
||||
#####################################################################################################
|
||||
|
||||
coverage:
|
||||
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.gcov/g'`
|
||||
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.gcov/g'`
|
||||
$(MAKE) synthesis
|
||||
|
||||
SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
|
||||
@@ -102,6 +114,7 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
|
||||
M-BITSET ../m-bitset.h test-mbitset.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-DEQUE test-mdeque.c.c test-mdeque.synt \
|
||||
M-DICT test-mdict.c.c test-mdict.synt \
|
||||
@@ -112,6 +125,8 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.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 \
|
||||
M-SERIAL-JSON ../m-serial-json.h test-mserial-json.synt \
|
||||
M-SHARED test-mshared.c.c test-mshared.synt \
|
||||
M-SNAPSHOT test-msnapshot.c.c test-msnapshot.synt \
|
||||
M-STRING ../m-string.h test-mstring.synt \
|
||||
@@ -130,12 +145,13 @@ synthesis:
|
||||
done
|
||||
@printf "+-------------------------------+-------------------------------+\n"
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Code analyzer tools
|
||||
#####################################################################################################
|
||||
|
||||
valgrind:
|
||||
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.valgrind/g'`
|
||||
$(MAKE) `ls test-*.c|$(SED) 's/\.c/\.valgrind/g'`
|
||||
|
||||
scan-build: clean
|
||||
$(RM) -rf ./mlib-report
|
||||
@@ -150,9 +166,6 @@ sanitize:
|
||||
# Test All
|
||||
#####################################################################################################
|
||||
|
||||
WCFLAGS_CLANG=-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 -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
|
||||
WCFLAGS_CPP=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wno-unused-function -ftrapv
|
||||
|
||||
checkall:
|
||||
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c99"
|
||||
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c99 -m32"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
|
||||
+32
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,9 +25,11 @@
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-deque.h"
|
||||
#include "test-obj.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
#include "test-obj.h"
|
||||
|
||||
typedef struct over_s {
|
||||
unsigned long data;
|
||||
ILIST_INTERFACE(ilist_over, over_s);
|
||||
@@ -42,6 +44,8 @@ ILIST_DEF(ilist_over, over_t, M_POD_OPLIST)
|
||||
LIST_DEF(list_string, string_t, STRING_OPLIST)
|
||||
DEQUE_DEF(deque_obj, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
#define M_OPL_deque_obj_t() DEQUE_OPLIST(deque_obj, TESTOBJ_CMP_OPLIST)
|
||||
DICT_DEF2(dict_obj, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
|
||||
LIST_DUAL_PUSH_DEF(dlist_int, int)
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
@@ -50,9 +54,13 @@ ALGO_DEF(algo_list, LIST_OPLIST(list_int))
|
||||
ALGO_DEF(algo_over, ILIST_OPLIST(ilist_over, M_POD_OPLIST))
|
||||
ALGO_DEF(algo_string, LIST_OPLIST(list_string, STRING_OPLIST))
|
||||
ALGO_DEF(algo_deque, deque_obj_t)
|
||||
ALGO_DEF(algo_dict, DICT_OPLIST(dict_obj, STRING_OPLIST, TESTOBJ_OPLIST))
|
||||
END_COVERAGE
|
||||
ALGO_DEF(algo_dlist, LIST_OPLIST(list_int))
|
||||
|
||||
/* Helper functions */
|
||||
int g_min, g_max, g_count;
|
||||
|
||||
static void g_f(int n)
|
||||
{
|
||||
assert (g_min <= n && n <= g_max);
|
||||
@@ -91,6 +99,8 @@ static bool func_test_both_even_or_odd(int a, int b)
|
||||
return (a&1) == (b&1);
|
||||
}
|
||||
|
||||
|
||||
/* Tests starts */
|
||||
static void test_list(void)
|
||||
{
|
||||
list_int_t l;
|
||||
@@ -156,11 +166,29 @@ static void test_list(void)
|
||||
assert (!algo_list_sort_p(l));
|
||||
algo_list_sort(l);
|
||||
assert (algo_list_sort_p(l));
|
||||
|
||||
|
||||
list_int_clear(l);
|
||||
|
||||
ALGO_INIT_VA(l, LIST_OPLIST(list_int), 1, 2, 3, 4, 5);
|
||||
assert (list_int_size(l) == 5);
|
||||
|
||||
algo_list_remove_val(l, 3);
|
||||
assert (list_int_size(l) == 4);
|
||||
algo_list_find (it1, l, 3);
|
||||
assert (list_int_end_p (it1));
|
||||
algo_list_remove_val(l, 3);
|
||||
assert (list_int_size(l) == 4);
|
||||
|
||||
algo_list_remove_if(l, func_test_42);
|
||||
assert (list_int_size(l) == 4);
|
||||
list_int_push_back(l, 42);
|
||||
list_int_push_back(l, 43);
|
||||
list_int_push_back(l, 42);
|
||||
algo_list_remove_if(l, func_test_42);
|
||||
assert (list_int_size(l) == 5);
|
||||
algo_list_find (it1, l, 42);
|
||||
assert (list_int_end_p (it1));
|
||||
|
||||
list_int_clear(l);
|
||||
}
|
||||
|
||||
@@ -397,6 +425,7 @@ static void test_insert(void)
|
||||
ALGO_LET_INIT_VA(b, aint_t, -1, -2, -3) {
|
||||
lint_it_t i;
|
||||
lint_it(i, a);
|
||||
// Insert it after first element
|
||||
ALGO_INSERT_AT(a, lint_t, i, b, aint_t);
|
||||
// TBC: order ok for the list (the reverse is confusing...)?
|
||||
ALGO_LET_INIT_VA(c, lint_t, 1, 2, 3, -3, -2, -1, 4) {
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -128,7 +128,15 @@ static void test_uint(void)
|
||||
assert(*array_uint_cget(v2, 500) == 0);
|
||||
assert(array_uint_size(v2) == 1005);
|
||||
assert(*array_uint_cget(v2, 1004) == 0);
|
||||
|
||||
array_uint_set_at(v2, 1004, 465487);
|
||||
array_uint_insert_v (v2, 500, 0);
|
||||
assert(array_uint_size(v2) == 1005);
|
||||
assert(*array_uint_cget(v2, 1004) == 465487);
|
||||
array_uint_insert_v (v2, 1004, 10000);
|
||||
assert(array_uint_size(v2) == 11005);
|
||||
assert(*array_uint_cget(v2, 11004) == 465487);
|
||||
array_uint_resize(v2, 1005);
|
||||
|
||||
array_uint_init(v);
|
||||
array_uint_swap(v,v2);
|
||||
assert(array_uint_size(v) == 1005);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
|
||||
+190
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,12 +30,17 @@
|
||||
START_COVERAGE
|
||||
BPTREE_DEF2(btree, 3, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
END_COVERAGE
|
||||
|
||||
BPTREE_DEF2(btree_my, 3, testobj_t, TESTOBJ_CMP_OPLIST, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
|
||||
BPTREE_DEF2(btree_string, 5, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
|
||||
BPTREE_DEF2(btree_int, 17, int, int)
|
||||
|
||||
BPTREE_DEF(btree_intset, 13, int)
|
||||
BPTREE_DEF(btree_myset, 15, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
|
||||
BPTREE_MULTI_DEF2(multimap, 3, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
BPTREE_MULTI_DEF(multiset, 6, int, M_DEFAULT_OPLIST)
|
||||
|
||||
static void test1(void)
|
||||
{
|
||||
btree_t b;
|
||||
@@ -68,6 +73,7 @@ static void test1(void)
|
||||
btree_set_at(b, 5, 39);
|
||||
assert (btree_size(b) == 6);
|
||||
assert (*btree_get(b, -25) == 38);
|
||||
assert (*btree_get(b, 5) == 39);
|
||||
btree_set_at(b, 5, 40);
|
||||
assert (btree_size(b) == 6);
|
||||
assert (*btree_get(b, 5) == 40);
|
||||
@@ -194,9 +200,88 @@ static void test3(void)
|
||||
}
|
||||
assert(btree_size(b) == 0);
|
||||
|
||||
const int prime[5] = { 3, 5, 11, 13, 17};
|
||||
const int prime_size = 5;
|
||||
for(int k = 0; k < prime_size; k++) {
|
||||
int p = prime[k];
|
||||
for(int i = 0; i < 1024; i++) {
|
||||
btree_set_at(b, (i*p) % 1024, i);
|
||||
assert(*btree_cget(b, (i*p) % 1024) == i);
|
||||
}
|
||||
assert(btree_size(b) == 1024);
|
||||
for(int i = 0; i < 1024; i++) {
|
||||
assert(*btree_cget(b, (i*p) % 1024) == i);
|
||||
}
|
||||
for(int i = 0; i < 1024; i++) {
|
||||
bool r = btree_erase(b, (i*p) % 1024);
|
||||
assert(r);
|
||||
assert(btree_cget(b, (i*p) % 1024) == NULL);
|
||||
}
|
||||
assert(btree_size(b) == 0);
|
||||
}
|
||||
|
||||
for(int k = 0; k < prime_size; k++) {
|
||||
int p = prime[k];
|
||||
for(int i = 0; i < 512; i++) {
|
||||
btree_set_at(b, (i*p) % 1024, i);
|
||||
assert(*btree_cget(b, (i*p) % 1024) == i);
|
||||
}
|
||||
assert(btree_size(b) == 512);
|
||||
for(int i = 256; i < 512; i++) {
|
||||
bool r = btree_erase(b, (i*p) % 1024);
|
||||
assert(r);
|
||||
assert(btree_cget(b, (i*p) % 1024) == NULL);
|
||||
}
|
||||
for(int i = 256; i < 1024; i++) {
|
||||
btree_set_at(b, (i*p) % 1024, i);
|
||||
assert(*btree_cget(b, (i*p) % 1024) == i);
|
||||
}
|
||||
assert(btree_size(b) == 1024);
|
||||
for(int i = 0; i < 1024; i++) {
|
||||
bool r = btree_erase(b, (i*p) % 1024);
|
||||
assert(r);
|
||||
assert(btree_cget(b, (i*p) % 1024) == NULL);
|
||||
}
|
||||
assert(btree_size(b) == 0);
|
||||
}
|
||||
|
||||
btree_clear(b);
|
||||
}
|
||||
|
||||
static void test3_string(void)
|
||||
{
|
||||
string_t key, value;
|
||||
string_init(key);
|
||||
string_init(value);
|
||||
btree_string_t b;
|
||||
btree_string_init(b);
|
||||
|
||||
const int prime[6] = { 1, 3, 5, 11, 13, 17};
|
||||
const int prime_size = 6;
|
||||
for(int k = 0; k < prime_size; k++) {
|
||||
int p = prime[k];
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
string_printf(key, "%d", (i*p) % 1024);
|
||||
string_printf(value, "%d", 1000*i);
|
||||
btree_string_set_at(b, key, value);
|
||||
assert(string_equal_p(value, *btree_string_cget(b, key)));
|
||||
}
|
||||
assert(btree_string_size(b) == 1000);
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
string_printf(key, "%d", (i*p) % 1024);
|
||||
string_printf(value, "%d", 1000*i);
|
||||
assert(string_equal_p(value, *btree_string_cget(b, key)));
|
||||
bool r = btree_string_erase(b, key);
|
||||
assert (r == true);
|
||||
}
|
||||
assert(btree_string_size(b) == 0);
|
||||
}
|
||||
|
||||
btree_string_clear(b);
|
||||
string_clear(key);
|
||||
string_clear(value);
|
||||
}
|
||||
|
||||
static void test4(void)
|
||||
{
|
||||
btree_t b1, b2;
|
||||
@@ -282,7 +367,7 @@ static void test5(void)
|
||||
assert (btree_it_equal_p(it, it2));
|
||||
|
||||
i = 500;
|
||||
for(btree_it_from(it, b, 500); !btree_it_to_p(it, 600); btree_next(it)) {
|
||||
for(btree_it_from(it, b, 500); !btree_it_until_p(it, 600); btree_next(it)) {
|
||||
const btree_type_t *item = btree_cref(it);
|
||||
assert (*item->key_ptr == i);
|
||||
assert (*item->value_ptr == 1000*i);
|
||||
@@ -292,7 +377,7 @@ static void test5(void)
|
||||
|
||||
btree_erase (b, 500);
|
||||
i = 501;
|
||||
for(btree_it_from(it, b, 500); !btree_it_to_p(it, 600); btree_next(it)) {
|
||||
for(btree_it_from(it, b, 500); !btree_it_until_p(it, 600); btree_next(it)) {
|
||||
const btree_type_t *item = btree_cref(it);
|
||||
assert (*item->key_ptr == i);
|
||||
assert (*item->value_ptr == 1000*i);
|
||||
@@ -302,7 +387,7 @@ static void test5(void)
|
||||
|
||||
btree_it_from(it, b, 1000);
|
||||
assert (btree_end_p(it));
|
||||
assert (btree_it_to_p(it, 1001));
|
||||
assert (btree_it_until_p(it, 1001));
|
||||
|
||||
btree_clear(b);
|
||||
}
|
||||
@@ -414,14 +499,114 @@ static void test_io_set(void)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Dump code for debug
|
||||
static void multimap_dump_node(multimap_node_t n)
|
||||
{
|
||||
while (n) {
|
||||
int num = n->num < 0 ? -n->num : n->num;
|
||||
printf ("[");
|
||||
for(int i = 0; i < num; i++)
|
||||
printf ("%d, ", n->key[i]);
|
||||
printf ("] ");
|
||||
n = n->next;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
static void
|
||||
multimap_dump(multimap_t b)
|
||||
{
|
||||
multimap_node_t n = b->root;
|
||||
while (n->num > 0) {
|
||||
multimap_dump_node(n);
|
||||
n = n->kind.node[0];
|
||||
}
|
||||
multimap_dump_node(n);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_multimap(void)
|
||||
{
|
||||
multimap_t b;
|
||||
multimap_init(b);
|
||||
|
||||
for (int size = 20; size < 1000; size += 10) {
|
||||
for(int i = 0; i < size; i++) {
|
||||
multimap_set_at(b, i/4, i);
|
||||
}
|
||||
assert(multimap_size(b) == (unsigned) size);
|
||||
|
||||
for (int k = 0 ; k < size/4; k++) {
|
||||
int j = (4*k+3);
|
||||
multimap_it_t it;
|
||||
for(multimap_it_from(it, b, k);
|
||||
multimap_it_while_p(it, k);
|
||||
multimap_next(it)) {
|
||||
const multimap_type_t *ref = multimap_cref(it);
|
||||
assert(*ref->key_ptr == k);
|
||||
assert(*ref->value_ptr == j);
|
||||
j--;
|
||||
}
|
||||
assert(j == 4*(k-1)+3);
|
||||
}
|
||||
|
||||
for(int i = 0; i < size; i++) {
|
||||
bool r = multimap_erase(b, i/4);
|
||||
assert (r == true);
|
||||
}
|
||||
assert(multimap_size(b) == 0);
|
||||
}
|
||||
|
||||
multimap_clear(b);
|
||||
}
|
||||
|
||||
static void
|
||||
test_multiset(void)
|
||||
{
|
||||
multiset_t b;
|
||||
multiset_init(b);
|
||||
|
||||
for (int size = 20; size < 1000; size += 10) {
|
||||
for(int i = 0; i < size; i++) {
|
||||
multiset_push(b, i/4);
|
||||
}
|
||||
assert(multiset_size(b) == (unsigned) size);
|
||||
|
||||
for (int k = 0 ; k < size/4; k++) {
|
||||
int j = 0;
|
||||
multiset_it_t it;
|
||||
for(multiset_it_from(it, b, k);
|
||||
multiset_it_while_p(it, k);
|
||||
multiset_next(it)) {
|
||||
const multiset_type_t *ref = multiset_cref(it);
|
||||
assert(*ref == k);
|
||||
j++;
|
||||
}
|
||||
assert(j == 4);
|
||||
}
|
||||
|
||||
for(int i = 0; i < size; i++) {
|
||||
bool r = multiset_erase(b, i/4);
|
||||
assert (r == true);
|
||||
}
|
||||
assert(multiset_size(b) == 0);
|
||||
}
|
||||
|
||||
multiset_clear(b);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test3_string();
|
||||
test4();
|
||||
test5();
|
||||
test_io();
|
||||
test_io_set();
|
||||
test_multimap();
|
||||
test_multiset();
|
||||
exit(0);
|
||||
}
|
||||
+82
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -407,6 +407,86 @@ static void test_queue(size_t n, int cpu_count, unsigned long long ref)
|
||||
queue_uint_clear(g_buff2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_spsc(void)
|
||||
{
|
||||
squeue_uint_t q;
|
||||
bool b;
|
||||
unsigned j;
|
||||
|
||||
squeue_uint_init(q, 256);
|
||||
|
||||
assert(squeue_uint_empty_p(q));
|
||||
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
b = squeue_uint_push(q, i);
|
||||
assert(b);
|
||||
}
|
||||
assert(!squeue_uint_empty_p(q));
|
||||
assert(squeue_uint_full_p(q));
|
||||
assert(squeue_uint_size(q) == 256);
|
||||
assert(squeue_uint_capacity(q) == 256);
|
||||
|
||||
b = squeue_uint_push(q, 256);
|
||||
assert (!b);
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
b = squeue_uint_pop(&j, q);
|
||||
assert(b);
|
||||
assert(j == i);
|
||||
}
|
||||
assert(squeue_uint_empty_p(q));
|
||||
assert(!squeue_uint_full_p(q));
|
||||
assert(squeue_uint_size(q) == 0);
|
||||
assert(squeue_uint_capacity(q) == 256);
|
||||
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
b = squeue_uint_push(q, i);
|
||||
assert(b);
|
||||
}
|
||||
for(unsigned i = 256; i < 256+128; i++) {
|
||||
assert(squeue_uint_full_p(q));
|
||||
squeue_uint_push_force(q, i);
|
||||
}
|
||||
for(unsigned i = 128; i < 256+128; i++) {
|
||||
b = squeue_uint_pop(&j, q);
|
||||
assert(b);
|
||||
assert(j == i);
|
||||
}
|
||||
|
||||
unsigned tab[16];
|
||||
for(unsigned i = 0; i < 16; i++)
|
||||
tab[i] = i * i;
|
||||
for(unsigned i = 0; i < 15; i++) {
|
||||
j = squeue_uint_push_bulk(q, 16, tab);
|
||||
assert(j == 16);
|
||||
}
|
||||
b = squeue_uint_push(q, 1024);
|
||||
assert(b);
|
||||
j = squeue_uint_push_bulk(q, 16, tab);
|
||||
assert(j == 15);
|
||||
|
||||
for(unsigned i = 0; i < 16; i++)
|
||||
tab[i] = 0;
|
||||
j = squeue_uint_pop_bulk(16, tab, q);
|
||||
assert(j == 16);
|
||||
for(unsigned i = 0; i < 16; i++)
|
||||
assert( tab[i] == i * i);
|
||||
|
||||
do {
|
||||
b = squeue_uint_pop(&j, q);
|
||||
} while (b);
|
||||
assert(squeue_uint_empty_p(q));
|
||||
b = squeue_uint_push_move(q, 65536);
|
||||
assert(b);
|
||||
b = squeue_uint_pop(&j, q);
|
||||
assert(b);
|
||||
assert(j == 65536);
|
||||
|
||||
squeue_uint_clear(q);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
int main(void)
|
||||
@@ -438,5 +518,6 @@ int main(void)
|
||||
test_no_thread();
|
||||
test_global_ishared();
|
||||
test_queue(1000000, 2, 2148371710223136ULL);
|
||||
test_spsc();
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* M*LIB - Test for Concurrent memory pool allocator
|
||||
*
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "m-c-mempool.h"
|
||||
#include "m-mutex.h"
|
||||
|
||||
C_MEMPOOL_DEF(lf_mempool, int)
|
||||
|
||||
m_gc_t gc;
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
#define MAX_VALUE 100
|
||||
#define MAX_THREAD 4
|
||||
|
||||
lf_mempool_t g;
|
||||
|
||||
typedef struct {
|
||||
int step;
|
||||
int *ptr[MAX_VALUE];
|
||||
int value[MAX_VALUE];
|
||||
} p_t;
|
||||
|
||||
p_t g_tab[MAX_THREAD];
|
||||
|
||||
static void do_stuff(m_gc_tid_t id)
|
||||
{
|
||||
// Mix of local and more global allocation
|
||||
int *d[MAX_VALUE];
|
||||
for(int i = 0; i < MAX_VALUE; i++) {
|
||||
d[i] = lf_mempool_new(g, id);
|
||||
*d[i] = i*i;
|
||||
}
|
||||
switch (g_tab[id].step)
|
||||
{
|
||||
case 0:
|
||||
for(int i = 0; i < 50; i++) {
|
||||
g_tab[id].value[i] += i;
|
||||
g_tab[id].ptr[i] = lf_mempool_new(g, id);
|
||||
*g_tab[id].ptr[i] = g_tab[id].value[i];
|
||||
}
|
||||
g_tab[id].step = 1;
|
||||
break;
|
||||
case 1:
|
||||
for(int i = 50; i < MAX_VALUE; i++) {
|
||||
g_tab[id].value[i] += i;
|
||||
g_tab[id].ptr[i] = lf_mempool_new(g, id);
|
||||
*g_tab[id].ptr[i] = g_tab[id].value[i];
|
||||
}
|
||||
g_tab[id].step = 2;
|
||||
break;
|
||||
case 2:
|
||||
for(int i = 0; i < 50; i++) {
|
||||
assert(g_tab[id].ptr[i] != NULL);
|
||||
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
|
||||
lf_mempool_del(g, g_tab[id].ptr[i], id);
|
||||
g_tab[id].ptr[i] = NULL;
|
||||
}
|
||||
g_tab[id].step = 3;
|
||||
break;
|
||||
case 3:
|
||||
for(int i = 50; i < MAX_VALUE; i++) {
|
||||
assert(g_tab[id].ptr[i] != NULL);
|
||||
assert(*g_tab[id].ptr[i] == g_tab[id].value[i] );
|
||||
lf_mempool_del(g, g_tab[id].ptr[i], id);
|
||||
g_tab[id].ptr[i] = NULL;
|
||||
}
|
||||
g_tab[id].step = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for(int i = 0; i < MAX_VALUE; i++) {
|
||||
assert (*d[i] == i*i);
|
||||
lf_mempool_del(g, d[i], id);
|
||||
}
|
||||
}
|
||||
|
||||
static void thread1(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
/* Attach this thread to the GC */
|
||||
m_gc_tid_t id = m_gc_attach_thread(gc);
|
||||
for(int n = 0; n < 100000; n ++) {
|
||||
/* Awake the thread: now we can alloc nodes */
|
||||
m_gc_awake(gc, id);
|
||||
/* Do some stuff */
|
||||
do_stuff(id);
|
||||
/* Sleep the thread. A Garbage Collect may occur if possible */
|
||||
m_gc_sleep(gc, id);
|
||||
}
|
||||
/* Detach the thread from the GC */
|
||||
m_gc_detach_thread(gc, id);
|
||||
}
|
||||
|
||||
static void thread2(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
m_gc_tid_t id = m_gc_attach_thread(gc);
|
||||
for(int n = 0; n < 10000; n ++) {
|
||||
m_gc_awake(gc, id);
|
||||
do_stuff(id);
|
||||
m_thread_sleep(1);
|
||||
m_gc_sleep(gc, id);
|
||||
}
|
||||
m_gc_detach_thread(gc, id);
|
||||
}
|
||||
|
||||
static void test(void)
|
||||
{
|
||||
/* First create the Garbage Collector */
|
||||
m_gc_init (gc, MAX_THREAD);
|
||||
/* Create a mempool and attach it to the Garbage Collector */
|
||||
lf_mempool_init(g, gc, 256, MAX_THREAD);
|
||||
|
||||
m_thread_t id[MAX_THREAD];
|
||||
for(int i = 0; i < MAX_THREAD; i+=2) {
|
||||
m_thread_create(id[i], thread1, NULL);
|
||||
m_thread_create(id[i+1], thread2, NULL);
|
||||
}
|
||||
for(int i = 0; i < MAX_THREAD; i++) {
|
||||
m_thread_join(id[i]);
|
||||
}
|
||||
/* Destroy the mempool */
|
||||
lf_mempool_clear(g);
|
||||
/* Destroy the Garbage Collector */
|
||||
m_gc_clear (gc);
|
||||
}
|
||||
|
||||
static void test2(void)
|
||||
{
|
||||
/* First create the Garbage Collector */
|
||||
m_gc_init (gc, MAX_THREAD);
|
||||
/* Create a mempool and attach it to the Garbage Collector */
|
||||
lf_mempool_init(g, gc, 256, MAX_THREAD);
|
||||
/* Create another mempool and attach it to the Garbage Collector */
|
||||
lf_mempool_t g2;
|
||||
lf_mempool_init(g2, gc, 256, MAX_THREAD);
|
||||
/* Register VLA */
|
||||
m_vlapool_t vla;
|
||||
m_vlapool_init(vla, gc);
|
||||
|
||||
/* Attach the thread */
|
||||
m_gc_tid_t id = m_gc_attach_thread(gc);
|
||||
/* Awake the thread: now we can alloc nodes */
|
||||
m_gc_awake(gc, id);
|
||||
|
||||
/* Test some allocation */
|
||||
void *ptr = m_vlapool_new(vla, id, sizeof (int));
|
||||
*(int*)ptr = 42;
|
||||
m_vlapool_del(vla, ptr, id);
|
||||
|
||||
int *pi = lf_mempool_new(g, id);
|
||||
*pi = 44;
|
||||
lf_mempool_del(g, pi, id);
|
||||
|
||||
int *pi2 = lf_mempool_new(g2, id);
|
||||
*pi2 = 46;
|
||||
lf_mempool_del(g2, pi2, id);
|
||||
|
||||
/* Test that the data remain accessible
|
||||
as it has only be logically deleted */
|
||||
assert (*(int*)ptr == 42);
|
||||
assert (*pi == 44);
|
||||
assert (*pi2 == 46);
|
||||
|
||||
/* Sleep the thread */
|
||||
m_gc_sleep(gc, id);
|
||||
|
||||
/* Detach the thread */
|
||||
m_gc_detach_thread(gc, id);
|
||||
|
||||
/* Clear everything */
|
||||
m_vlapool_clear(vla);
|
||||
lf_mempool_clear(g);
|
||||
lf_mempool_clear(g2);
|
||||
m_gc_clear(gc);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test();
|
||||
test2();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,18 +24,27 @@
|
||||
#include "test-obj.h"
|
||||
#include "coverage.h"
|
||||
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-bptree.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-list.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-bptree.h"
|
||||
#include "m-prioqueue.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-variant.h"
|
||||
|
||||
#include "m-concurrent.h"
|
||||
|
||||
TUPLE_DEF2(point, (x, int), (y, int))
|
||||
#define M_OPL_point_t() TUPLE_OPLIST(point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
START_COVERAGE
|
||||
CONCURRENT_DEF(ppoint, point_t)
|
||||
END_COVERAGE
|
||||
|
||||
VARIANT_DEF2(dimension, (x, int), (y, float))
|
||||
#define M_OPL_dimension_t() VARIANT_OPLIST(dimension, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
CONCURRENT_DEF(pdimension, dimension_t)
|
||||
|
||||
ARRAY_DEF(array1, int)
|
||||
CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1))
|
||||
@@ -55,6 +64,9 @@ CONCURRENT_DEF(plist2, list2_t, LIST_OPLIST(list2))
|
||||
DEQUE_DEF(deque1, int)
|
||||
CONCURRENT_DEF(pdeque1, deque1_t, DEQUE_OPLIST(deque1))
|
||||
|
||||
PRIOQUEUE_DEF(prio1, int)
|
||||
CONCURRENT_DEF(pprio1, prio1_t, PRIOQUEUE_OPLIST(prio1))
|
||||
|
||||
BPTREE_DEF2(bptree1, 10, int, int)
|
||||
CONCURRENT_DEF(pbtree1, bptree1_t, BPTREE_OPLIST2(bptree1, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST))
|
||||
|
||||
@@ -68,7 +80,6 @@ DICT_DEF2(string_pool, string_t, string_t)
|
||||
#define STRING_POOL_OPLIST \
|
||||
DICT_OPLIST(string_pool, STRING_OPLIST, STRING_OPLIST)
|
||||
#define M_OPL_string_pool_t() STRING_POOL_OPLIST
|
||||
|
||||
CONCURRENT_DEF(string_pool_ts, string_pool_t, STRING_POOL_OPLIST)
|
||||
#define STRING_POOL_TS_OPLIST \
|
||||
CONCURRENT_OPLIST(string_pool_ts, STRING_POOL_OPLIST)
|
||||
@@ -78,6 +89,28 @@ TUPLE_DEF2(my_tuple, (pool, string_pool_ts_t))
|
||||
#define MY_TUPLE_OPLIST TUPLE_OPLIST(my_tuple, STRING_POOL_TS_OPLIST)
|
||||
#define M_OPL_my_tuple_t() MY_TUPLE_OPLIST
|
||||
|
||||
ARRAY_DEF(key_list, string_t)
|
||||
#define M_OPL_key_list_t() ARRAY_OPLIST(key_list, STRING_OPLIST)
|
||||
CONCURRENT_DEF(key_list_ts, key_list_t)
|
||||
|
||||
CONCURRENT_RP_DEF(rparray1, array1_t, ARRAY_OPLIST(array1))
|
||||
|
||||
CONCURRENT_RP_DEF(rpdict1, dict1_t, DICT_OPLIST(dict1))
|
||||
|
||||
/* OA dict needs more operators than the default ones to work */
|
||||
static inline bool int_oor_equal_p(int s, unsigned char n)
|
||||
{
|
||||
return s == -n;
|
||||
}
|
||||
static inline void int_oor_set(int *s, unsigned char n)
|
||||
{
|
||||
*s = -n;
|
||||
}
|
||||
#define INT_OA_OPLIST \
|
||||
M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(int_oor_equal_p), OOR_SET(API_2(int_oor_set)))
|
||||
DICT_OA_DEF2(dict3, int, INT_OA_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
CONCURRENT_DEF(pdict3, dict3_t, DICT_OPLIST(dict3))
|
||||
|
||||
/********************************/
|
||||
parray1_t arr;
|
||||
|
||||
@@ -92,7 +125,7 @@ static void conso(void *p)
|
||||
}
|
||||
}
|
||||
|
||||
static void test(void)
|
||||
static void test_thread(void)
|
||||
{
|
||||
m_thread_t idx;
|
||||
parray1_init(arr);
|
||||
@@ -118,6 +151,7 @@ static void test_basic(void)
|
||||
bool b = pdict1_get_blocking(&z, dict, 2, false);
|
||||
assert (b);
|
||||
assert (z == 3);
|
||||
assert (pdict1_size(dict) == 1);
|
||||
pdict1_clear(dict);
|
||||
|
||||
M_LET(str, string_t)
|
||||
@@ -132,8 +166,55 @@ static void test_basic(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rparray1_t rarr;
|
||||
|
||||
static void conso_rp(void *p)
|
||||
{
|
||||
assert (p == NULL);
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
int j;
|
||||
bool b = rparray1_pop_blocking(&j, rarr, true);
|
||||
assert (b);
|
||||
assert (j == i);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_rp_thread(void)
|
||||
{
|
||||
m_thread_t idx;
|
||||
rparray1_init(rarr);
|
||||
m_thread_create (idx, conso_rp, NULL);
|
||||
m_thread_sleep(1000);
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
rparray1_push (rarr, i);
|
||||
while (!rparray1_empty_p(rarr));
|
||||
}
|
||||
m_thread_join(idx);
|
||||
rparray1_clear(rarr);
|
||||
}
|
||||
|
||||
static void test_rp_basic(void)
|
||||
{
|
||||
rpdict1_t dict;
|
||||
rpdict1_init(dict);
|
||||
int z;
|
||||
rpdict1_set_at(dict, 2, 3);
|
||||
rpdict1_get_copy(&z, dict, 2);
|
||||
assert (z == 3);
|
||||
z = 1;
|
||||
bool b = rpdict1_get_blocking(&z, dict, 2, false);
|
||||
assert (b);
|
||||
assert (z == 3);
|
||||
assert (rpdict1_size(dict) == 1);
|
||||
rpdict1_clear(dict);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_basic();
|
||||
test();
|
||||
test_rp_basic();
|
||||
test_thread();
|
||||
test_rp_thread();
|
||||
exit(0);
|
||||
}
|
||||
+82
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -273,15 +273,15 @@ static void test_oplist(void)
|
||||
assert (!M_OPLIST_P(func(INIT(init)) a));
|
||||
assert (!M_OPLIST_P(func(INIT(init),) a));
|
||||
|
||||
#define a (INIT(0), CLEAR(clear))
|
||||
assert (M_TEST_DISABLED_METHOD_P(INIT, a));
|
||||
assert (!M_TEST_DISABLED_METHOD_P(INIT2, a));
|
||||
assert (!M_TEST_DISABLED_METHOD_P(CLEAR, a));
|
||||
#define aop (INIT(0), CLEAR(clear))
|
||||
assert (M_TEST_DISABLED_METHOD_P(INIT, aop));
|
||||
assert (!M_TEST_DISABLED_METHOD_P(INIT2, aop));
|
||||
assert (!M_TEST_DISABLED_METHOD_P(CLEAR, aop));
|
||||
assert (!M_TEST_DISABLED_METHOD_P(INIT, ()));
|
||||
|
||||
#define M_OPL_op() (INIT(1), CLEAR(0))
|
||||
assert(M_GET_INIT M_GLOBAL_OPLIST(op));
|
||||
assert(!M_GET_INIT M_GLOBAL_OPLIST(a));
|
||||
assert(!M_GET_INIT M_GLOBAL_OPLIST(aop));
|
||||
|
||||
#define MAKE_OPLIST(op) (OPLIST(M_GLOBAL_OPLIST_OR_DEF(op)()))
|
||||
#define M_OPL_uint() (INIT(0), CLEAR(1))
|
||||
@@ -371,6 +371,81 @@ static void test_parse_standard_c_type(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef int ti[1];
|
||||
typedef struct { int b,c; } ts;
|
||||
|
||||
static void f_ti(ti x)
|
||||
{
|
||||
ti y;
|
||||
M_MOVE_A1_DEFAULT(y,x);
|
||||
assert(y[0] == 9);
|
||||
assert(x[0] == 0);
|
||||
}
|
||||
|
||||
static void f_ts(ts x)
|
||||
{
|
||||
ts y;
|
||||
M_MOVE_DEFAULT(y,x);
|
||||
assert(y.b == 2);
|
||||
assert(y.c == 3);
|
||||
assert(x.b == 0);
|
||||
assert(x.c == 0);
|
||||
}
|
||||
|
||||
static void f_ts2(ts *x)
|
||||
{
|
||||
ts y;
|
||||
M_MOVE_DEFAULT(y,*x);
|
||||
assert(y.b == 2);
|
||||
assert(y.c == 3);
|
||||
assert(x->b == 0);
|
||||
assert(x->c == 0);
|
||||
}
|
||||
|
||||
static void test_move_default(void)
|
||||
{
|
||||
int o, p;
|
||||
o = 9;
|
||||
M_MOVE_DEFAULT(p, o);
|
||||
assert(p == 9);
|
||||
assert(o == 0);
|
||||
|
||||
double u, k;
|
||||
u = 9.5;
|
||||
M_MOVE_DEFAULT(k, u);
|
||||
assert(k == 9.5);
|
||||
assert(u == 0.0); // NOTE: memset(0) sets an IEEE-754 number to 0.0
|
||||
|
||||
int *ptr = &o, *m;
|
||||
M_MOVE_DEFAULT(m, ptr);
|
||||
assert( ptr == NULL);
|
||||
assert( m == &o);
|
||||
|
||||
ti x, y;
|
||||
x[0] = 9;
|
||||
M_MOVE_A1_DEFAULT(y, x);
|
||||
assert(y[0] == 9);
|
||||
assert(x[0] == 0);
|
||||
|
||||
x[0] = 9;
|
||||
f_ti(x);
|
||||
assert(x[0] == 0);
|
||||
|
||||
ts y2, x2 = { .b = 2, .c = 3};
|
||||
M_MOVE_DEFAULT(y2, x2);
|
||||
assert(y2.b == 2 && y2.c == 3);
|
||||
assert(x2.b == 0 && x2.c == 0);
|
||||
|
||||
x2.b = 2; x2.c = 3;
|
||||
f_ts(x2);
|
||||
assert(x2.b == 2);
|
||||
assert(x2.c == 3);
|
||||
|
||||
f_ts2(&x2);
|
||||
assert(x2.b == 0);
|
||||
assert(x2.c == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_cat();
|
||||
@@ -389,5 +464,6 @@ int main(void)
|
||||
test_cast();
|
||||
test_reduce();
|
||||
test_parse_standard_c_type();
|
||||
test_move_default();
|
||||
exit(0);
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
|
||||
+50
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,6 +41,12 @@ DICT_SET_DEF(dict_setstr, string_t, STRING_OPLIST)
|
||||
DICT_DEF2(dict_int, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
DICT_DEF2(dict_mpz, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
|
||||
|
||||
BOUNDED_STRING_DEF(symbol, 15)
|
||||
DICT_OA_DEF2(dict_oa_str, symbol_t, BOUNDED_STRING_OPLIST(symbol), int, M_DEFAULT_OPLIST)
|
||||
|
||||
DICT_OA_DEF2(dict_oa_bstr, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
DICT_OASET_DEF(dict_oa_setstr, string_t, STRING_OPLIST)
|
||||
|
||||
/* Helper structure */
|
||||
ARRAY_DEF(array_string, string_t, STRING_OPLIST)
|
||||
array_string_t v_str;
|
||||
@@ -463,6 +469,47 @@ static void test_it_oa(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_oa_str1(void)
|
||||
{
|
||||
symbol_t s;
|
||||
int *p;
|
||||
dict_oa_str_t dict;
|
||||
|
||||
symbol_init(s);
|
||||
dict_oa_str_init(dict);
|
||||
|
||||
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "x"), 1);
|
||||
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "y"), 2);
|
||||
dict_oa_str_set_at(dict, BOUNDED_STRING_CTE(symbol, "z"), 3);
|
||||
|
||||
p = dict_oa_str_get_at(dict, BOUNDED_STRING_CTE(symbol, "x"));
|
||||
assert(p != NULL);
|
||||
assert(*p == 1);
|
||||
|
||||
dict_oa_str_clear(dict);
|
||||
symbol_clear(s);
|
||||
}
|
||||
|
||||
static void
|
||||
test_oa_str2(void)
|
||||
{
|
||||
int *p;
|
||||
dict_oa_bstr_t dict;
|
||||
|
||||
dict_oa_bstr_init(dict);
|
||||
|
||||
dict_oa_bstr_set_at(dict, STRING_CTE("x"), 1);
|
||||
dict_oa_bstr_set_at(dict, STRING_CTE("y"), 2);
|
||||
dict_oa_bstr_set_at(dict, STRING_CTE("z"), 3);
|
||||
|
||||
p = dict_oa_bstr_get_at(dict, STRING_CTE("x"));
|
||||
assert(p != NULL);
|
||||
assert(*p == 1);
|
||||
|
||||
dict_oa_bstr_clear(dict);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
@@ -472,5 +519,7 @@ int main(void)
|
||||
test_oa();
|
||||
test_init_oa();
|
||||
test_it_oa();
|
||||
test_oa_str1();
|
||||
test_oa_str2();
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MLIB - TEST module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "m-serial-bin.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-variant.h"
|
||||
#include "m-list.h"
|
||||
#include "m-dict.h"
|
||||
|
||||
// Serial bin is not supported for standard types if not C11
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
|
||||
ARRAY_DEF(a2, int)
|
||||
#define M_OPL_a2_t() ARRAY_OPLIST(a2, M_DEFAULT_OPLIST)
|
||||
|
||||
ARRAY_DEF(l2, int)
|
||||
#define M_OPL_l2_t() ARRAY_OPLIST(l2, M_DEFAULT_OPLIST)
|
||||
|
||||
DICT_DEF2(d2, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST )
|
||||
#define M_OPL_d2_t() DICT_OPLIST(d2, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
VARIANT_DEF2(v2,
|
||||
(is_int, int),
|
||||
(is_bool, bool) )
|
||||
#define M_OPL_v2_t() VARIANT_OPLIST(v2, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
TUPLE_DEF2(my,
|
||||
(vala, int),
|
||||
(valb, float),
|
||||
(valc, bool),
|
||||
(vald, string_t),
|
||||
(vale, a2_t),
|
||||
(valf, v2_t),
|
||||
(valg, l2_t),
|
||||
(valh, d2_t)
|
||||
)
|
||||
#define M_OPL_my_t() TUPLE_OPLIST(my, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, STRING_OPLIST, M_OPL_a2_t(), M_OPL_v2_t(), M_OPL_l2_t(), M_OPL_d2_t() )
|
||||
|
||||
TUPLE_DEF2(my2,
|
||||
(activated, bool),
|
||||
(data, my_t, M_OPL_my_t() ) )
|
||||
|
||||
static void test_out_empty(void)
|
||||
{
|
||||
m_serial_read_t in;
|
||||
m_serial_write_t out;
|
||||
m_serial_return_code_t ret;
|
||||
my2_t el1, el2;
|
||||
my2_init(el1);
|
||||
my2_init(el2);
|
||||
|
||||
FILE *f = fopen ("a-mbin.dat", "wt");
|
||||
if (!f) abort();
|
||||
m_serial_bin_write_init(out, f);
|
||||
ret = my2_out_serial(out, el1);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_bin_write_clear(out);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mbin.dat", "rt");
|
||||
if (!f) abort();
|
||||
m_serial_bin_read_init(in, f);
|
||||
ret = my2_in_serial(el2, in);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_bin_read_clear(in);
|
||||
fclose(f);
|
||||
|
||||
assert (my2_equal_p (el1, el2));
|
||||
|
||||
my2_clear(el1);
|
||||
my2_clear(el2);
|
||||
}
|
||||
my2_t el1, el2;
|
||||
|
||||
static void test_out_fill(void)
|
||||
{
|
||||
m_serial_read_t in;
|
||||
m_serial_write_t out;
|
||||
m_serial_return_code_t ret;
|
||||
my2_init(el1);
|
||||
my2_init(el2);
|
||||
|
||||
FILE *f;
|
||||
|
||||
el2->activated = true;
|
||||
el2->data->vala = 145788;
|
||||
el2->data->valb = -0.1;
|
||||
el2->data->valc = false;
|
||||
string_set_str(el2->data->vald, "This is a string test.");
|
||||
a2_push_back(el2->data->vale, 1);
|
||||
a2_push_back(el2->data->vale, 4);
|
||||
a2_push_back(el2->data->vale, -5);
|
||||
a2_push_back(el2->data->vale, 1458);
|
||||
v2_set_is_int(el2->data->valf, 12356789);
|
||||
l2_push_back(el2->data->valg, 1345);
|
||||
l2_push_back(el2->data->valg, 46543);
|
||||
l2_push_back(el2->data->valg, -5678);
|
||||
d2_set_at(el2->data->valh, STRING_CTE("Paul"), 1);
|
||||
d2_set_at(el2->data->valh, STRING_CTE("Smith"), 2);
|
||||
|
||||
f = fopen ("a-mbin.dat", "wt");
|
||||
if (!f) abort();
|
||||
m_serial_bin_write_init(out, f);
|
||||
ret = my2_out_serial(out, el2);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_bin_write_clear(out);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mbin.dat", "rt");
|
||||
if (!f) abort();
|
||||
m_serial_bin_read_init(in, f);
|
||||
ret = my2_in_serial(el1, in);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_bin_read_clear(in);
|
||||
fclose(f);
|
||||
|
||||
assert (my2_equal_p (el1, el2));
|
||||
|
||||
my2_clear(el1);
|
||||
my2_clear(el2);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
//test_out_empty();
|
||||
test_out_fill();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "m-serial-json.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-variant.h"
|
||||
#include "m-list.h"
|
||||
#include "m-dict.h"
|
||||
|
||||
// Serial json is not supported for standard types if not C11
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
|
||||
ARRAY_DEF(a2, int)
|
||||
#define M_OPL_a2_t() ARRAY_OPLIST(a2, M_DEFAULT_OPLIST)
|
||||
|
||||
ARRAY_DEF(l2, int)
|
||||
#define M_OPL_l2_t() ARRAY_OPLIST(l2, M_DEFAULT_OPLIST)
|
||||
|
||||
DICT_DEF2(d2, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST )
|
||||
#define M_OPL_d2_t() DICT_OPLIST(d2, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
VARIANT_DEF2(v2,
|
||||
(is_int, int),
|
||||
(is_bool, bool) )
|
||||
#define M_OPL_v2_t() VARIANT_OPLIST(v2, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
TUPLE_DEF2(my,
|
||||
(vala, int),
|
||||
(valb, float),
|
||||
(valc, bool),
|
||||
(vald, string_t),
|
||||
(vale, a2_t),
|
||||
(valf, v2_t),
|
||||
(valg, l2_t),
|
||||
(valh, d2_t)
|
||||
)
|
||||
#define M_OPL_my_t() TUPLE_OPLIST(my, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, STRING_OPLIST, M_OPL_a2_t(), M_OPL_v2_t(), M_OPL_l2_t(), M_OPL_d2_t() )
|
||||
|
||||
TUPLE_DEF2(my2,
|
||||
(activated, bool),
|
||||
(data, my_t, M_OPL_my_t() ) )
|
||||
|
||||
static void test_out_empty(void)
|
||||
{
|
||||
m_serial_read_t in;
|
||||
m_serial_write_t out;
|
||||
m_serial_return_code_t ret;
|
||||
my2_t el1, el2;
|
||||
my2_init(el1);
|
||||
my2_init(el2);
|
||||
|
||||
FILE *f = fopen ("a-mjson.dat", "wt");
|
||||
if (!f) abort();
|
||||
m_serial_json_write_init(out, f);
|
||||
ret = my2_out_serial(out, el1);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_json_write_clear(out);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mjson.dat", "rt");
|
||||
if (!f) abort();
|
||||
static const char expected[] = "{ \"activated\":false,\"data\":{ \"vala\":0,\"valb\":0.000000,\"valc\":false,\"vald\":\"\",\"vale\":[],\"valf\":{},\"valg\":[],\"valh\":{}}";
|
||||
char get[sizeof expected];
|
||||
fgets (get, sizeof expected , f);
|
||||
assert (strcmp(get, expected) == 0);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mjson.dat", "rt");
|
||||
if (!f) abort();
|
||||
m_serial_json_read_init(in, f);
|
||||
ret = my2_in_serial(el2, in);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_json_read_clear(in);
|
||||
fclose(f);
|
||||
|
||||
assert (my2_equal_p (el1, el2));
|
||||
|
||||
my2_clear(el1);
|
||||
my2_clear(el2);
|
||||
}
|
||||
|
||||
static void test_out_fill(void)
|
||||
{
|
||||
m_serial_read_t in;
|
||||
m_serial_write_t out;
|
||||
m_serial_return_code_t ret;
|
||||
my2_t el1, el2;
|
||||
my2_init(el1);
|
||||
my2_init(el2);
|
||||
|
||||
FILE *f = fopen("a-mjson.dat", "wt");
|
||||
if (!f) abort();
|
||||
fprintf(f,
|
||||
"{\n"
|
||||
" \"activated\":false,\n"
|
||||
"\"data\": {\n"
|
||||
" \"valb\": -2.300000 , \n"
|
||||
"\"vale\": [1,2,3],\n"
|
||||
"\"valg\": [1,2,3,4,5,6],\n"
|
||||
"\"valh\": { \"jane\": 3, \"steeve\": -4 },\n"
|
||||
"\"valf\": { \"is_bool\": true },\n"
|
||||
" \"vala\":1742,\n"
|
||||
" \"vald\": \"This is a test\",\n"
|
||||
" \"valc\": true } }\n");
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mjson.dat", "rt");
|
||||
if (!f) abort();
|
||||
m_serial_json_read_init(in, f);
|
||||
ret = my2_in_serial(el2, in);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_json_read_clear(in);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mjson.dat", "wt");
|
||||
if (!f) abort();
|
||||
m_serial_json_write_init(out, f);
|
||||
ret = my2_out_serial(out, el2);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_json_write_clear(out);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mjson.dat", "rt");
|
||||
if (!f) abort();
|
||||
static const char expected[] = "{ \"activated\":false,\"data\":{ \"vala\":1742,\"valb\":-2.300000,\"valc\":true,\"vald\":\"This is a test\",\"vale\":[1,2,3],\"valf\":{\"is_bool\":true},\"valg\":[1,2,3,4,5,6],\"valh\":{\"steeve\":-4,\"jane\":3}}}";
|
||||
static const char expected2[] = "{ \"activated\":false,\"data\":{ \"vala\":1742,\"valb\":-2.300000,\"valc\":true,\"vald\":\"This is a test\",\"vale\":[1,2,3],\"valf\":{\"is_bool\":true},\"valg\":[1,2,3,4,5,6],\"valh\":{\"jane\":3,\"steeve\":-4}}}";
|
||||
char get[sizeof expected];
|
||||
fgets (get, sizeof expected , f);
|
||||
assert (strcmp(get, expected) == 0 || strcmp(get, expected2) == 0);
|
||||
fclose(f);
|
||||
|
||||
f = fopen ("a-mjson.dat", "rt");
|
||||
if (!f) abort();
|
||||
m_serial_json_read_init(in, f);
|
||||
ret = my2_in_serial(el1, in);
|
||||
assert (ret == M_SERIAL_OK_DONE);
|
||||
m_serial_json_read_clear(in);
|
||||
fclose(f);
|
||||
|
||||
assert (my2_equal_p (el1, el2));
|
||||
|
||||
my2_clear(el1);
|
||||
my2_clear(el2);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_out_empty();
|
||||
test_out_fill();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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,16 +34,18 @@ typedef struct {
|
||||
unsigned int c;
|
||||
unsigned long long spare2;
|
||||
unsigned int p;
|
||||
void *v;
|
||||
} data_t;
|
||||
|
||||
static void data_crc(data_t *p) { p->p = -p->n; p->c = ~p->n; }
|
||||
static void data_init(data_t *p) { p->n = 0; p->spare1 = 0; p->spare2 = 0; data_crc(p); }
|
||||
static void data_init(data_t *p) { p->n = 0; p->spare1 = 0; p->spare2 = 0; data_crc(p); p->v = calloc(1, 1); assert(p->v); }
|
||||
static void data_valid_p(const data_t *p) { assert (p->n == -p->p && p->n == ~p->c); }
|
||||
static void data_clear(data_t *p) { (void) p;}
|
||||
static void data_clear(data_t *p) { free(p->v); }
|
||||
static void data_set(data_t *p, data_t o) { p->n = o.n; data_crc(p); }
|
||||
static void data_init_set(data_t *p, data_t o) { data_init(p); data_set(p, o); }
|
||||
|
||||
#define DATA_OPLIST \
|
||||
(INIT(data_init M_IPTR), INIT_SET(data_set M_IPTR), SET(data_set M_IPTR), CLEAR(data_clear M_IPTR))
|
||||
(INIT(data_init M_IPTR), INIT_SET(data_init_set M_IPTR), SET(data_set M_IPTR), CLEAR(data_clear M_IPTR))
|
||||
|
||||
START_COVERAGE
|
||||
SNAPSHOT_SPSC_DEF(snapshot_uint, unsigned int)
|
||||
|
||||
+59
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -91,12 +91,17 @@ static void test_utf8_basic(void)
|
||||
static void test_utf8_it(void)
|
||||
{
|
||||
string_t s;
|
||||
string_it_t it;
|
||||
string_init(s);
|
||||
|
||||
for(string_it(it, s) ; !string_end_p(it); string_next(it)) {
|
||||
assert(0); // Shall not be called
|
||||
}
|
||||
|
||||
const unsigned int tab[] = { 45, 1458, 25623, 129, 24 };
|
||||
for(int i = 0; i < 5; i++)
|
||||
string_push_u (s, tab[i]);
|
||||
assert (string_length_u(s) == 5);
|
||||
string_it_t it;
|
||||
int i = 0;
|
||||
for(string_it(it, s) ; !string_end_p(it); string_next(it), i++) {
|
||||
assert (i < 5);
|
||||
@@ -269,8 +274,13 @@ static void test0(void)
|
||||
string_set_str(s1, "Hello, world!");
|
||||
assert(string_start_with_str_p(s1, "Hello") == true);
|
||||
assert(string_start_with_str_p(s1, "Help") == false);
|
||||
assert(string_end_with_str_p(s1, "world!") == true);
|
||||
assert(string_end_with_str_p(s1, "worldX") == false);
|
||||
assert(string_end_with_str_p(s1, "Hello, world!!") == false);
|
||||
string_set_str(s2, "H");
|
||||
assert(string_start_with_string_p(s1, s2) == true);
|
||||
string_set_str(s2, "!");
|
||||
assert(string_end_with_string_p(s1, s2) == true);
|
||||
|
||||
string_cat_printf(s1, " %d little %s.", 10, "suns");
|
||||
string_set_str(s2, "Hello, world! 10 little suns.");
|
||||
@@ -345,6 +355,13 @@ static void test0(void)
|
||||
char *s = string_clear_get_str (s1);
|
||||
assert(strcmp(s, "QWERTYQWERTY") == 0);
|
||||
free(s);
|
||||
|
||||
string_t s3;
|
||||
string_init(s3);
|
||||
string_cat_str(s3, "ABC");
|
||||
s = string_clear_get_str(s3);
|
||||
assert(strcmp(s, "ABC") == 0);
|
||||
free(s);
|
||||
|
||||
string_init_set_str(s1, "RESTART");
|
||||
assert (string_equal_str_p(s1, "RESTART"));
|
||||
@@ -384,6 +401,9 @@ static void test0(void)
|
||||
string_init (s1);
|
||||
string_init (s2);
|
||||
|
||||
size_t h1 = string_hash(s1);
|
||||
assert (h1 != 0);
|
||||
|
||||
string_printf (s1, "Hello %d worlds. How do you do? I'm fine. Thank you! The weather is bad today. I should had brought my umbrella. Oh! You can lend me one! Thank you very much! No really thank you. I wouldn't be able to get in time for my job.", 2);
|
||||
assert (string_equal_str_p(s1, "Hello 2 worlds. How do you do? I'm fine. Thank you! The weather is bad today. I should had brought my umbrella. Oh! You can lend me one! Thank you very much! No really thank you. I wouldn't be able to get in time for my job."));
|
||||
|
||||
@@ -447,7 +467,7 @@ static void test0(void)
|
||||
string_set_str (s1, "");
|
||||
assert (string_capacity(s1) > 0);
|
||||
string_reserve (s1, 0);
|
||||
assert (string_capacity(s1) == 0);
|
||||
assert (string_capacity(s1) == sizeof (string_heap_t) - 1);
|
||||
|
||||
string_set_str(s1, "HELLO XXX!");
|
||||
string_replace_at(s1, 6, 3, "World");
|
||||
@@ -518,6 +538,41 @@ static void test_bounded1(void)
|
||||
string16_clear(s);
|
||||
}
|
||||
|
||||
static void test_bounded_io(void)
|
||||
{
|
||||
string16_t s, d;
|
||||
bool b;
|
||||
FILE *f;
|
||||
|
||||
string16_init(s);
|
||||
string16_init(d);
|
||||
|
||||
string16_set_str(s, "Hello \"world\"");
|
||||
f = fopen ("a-mstring.dat", "wt");
|
||||
assert (f != NULL);
|
||||
string16_out_str (f, s);
|
||||
fclose (f);
|
||||
f = fopen("a-mstring.dat", "rt");
|
||||
assert (f != NULL);
|
||||
b = string16_in_str(d, f);
|
||||
assert(b);
|
||||
fclose(f);
|
||||
assert (string16_equal_p(s, d));
|
||||
|
||||
const char *end;
|
||||
string_t str;
|
||||
string_init(str);
|
||||
string16_get_str(str, s, false);
|
||||
assert(string_equal_str_p(str, "\"Hello \\\"world\\\"\""));
|
||||
b = string16_parse_str(d, string_get_cstr(str), &end);
|
||||
assert (b);
|
||||
assert (string16_equal_p(s, d));
|
||||
|
||||
string_clear(str);
|
||||
string16_clear(s);
|
||||
string16_clear(d);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test0();
|
||||
@@ -525,5 +580,6 @@ int main(void)
|
||||
test_utf8_basic();
|
||||
test_utf8_it();
|
||||
test_bounded1();
|
||||
test_bounded_io();
|
||||
exit(0);
|
||||
}
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -60,6 +60,10 @@ TUPLE_DEF2(pair2_str,
|
||||
(str, string_t),
|
||||
(value, unsigned long))
|
||||
|
||||
TUPLE_DEF2(int_float,
|
||||
(vala, int),
|
||||
(valb, float))
|
||||
|
||||
// Recursive tuple definition.
|
||||
TUPLE_DEF2(rtuple,
|
||||
(ituple, pair_str_t),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2019, 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:
|
||||
@@ -76,7 +76,7 @@ static void test1bis(void)
|
||||
assert (atomic_load(&resetFunc_called) == true || workeri_get_cpu_count() == 1);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && (!defined(__clang__) || WORKER_USE_CLANG_BLOCK || WORKER_USE_CPP_FUNCTION)
|
||||
#if defined(__GNUC__) && (!defined(__clang__) || (defined(WORKER_USE_CLANG_BLOCK) && WORKER_USE_CLANG_BLOCK) || (defined(WORKER_USE_CPP_FUNCTION) && WORKER_USE_CPP_FUNCTION))
|
||||
|
||||
/* The macro version will generate warnings about shadow variables.
|
||||
There is no way to avoid this. */
|
||||
|
||||
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user