Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46759b5f2c | ||
|
|
8c8a1e2822 | ||
|
|
ad646f5e04 | ||
|
|
9ecc031fd7 | ||
|
|
ea3c2c5019 | ||
|
|
a9dc2eef12 | ||
|
|
39a0a4f672 | ||
|
|
4ffa4a15ac | ||
|
|
2d5c9e71ae | ||
|
|
e802e22b7b | ||
|
|
3fbe8ec0b6 | ||
|
|
34085aa637 | ||
|
|
77033e2020 | ||
|
|
5c57ad1f51 | ||
|
|
27621db95d | ||
|
|
eab3f701a8 | ||
|
|
cbc5f17cf0 | ||
|
|
02a39fce3f | ||
|
|
7c4f94a674 | ||
|
|
8c70a1f96c | ||
|
|
25dcdeed69 | ||
|
|
7e8cb07a6c | ||
|
|
2296f203fd | ||
|
|
790ae33334 | ||
|
|
d795476157 | ||
|
|
6a2bdc49fb | ||
|
|
725f42852e | ||
|
|
bfcc4134d6 | ||
|
|
de6ccc293e | ||
|
|
d9953daf59 | ||
|
|
1a62f5a4ed | ||
|
|
3721129da1 | ||
|
|
f585167fa8 | ||
|
|
6d5d34fb72 | ||
|
|
196db1cf62 | ||
|
|
174c62015b | ||
|
|
fb3140e645 | ||
|
|
431e3a1228 | ||
|
|
3ddf9438da | ||
|
|
6e8aa58bd6 | ||
|
|
df37a5d8f1 | ||
|
|
2ad8d42401 | ||
|
|
8f47fb35ae | ||
|
|
a1a5ae749d | ||
|
|
147bc0a363 | ||
|
|
33aa102b7c | ||
|
|
bd9485a3fe | ||
|
|
0de3c622e3 | ||
|
|
93a1be616d | ||
|
|
692c0487af | ||
|
|
22ccae74c3 | ||
|
|
586d0ae13a | ||
|
|
c7bad98a7e | ||
|
|
8e3a06b852 | ||
|
|
d548117538 | ||
|
|
3a39ce5a4a | ||
|
|
fa41f3affd | ||
|
|
0d0747dea9 | ||
|
|
ab75397e21 | ||
|
|
cc97aa887f | ||
|
|
b275859a37 | ||
|
|
256ff916a2 | ||
|
|
77195fb1d0 | ||
|
|
c2cfb2f0a0 | ||
|
|
03cceafd73 | ||
|
|
505e2c4a62 | ||
|
|
daadf693f7 | ||
|
|
98b3bbd7a1 | ||
|
|
decec8f7ae | ||
|
|
ce3f674ba8 | ||
|
|
01f5228a36 | ||
|
|
a7294abe0c | ||
|
|
77e5666802 | ||
|
|
689527c2dc | ||
|
|
5d1ebdf5bb | ||
|
|
10d918bbc9 | ||
|
|
8069fae3d5 | ||
|
|
94dd0a5571 | ||
|
|
32a3d95104 | ||
|
|
d08aeb32fe | ||
|
|
26fc2bcffe | ||
|
|
7617048073 | ||
|
|
57e7510ae7 | ||
|
|
324ba5d256 | ||
|
|
8939b4d046 | ||
|
|
1cf7c4fa4d | ||
|
|
839e7c2957 | ||
|
|
06e3046268 | ||
|
|
e959c001aa | ||
|
|
bcc9f2c894 | ||
|
|
6e828eabaf | ||
|
|
7677b13d8a | ||
|
|
e16c6987e4 | ||
|
|
aa4cbfebdb | ||
|
|
66f376ea80 | ||
|
|
6de8eab5c3 | ||
|
|
9e63289a38 | ||
|
|
bf617ab6ab | ||
|
|
3f8c30e592 | ||
|
|
37b2f4d2ac | ||
|
|
50e8d1f469 | ||
|
|
d40a6e5bc3 | ||
|
|
b4c29d5423 | ||
|
|
9f4f9d1d5d | ||
|
|
1e196fe22a | ||
|
|
450d2c9000 | ||
|
|
93112a5a7c | ||
|
|
1f2584ccea | ||
|
|
5b4c9cd618 | ||
|
|
f4e3d9cd4c | ||
|
|
4988661b7d | ||
|
|
7033a922c7 | ||
|
|
dd607252eb | ||
|
|
73da9a4316 | ||
|
|
b3a0d70147 | ||
|
|
c55bb6b9ad | ||
|
|
68fdc2dde1 | ||
|
|
8877af10b0 | ||
|
|
782963dec5 | ||
|
|
36d11bc2bd | ||
|
|
6d621703eb | ||
|
|
76b40d247d | ||
|
|
033deac9f5 | ||
|
|
b60553165d | ||
|
|
e1a4d359a2 | ||
|
|
4ab2615a1c | ||
|
|
9136a4a972 | ||
|
|
55dcf0e438 | ||
|
|
fb604f02b7 | ||
|
|
1db417e30e | ||
|
|
5b77fe7207 | ||
|
|
dd509f2b7e | ||
|
|
50d4e15297 | ||
|
|
f6d1b16338 | ||
|
|
64a035559d | ||
|
|
c4bc37560c | ||
|
|
e7309b7d3a | ||
|
|
c461b4eca4 | ||
|
|
f21d427de1 | ||
|
|
0f9edaa7ee | ||
|
|
64888127c4 | ||
|
|
d5b4ec249b | ||
|
|
0bcd78a8f5 | ||
|
|
27b0850af4 | ||
|
|
ae55ccfc40 | ||
|
|
5bf644ccf0 | ||
|
|
3f09adc507 | ||
|
|
4e851142c2 | ||
|
|
fec6b42260 | ||
|
|
9dbc48aa5d | ||
|
|
b92bae34eb | ||
|
|
8faf7f79c4 | ||
|
|
c3e29a16df | ||
|
|
1bc33a10fc | ||
|
|
b7074d1c69 | ||
|
|
26d541fe06 | ||
|
|
2991c7cc5a | ||
|
|
4e7cbae766 | ||
|
|
d5e0620e4c | ||
|
|
32cfbd23bc | ||
|
|
0e09681a6a | ||
|
|
bc8098b337 | ||
|
|
c5692e7e9f | ||
|
|
f43db93791 | ||
|
|
14c954c6ce | ||
|
|
60280087c7 | ||
|
|
1e57d9c6d6 | ||
|
|
eeaf74d24f | ||
|
|
a318d36c35 | ||
|
|
edcf8ee37a | ||
|
|
8a1f30eeff | ||
|
|
2995b119b7 | ||
|
|
08bd5a888b | ||
|
|
2f1b17f805 | ||
|
|
0379f5f1c6 | ||
|
|
45b11fb49b | ||
|
|
82ea4341de | ||
|
|
341d145b4d | ||
|
|
847f52fa5e | ||
|
|
02828a0109 | ||
|
|
0f7d5bf366 | ||
|
|
1277c75ea3 | ||
|
|
47a51ee5c4 | ||
|
|
3f102c2270 | ||
|
|
4ba55dc3ca | ||
|
|
2c0db40158 | ||
|
|
8987288a10 | ||
|
|
cf8c2afddd | ||
|
|
0a8e336037 | ||
|
|
e7e29d913e | ||
|
|
f3ebc8c7eb | ||
|
|
2d4fc9dc97 | ||
|
|
afedb06dee | ||
|
|
3fd2146bbe | ||
|
|
8e70b6175a | ||
|
|
67e9c8ff06 | ||
|
|
bf59c53921 | ||
|
|
5589105f5c | ||
|
|
66d2aa26d3 | ||
|
|
1ae904dff2 | ||
|
|
4549f4ab91 | ||
|
|
75d5627378 | ||
|
|
8cbd27e629 | ||
|
|
333cc2ccf8 | ||
|
|
000dba1da5 | ||
|
|
11946f155d | ||
|
|
99713e873c | ||
|
|
ea5c993ff0 | ||
|
|
22d62f94bb | ||
|
|
8fd79f07d5 | ||
|
|
29fe80a6af | ||
|
|
e787b64978 | ||
|
|
4f9db5eba6 | ||
|
|
db5990de03 | ||
|
|
d4383ef854 | ||
|
|
d10c8c1b41 | ||
|
|
3c669548a2 | ||
|
|
ec11230b5e | ||
|
|
567473869b | ||
|
|
0b6733fdcb | ||
|
|
8e330862e1 | ||
|
|
b2adc77d76 | ||
|
|
82cbe9e282 | ||
|
|
93429c8bf3 | ||
|
|
150bef4d4d | ||
|
|
562fd875e9 | ||
|
|
5513a9df1b | ||
|
|
6fd2610eec | ||
|
|
11bf5e4691 | ||
|
|
cf0d82a706 | ||
|
|
b0cc17ea86 | ||
|
|
04d4bf6866 | ||
|
|
738123d4a7 | ||
|
|
a3ece09e66 | ||
|
|
8109c094db | ||
|
|
2c81582419 | ||
|
|
f8c79d9259 | ||
|
|
c7a894b34e | ||
|
|
5c36e045ff | ||
|
|
0f883d2ecf | ||
|
|
9dfcf41b64 | ||
|
|
1416f4e225 | ||
|
|
16c6a13fb1 | ||
|
|
9382cdc0f7 | ||
|
|
ced2719760 | ||
|
|
1798530284 | ||
|
|
68f890a015 | ||
|
|
5805038943 | ||
|
|
2395ceb3d2 | ||
|
|
2b455c5c6b | ||
|
|
d466e76702 | ||
|
|
9d33c85e13 | ||
|
|
4e24e7a533 | ||
|
|
0914c26b8d | ||
|
|
c245285123 | ||
|
|
be4ff6f94d | ||
|
|
aa055d0b00 | ||
|
|
e7d6e0d6ac | ||
|
|
8f97a194ae | ||
|
|
b191baec8b | ||
|
|
e4a85cdc61 | ||
|
|
12ee3213b0 | ||
|
|
6cba7ad8ba | ||
|
|
955c8bfb81 | ||
|
|
8602a259fd | ||
|
|
7404e63b53 | ||
|
|
c539eb57b7 | ||
|
|
3c4943015b | ||
|
|
c9fef6d80a | ||
|
|
5174781a7a | ||
|
|
7bf9f6a377 | ||
|
|
7575681e27 | ||
|
|
cfab3cf0cf | ||
|
|
a523a97a0e | ||
|
|
6f75bb938b | ||
|
|
a22046bc2a | ||
|
|
84ee15b2e7 | ||
|
|
fd6a8f1361 | ||
|
|
d22e75d7a0 | ||
|
|
ca25dfb537 | ||
|
|
e2159109b3 | ||
|
|
cc6bdbf06c | ||
|
|
cd37efc69d | ||
|
|
db08e62f1a | ||
|
|
3be653d55a | ||
|
|
e351730ad0 | ||
|
|
d7a232364f | ||
|
|
f7580e7ce9 | ||
|
|
26cec1591a | ||
|
|
52b58c5a51 |
No files matched your search
@@ -141,13 +141,80 @@ jobs:
|
||||
name: Build on aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v2.5.0
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
name: Run commands on aarch64
|
||||
id: make
|
||||
timeout-minutes: 60
|
||||
with:
|
||||
arch: aarch64
|
||||
distro: bullseye
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
shell: /bin/sh
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc
|
||||
lscpu
|
||||
cd tests && make check -j 2 && make clean
|
||||
|
||||
# The host should always be linux
|
||||
build_armv6:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build on armv6
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
name: Run commands on armv6
|
||||
id: make
|
||||
timeout-minutes: 60
|
||||
with:
|
||||
arch: armv6
|
||||
distro: alpine_latest
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
shell: /bin/sh
|
||||
run: |
|
||||
apk update
|
||||
apk add build-base gcc make
|
||||
cd tests && make check -j 2 && make clean
|
||||
|
||||
# The host should always be linux
|
||||
build_riscv64:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build on riscv64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
name: Run commands on riscv64
|
||||
id: make
|
||||
timeout-minutes: 60
|
||||
with:
|
||||
arch: riscv64
|
||||
distro: ubuntu22.04
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
shell: /bin/sh
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc
|
||||
lscpu
|
||||
cd tests && make check -j 2 && make clean
|
||||
|
||||
# The host should always be linux
|
||||
build_ppc64le:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build on ppc64le
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
name: Run commands on ppc64le
|
||||
id: make
|
||||
timeout-minutes: 60
|
||||
with:
|
||||
arch: ppc64le
|
||||
distro: ubuntu22.04
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
shell: /bin/sh
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2023, Patrick Pelissier
|
||||
Copyright (c) 2017-2025, Patrick Pelissier
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2017-2023, Patrick Pelissier
|
||||
# Copyright (c) 2017-2025, Patrick Pelissier
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@@ -34,14 +34,14 @@ DESTDIR=
|
||||
|
||||
# Package name & version.
|
||||
PACKAGE=m_lib-$(VERSION)
|
||||
VERSION=0.7.2
|
||||
VERSION=0.8.1
|
||||
|
||||
# Define the contain of the distribution tarball
|
||||
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-thread.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h
|
||||
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-list.h m-thread.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h m-bstring.h m-shared-ptr.h m-queue.h
|
||||
DOC1=LICENSE README.md
|
||||
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png doc/bench-array-log.png doc/bench-array.png doc/bench-list-log.png doc/bench-list.png doc/bench-oset-log.png doc/bench-oset.png doc/bench-umap-log.png doc/bench-umap.png doc/cc.sh
|
||||
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c example/ex-curl.c example/ex-dict05.c example/ex-dict06.c example/exm-lib.c example/exm-main.c example/exn-lib.c example/exn-main.c example/exm-header.h example/exn-header.h example/ex-alloc1.c example/ex-alloc2.c example/ex-try01.c example/ex11-generic01.c
|
||||
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mfuncobj.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/test-mishared.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp tests/test-mtree.c tests/check-bptree-map.cpp tests/check-bptree-set.cpp tests/check-prioqueue.cpp tests/check-umap.cpp tests/test-mtry.c tests/tgen-mmap.c tests/tgen-try.c tests/tgen-tuple.c tests/test-mgeneric.c
|
||||
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c example/ex-curl.c example/ex-dict05.c example/ex-dict06.c example/exm-lib.c example/exm-main.c example/exn-lib.c example/exn-main.c example/exm-header.h example/exn-header.h example/ex-alloc1.c example/ex-alloc2.c example/ex-try01.c example/ex11-generic01.c example/ex-bptree02.c example/ex-i-list.c example/ex11-generic02.c example/ex11-small-name.c example/ex11-variant01.c example/ex11-use-pool.c
|
||||
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mfuncobj.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp tests/test-mtree.c tests/check-bptree-map.cpp tests/check-bptree-set.cpp tests/check-prioqueue.cpp tests/check-umap.cpp tests/test-mtry.c tests/tgen-mmap.c tests/tgen-try.c tests/tgen-tuple.c tests/test-mgeneric.c tests/test-mbstring.c tests/except-array.c tests/except-bitset.c tests/except-deque.c tests/except-list.c tests/except-string.c tests/test-mshared-ptr.c tests/test-mshared-ptr.h tests/test-obj-except.h tests/test-mqueue.c
|
||||
|
||||
.PHONY: all test check doc clean distclean depend install uninstall dist
|
||||
|
||||
@@ -66,7 +66,6 @@ doc: html doc/depend.png
|
||||
clean:
|
||||
cd tests && $(MAKE) clean
|
||||
cd example && $(MAKE) clean
|
||||
cd bench && $(MAKE) clean
|
||||
$(RM) README.html depend.dot
|
||||
$(RM) -r 'm_lib-$(VERSION)'
|
||||
$(RM) 'm_lib-$(VERSION).tar.bz2'
|
||||
|
||||
-512
@@ -1,512 +0,0 @@
|
||||
# Copyright (c) 2017-2023, Patrick Pelissier
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# + Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# + Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
CC=cc -std=c11
|
||||
CXX=c++ -std=c++11
|
||||
CFLAGS=-O2 -march=native -Wall
|
||||
XCFLAGS=
|
||||
CPPFLAGS=-I..
|
||||
N=4
|
||||
BENCH_DEF=
|
||||
RM=rm -rf
|
||||
|
||||
.PHONY: all pgo container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-emhash bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain bench-rigtorp-mpmc-queue bench-cmc
|
||||
|
||||
all: container queue mempool string plain
|
||||
|
||||
pgo:
|
||||
$(MAKE) all XCFLAGS="-fprofile-generate=pgo"
|
||||
$(MAKE) all XCFLAGS="-fprofile-use=pgo"
|
||||
|
||||
#########################################################################
|
||||
|
||||
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-cmc bench-sparsepp bench-densehashmap bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-emhash bench-hopscotchmap bench-uthash bench-qlibc bench-libsrt bench-nedtries bench-rigtorp-hashmap bench-ctl bench-stc bench-pottery bench-CC
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
queue: bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-rigtorp-mpmc-queue
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
mempool: bench-mempool
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
string: bench-string
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
plain: bench-plain
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
|
||||
|
||||
#########################################################################
|
||||
|
||||
|
||||
bench-mlib:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -pthread bench-mlib.c common.c -o bench-mlib.exe
|
||||
@./bench-mlib.exe 10
|
||||
@./bench-mlib.exe 11
|
||||
@./bench-mlib.exe 20
|
||||
@./bench-mlib.exe 30
|
||||
@./bench-mlib.exe 31
|
||||
@./bench-mlib.exe 40
|
||||
@./bench-mlib.exe 41
|
||||
@./bench-mlib.exe 42
|
||||
@./bench-mlib.exe 46
|
||||
@./bench-mlib.exe 47
|
||||
@./bench-mlib.exe 43
|
||||
@./bench-mlib.exe 50
|
||||
@./bench-mlib.exe 51
|
||||
|
||||
bench-mlib-mempool:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
|
||||
@./bench-mlib-mempool.exe 10
|
||||
@./bench-mlib-mempool.exe 11
|
||||
@./bench-mlib-mempool.exe 20
|
||||
@./bench-mlib-mempool.exe 30
|
||||
@./bench-mlib-mempool.exe 31
|
||||
@./bench-mlib-mempool.exe 40
|
||||
@./bench-mlib-mempool.exe 41
|
||||
@./bench-mlib-mempool.exe 42
|
||||
@./bench-mlib-mempool.exe 46
|
||||
@./bench-mlib-mempool.exe 47
|
||||
@./bench-mlib-mempool.exe 43
|
||||
@./bench-mlib-mempool.exe 50
|
||||
@./bench-mlib-mempool.exe 51
|
||||
|
||||
bench-mlib-thread:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
|
||||
@./bench-mlib-thread.exe 60
|
||||
@./bench-mlib-thread.exe 61
|
||||
@./bench-mlib-thread.exe 62
|
||||
@./bench-mlib-thread.exe 63
|
||||
@./bench-mlib-thread.exe 64
|
||||
@./bench-mlib-thread.exe 65
|
||||
@./bench-mlib-thread.exe 66
|
||||
|
||||
bench-stl:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-stl.cpp common.c -o bench-stl.exe
|
||||
@./bench-stl.exe 10
|
||||
@./bench-stl.exe 20
|
||||
@./bench-stl.exe 30
|
||||
@./bench-stl.exe 40
|
||||
@./bench-stl.exe 41
|
||||
@./bench-stl.exe 42
|
||||
@./bench-stl.exe 46
|
||||
@./bench-stl.exe 43
|
||||
@./bench-stl.exe 50
|
||||
@./bench-stl.exe 51
|
||||
|
||||
# QT shall point to the the directory containing the file .pc are stored
|
||||
bench-qt:
|
||||
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
|
||||
bench-qt0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -fPIC bench-qt.cpp common.c -o bench-qt.exe `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core`
|
||||
@./bench-qt.exe 10
|
||||
@./bench-qt.exe 20
|
||||
@./bench-qt.exe 30
|
||||
@./bench-qt.exe 40
|
||||
@./bench-qt.exe 41
|
||||
@./bench-qt.exe 42
|
||||
@./bench-qt.exe 50
|
||||
|
||||
# GLIB shall point to the the directory containing the file .pc are stored
|
||||
bench-glib:
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
|
||||
bench-glib0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-glib.c common.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
|
||||
@./bench-glib.exe 10
|
||||
@./bench-glib.exe 20
|
||||
@./bench-glib.exe 30
|
||||
@./bench-glib.exe 40
|
||||
@./bench-glib.exe 41
|
||||
@./bench-glib.exe 50
|
||||
|
||||
# KLIB shall point to the directory where the headers are
|
||||
bench-klib:
|
||||
@if test -n "$${KLIB}" ; then $(MAKE) bench-klib0 ; else echo "Nothing to be done for KLIB." ; fi
|
||||
|
||||
bench-klib0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c common.c -o bench-klib.exe
|
||||
@./bench-klib.exe 10
|
||||
@./bench-klib.exe 20
|
||||
@./bench-klib.exe 30
|
||||
@./bench-klib.exe 40
|
||||
@./bench-klib.exe 41
|
||||
@./bench-klib.exe 50
|
||||
|
||||
# CMC shall point to the directory where the CMC main header is
|
||||
bench-cmc:
|
||||
@if test -n "$${CMC}" ; then $(MAKE) bench-cmc0 ; else echo "Nothing to be done for CMC." ; fi
|
||||
|
||||
bench-cmc0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CMC} bench-cmc.c common.c -o bench-cmc.exe
|
||||
@./bench-cmc.exe 20
|
||||
@./bench-cmc.exe 30
|
||||
@./bench-cmc.exe 40
|
||||
|
||||
# LIBDYNAMIC shall point to the the source directory where the source is built
|
||||
bench-libdynamic:
|
||||
@if test -n "$${LIBDYNAMIC}" ; then $(MAKE) bench-libdynamic0 ; else echo "Nothing to be done for LIBDYNAMIC." ; fi
|
||||
|
||||
bench-libdynamic0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-libdynamic.c common.c -o bench-libdynamic.exe -I$${LIBDYNAMIC}/src $${LIBDYNAMIC}/.libs/libdynamic.a
|
||||
@./bench-libdynamic.exe 20
|
||||
@./bench-libdynamic.exe 40
|
||||
@./bench-libdynamic.exe 41
|
||||
|
||||
# SPARSEPP shall point to the directory where the headers are
|
||||
bench-sparsepp:
|
||||
@if test -n "$${SPARSEPP}" ; then $(MAKE) bench-sparsepp0 ; else echo "Nothing to be done for SPARSEPP." ; fi
|
||||
|
||||
bench-sparsepp0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp common.c -o bench-sparsepp.exe
|
||||
@./bench-sparsepp.exe 41
|
||||
@./bench-sparsepp.exe 42
|
||||
@./bench-sparsepp.exe 43
|
||||
|
||||
# DENSEHASHMAP shall point to the directory where the src are
|
||||
bench-densehashmap:
|
||||
@if test -n "$${DENSEHASHMAP}" ; then $(MAKE) bench-densehashmap0 ; else echo "Nothing to be done Google DENSEHASHMAP." ; fi
|
||||
|
||||
bench-densehashmap0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp common.c -o bench-densehashmap.exe
|
||||
@./bench-densehashmap.exe 41
|
||||
@./bench-densehashmap.exe 42
|
||||
@./bench-densehashmap.exe 43
|
||||
|
||||
# COLLECTIONC shall point to the directory where it is installed
|
||||
bench-collectionc:
|
||||
@if test -n "$${COLLECTIONC}" ; then $(MAKE) bench-collectionc0 ; else echo "Nothing to be done for COLLECTIONC." ; fi
|
||||
|
||||
bench-collectionc0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include/collectc" bench-collectionc.c common.c "$${COLLECTIONC}/lib/libcollectc.a" -o bench-collectionc.exe
|
||||
@./bench-collectionc.exe 10
|
||||
@./bench-collectionc.exe 20
|
||||
@./bench-collectionc.exe 30
|
||||
@./bench-collectionc.exe 40
|
||||
@./bench-collectionc.exe 41
|
||||
@./bench-collectionc.exe 50
|
||||
|
||||
# TOMMYDS shall point to the source directory.
|
||||
bench-tommyds:
|
||||
@if test -n "$${TOMMYDS}" ; then $(MAKE) bench-tommyds0 ; else echo "Nothing to be done for TOMMYDS." ; fi
|
||||
|
||||
bench-tommyds0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c common.c -o bench-tommyds.exe
|
||||
@./bench-tommyds.exe 10
|
||||
@./bench-tommyds.exe 20
|
||||
@./bench-tommyds.exe 30
|
||||
@./bench-tommyds.exe 40
|
||||
@./bench-tommyds.exe 41
|
||||
|
||||
# FLATHASHMAP shall point to the directory where the headers are
|
||||
bench-flathashmap:
|
||||
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
|
||||
|
||||
bench-flathashmap0:
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp common.c -o bench-flathashmap.exe
|
||||
@./bench-flathashmap.exe 41
|
||||
@./bench-flathashmap.exe 42
|
||||
@./bench-flathashmap.exe 46
|
||||
@./bench-flathashmap.exe 43
|
||||
|
||||
# EMILIB shall point to the directory where the headers are
|
||||
bench-emilib:
|
||||
@if test -n "$${EMILIB}" ; then $(MAKE) bench-emilib0 ; else echo "Nothing to be done for EMILIB." ; fi
|
||||
|
||||
bench-emilib0:
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${EMILIB}" bench-emilib.cpp common.c -o bench-emilib.exe
|
||||
@./bench-emilib.exe 41
|
||||
@./bench-emilib.exe 42
|
||||
@./bench-emilib.exe 46
|
||||
@./bench-emilib.exe 43
|
||||
|
||||
# EMHASh shall point to the directory where the headers are
|
||||
bench-emhash:
|
||||
@if test -n "$${EMHASH}" ; then $(MAKE) bench-emhash0 ; else echo "Nothing to be done for EMHASH." ; fi
|
||||
|
||||
bench-emhash0:
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${EMHASH}" bench-emhash.cpp common.c -o bench-emhash.exe
|
||||
@./bench-emhash.exe 41
|
||||
@./bench-emhash.exe 42
|
||||
@./bench-emhash.exe 46
|
||||
@./bench-emhash.exe 43
|
||||
|
||||
# HOPSCOTCHMAP shall point to the directory where the source are
|
||||
bench-hopscotchmap:
|
||||
@if test -n "$${HOPSCOTCHMAP}" ; then $(MAKE) bench-hopscotchmap0 ; else echo "Nothing to be done for HOPSCOTCHMAP." ; fi
|
||||
|
||||
bench-hopscotchmap0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}/include/tsl" bench-hopscotchmap.cpp common.c -o bench-hopscotchmap.exe
|
||||
@./bench-hopscotchmap.exe 41
|
||||
@./bench-hopscotchmap.exe 42
|
||||
@./bench-hopscotchmap.exe 43
|
||||
|
||||
# UTHASH shall point to the directory where the source are
|
||||
bench-uthash:
|
||||
@if test -n "$${UTHASH}" ; then $(MAKE) bench-uthash0 ; else echo "Nothing to be done for UTHASH." ; fi
|
||||
|
||||
bench-uthash0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c common.c -o bench-uthash.exe
|
||||
@./bench-uthash.exe 10
|
||||
@./bench-uthash.exe 20
|
||||
@./bench-uthash.exe 40
|
||||
@./bench-uthash.exe 41
|
||||
@./bench-uthash.exe 50
|
||||
|
||||
# QLIBC shall point to the directory where the library is installed.
|
||||
bench-qlibc:
|
||||
@if test -n "$${QLIBC}" ; then $(MAKE) bench-qlibc0 ; else echo "Nothing to be done for QLIBC." ; fi
|
||||
|
||||
bench-qlibc0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c common.c -o bench-qlibc.exe
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 10
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 20
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 30
|
||||
|
||||
# LIBSRT shall point to the directory where it is installed
|
||||
bench-libsrt:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-libsrt0 ; else echo "Nothing to be done for LIBSRT." ; fi
|
||||
|
||||
bench-libsrt0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c common.c -o bench-libsrt.exe $${LIBSRT}/src/.libs/libsrt.a
|
||||
@./bench-libsrt.exe 20
|
||||
@./bench-libsrt.exe 50
|
||||
|
||||
|
||||
# LIBLFDS shall point to the directory where the bin/build/inc/obj/src directories are
|
||||
# The library shall be built.
|
||||
bench-liblfds:
|
||||
@if test -n "$${LIBLFDS}" ; then $(MAKE) bench-liblfds0 ; else echo "Nothing to be done for LIBLFDS." ; fi
|
||||
bench-liblfds0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c common.c -DMULTI_THREAD_MEASURE -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
|
||||
@LD_LIBRARY_PATH="$${LIBLFDS}/bin:$${LD_LIBRARY_PATH}" ./bench-liblfds.exe 60
|
||||
|
||||
|
||||
# CONCURRENTQUEUE shall point to the directory where the bin/build/inc/obj/src directories are
|
||||
# The library shall be built.
|
||||
bench-concurrentqueue:
|
||||
@if test -n "$${CONCURRENTQUEUE}" ; then $(MAKE) bench-concurrentqueue0 ; else echo "Nothing to be done for CONCURRENTQUEUE." ; fi
|
||||
bench-concurrentqueue0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-concurrentqueue.exe -lpthread
|
||||
@./bench-concurrentqueue.exe 60
|
||||
|
||||
|
||||
# XXHASH shall point to the source directory of xxhash
|
||||
# The library shall be built.
|
||||
bench-xxhash:
|
||||
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
|
||||
bench-xxhash0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c common.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
|
||||
@./bench-xxhash.exe 70
|
||||
|
||||
# BOOST shall point to the header location.
|
||||
bench-boost:
|
||||
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
|
||||
bench-boost0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp common.c -DMULTI_THREAD_MEASURE -o bench-boost-thread.exe -lpthread
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp common.c -o bench-boost.exe -lpthread
|
||||
@./bench-boost.exe 42
|
||||
@./bench-boost.exe 41
|
||||
@./bench-boost-thread.exe 60
|
||||
|
||||
# NEDTRIES shall point to the header location.
|
||||
bench-nedtries:
|
||||
@if test -n "$${NEDTRIES}" ; then $(MAKE) bench-nedtries0 ; else echo "Nothing to be done for NEDTRIES." ; fi
|
||||
bench-nedtries0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c common.c -o bench-nedtries.exe
|
||||
@./bench-nedtries.exe 40
|
||||
@./bench-nedtries.exe 45
|
||||
|
||||
# RIGTORP_HASHMAP shall point to the directory where the headers are
|
||||
bench-rigtorp-hashmap:
|
||||
@if test -n "$${RIGTORP_HASHMAP}" ; then $(MAKE) bench-rigtorp-hashmap0 ; else echo "Nothing to be done for RIGTORP_HASHMAP." ; fi
|
||||
|
||||
bench-rigtorp-hashmap0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_HASHMAP}/include" bench-rigtorp-hashmap.cpp common.c -o bench-rigtorp-hashmap.exe
|
||||
@./bench-rigtorp-hashmap.exe 42
|
||||
@./bench-rigtorp-hashmap.exe 41
|
||||
@./bench-rigtorp-hashmap.exe 43
|
||||
|
||||
# RIGTORP_MPMC_QUEUE shall point to the directory where the headers are
|
||||
bench-rigtorp-mpmc-queue:
|
||||
@if test -n "$${RIGTORP_MPMC_QUEUE}" ; then $(MAKE) bench-rigtorp-mpmc-queue0 ; else echo "Nothing to be done for RIGTORP_MPMC_QUEUE." ; fi
|
||||
bench-rigtorp-mpmc-queue0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_MPMC_QUEUE}" bench-rigtorp-mpmcqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-mpmcqueue.exe -lpthread
|
||||
@./bench-mpmcqueue.exe 60
|
||||
|
||||
# CTL shall point to the source directory (not where the headers are)
|
||||
bench-ctl:
|
||||
@if test -n "$${CTL}" ; then $(MAKE) bench-ctl0 ; else echo "Nothing to be done for CTL." ; fi
|
||||
|
||||
bench-ctl0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CTL} bench-ctl.c common.c -o bench-ctl.exe
|
||||
@./bench-ctl.exe 10
|
||||
@./bench-ctl.exe 20
|
||||
@./bench-ctl.exe 30
|
||||
@./bench-ctl.exe 40
|
||||
@./bench-ctl.exe 50
|
||||
|
||||
# STC shall point to the source directory (not where the headers are)
|
||||
bench-stc:
|
||||
@if test -n "$${STC}" ; then $(MAKE) bench-stc0 ; else echo "Nothing to be done for STC." ; fi
|
||||
|
||||
bench-stc0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${STC}/include bench-stc.c common.c -o bench-stc.exe
|
||||
@./bench-stc.exe 10
|
||||
@./bench-stc.exe 20
|
||||
@./bench-stc.exe 30
|
||||
@./bench-stc.exe 40
|
||||
@./bench-stc.exe 41
|
||||
@./bench-stc.exe 50
|
||||
|
||||
# POTTERY shall point to the source directory (not where the headers are)
|
||||
bench-pottery:
|
||||
@if test -n "$${POTTERY}" ; then $(MAKE) bench-pottery0 ; else echo "Nothing to be done for POTTERY." ; fi
|
||||
|
||||
bench-pottery0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DPOTTERY_AVAILABLE=1 -I $${POTTERY}/include bench-pottery.c common.c -o bench-pottery.exe
|
||||
@./bench-pottery.exe 10
|
||||
@./bench-pottery.exe 20
|
||||
@./bench-pottery.exe 30
|
||||
@./bench-pottery.exe 40
|
||||
@./bench-pottery.exe 41
|
||||
@./bench-pottery.exe 50
|
||||
|
||||
# CCLIB shall point to the source directory of CC (where the headers are)
|
||||
bench-CC:
|
||||
@if test -n "$${CCLIB}" ; then $(MAKE) bench-CC0 ; else echo "Nothing to be done for CC." ; fi
|
||||
|
||||
bench-CC0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CCLIB}/ bench-CC.c common.c -o bench-CC.exe
|
||||
@./bench-CC.exe 10
|
||||
@./bench-CC.exe 20
|
||||
@./bench-CC.exe 40
|
||||
@./bench-CC.exe 41
|
||||
|
||||
# VERSTABLE shall point to the source directory of VERSTABLE (where the headers are)
|
||||
bench-verstable:
|
||||
@if test -n "$${VERSTABLE}" ; then $(MAKE) bench-verstable0 ; else echo "Nothing to be done for VERSTABLE." ; fi
|
||||
|
||||
bench-verstable0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${VERSTABLE}/ bench-verstable.c common.c -o bench-verstable.exe
|
||||
@./bench-verstable.exe 40
|
||||
@./bench-verstable.exe 41
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-mlib-json: bench-mlib
|
||||
@./bench-mlib.exe 110
|
||||
@./bench-mlib.exe 111
|
||||
@./bench-mlib.exe 112
|
||||
|
||||
bench-rapidjson:
|
||||
@if test -n "$${RAPIDJSON}" ; then $(MAKE) bench-rapidjson0 ; else echo "Nothing to be done for RAPIDJSON." ; fi
|
||||
bench-rapidjson0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RAPIDJSON}/" bench-rapidjson.cpp common.c -o bench-rapidjson.exe
|
||||
@./bench-rapidjson.exe 110
|
||||
@./bench-rapidjson.exe 111
|
||||
@./bench-rapidjson.exe 112
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-plain:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
|
||||
@./bench-plain-c.exe
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-mempool:
|
||||
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
|
||||
bench-mempool0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
|
||||
@./bench-mempool.exe
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-maxdict:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
|
||||
@./bench-max-dict.exe
|
||||
|
||||
############################################################################
|
||||
|
||||
# BSTRLIB shall point to the BSTRLIB sources
|
||||
# LIBSRT shall point to the LIBSRT sources
|
||||
bench-string:
|
||||
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-step1 BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-step1 ; fi
|
||||
|
||||
bench-string-step1:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/src/.libs/libsrt.a" ; else $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
|
||||
bench-string-step2:
|
||||
@if test -n "$${SDS}" ; then $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-step4:
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_GLIB $$(pkg-config --cflags --libs glib-2.0)" ; else $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-step5:
|
||||
@if test -n "$${POTTERY}" ; then $(MAKE) bench-string-step6 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_POTTERY -I$${POTTERY}/include -I$${POTTERY}/util/ $${POTTERY}/util/pottery/string/string.c" ; else $(MAKE) bench-string-step6 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-step6:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp common.c $(BENCH_DEF) -o bench-string.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string-2.cpp common.c $(BENCH_DEF) -o bench-string-2.exe
|
||||
@./bench-string.exe
|
||||
@./bench-string-2.exe 2000000 0
|
||||
@./bench-string-2.exe 2000000 1
|
||||
@./bench-string-2.exe 2000000 2
|
||||
@./bench-string-2.exe 2000000 4
|
||||
@./bench-string-2.exe 2000000 5
|
||||
|
||||
bench-string-pgo:
|
||||
$(MAKE) bench-string XCFLAGS="-fprofile-generate=pgo"
|
||||
$(MAKE) bench-string XCFLAGS="-fprofile-use=pgo"
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-find-sort:
|
||||
g++ bench-find-sort.cpp -O2 -march=native -std=c++11 -I.. -g common.c -o bench-find-sort.exe
|
||||
./bench-find-sort.exe 10000000
|
||||
|
||||
############################################################################
|
||||
|
||||
clean:
|
||||
$(RM) *.o bench-*.exe
|
||||
$(RM) bench-*.s *~ massif.out.*
|
||||
$(RM) plot-*.dat
|
||||
$(RM) pgo
|
||||
$(RM) tmp-serial.*
|
||||
cd nucleotide && $(MAKE) clean
|
||||
|
||||
############################################################################
|
||||
|
||||
massif-do: all
|
||||
@valgrind --tool=massif ./bench-mlib.exe $(N)
|
||||
@valgrind --tool=massif ./bench-mlib-mempool.exe $(N)
|
||||
@valgrind --tool=massif ./bench-stl.exe $(N)
|
||||
@valgrind --tool=massif ./bench-qt.exe $(N)
|
||||
@valgrind --tool=massif ./bench-glib.exe $(N)
|
||||
@test -f ./bench-klib.exe && valgrind --tool=massif ./bench-klib.exe $(N)
|
||||
@test -f ./bench-libdynamic.exe && valgrind --tool=massif ./bench-libdynamic.exe $(N)
|
||||
|
||||
massif-visu:
|
||||
test -f massif.out.* || $(MAKE) massif-do
|
||||
for i in massif.out.* ; do ms_print $$i|head -30 ; done
|
||||
@@ -1,140 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "cc.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
vec( unsigned int ) a1, a2;
|
||||
init(&a1);
|
||||
init(&a2);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
push(&a1, rand_get() );
|
||||
push(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *get(&a1, i ) * *get(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cleanup(&a1);
|
||||
cleanup(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_list(size_t n)
|
||||
{
|
||||
list( unsigned int ) a1, a2;
|
||||
init(&a1);
|
||||
init(&a2);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
push(&a1, rand_get() );
|
||||
push(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned *it1 = first( &a1 ), *it2 = first( &a2 ); it1 != end( &a1 ); it1 = next( &a1, it1 ), it2 = next(&a2, it2) ) {
|
||||
s += *it1 * *it2;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cleanup(&a1);
|
||||
cleanup(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
map( unsigned long, unsigned long ) dict;
|
||||
init(&dict);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
insert(&dict, key, value);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
const unsigned long *val = get(&dict, key);
|
||||
if (val != NULL)
|
||||
s += *val;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cleanup(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
|
||||
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
|
||||
static inline bool char_equal_p (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a)==0; }
|
||||
static inline bool char_cmp (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a); }
|
||||
static inline void char_clear (char_array_t *a) { (void)a; }
|
||||
static inline size_t char_hash(const char_array_t *a) {
|
||||
size_t hash = 0;
|
||||
const char *s = a->a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
#define CC_DTOR char_array_t, { char_clear(&val); }
|
||||
#define CC_CMPR char_array_t, { return char_cmp(&val_1, &val_2); }
|
||||
#define CC_HASH char_array_t, { return char_hash(&val); }
|
||||
#include "cc.h"
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
map( char_array_t, char_array_t) dict;
|
||||
init(&dict);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
insert(&dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
const char_array_t *val = get(&dict, s1);
|
||||
if (val != NULL)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cleanup(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "m-thread.h"
|
||||
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/lockfree/queue.hpp>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
boost::unordered_flat_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
boost::unordered_flat_map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
boost::unordered_flat_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++) {
|
||||
boost::unordered_flat_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
inline std::size_t hash_value(const char_array_s &k) {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
boost::unordered_flat_map<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
boost::unordered_flat_map<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
boost::unordered_flat_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
boost::unordered_flat_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
boost::lockfree::queue<unsigned int, boost::lockfree::capacity<4*64>> g_buff;
|
||||
boost::lockfree::queue<unsigned long long, boost::lockfree::capacity<4*64>> g_final;
|
||||
|
||||
static void final(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*) arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!g_final.pop(j));
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!g_buff.pop(j));
|
||||
s += j;
|
||||
}
|
||||
while (!g_final.push(s));
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!g_buff.push(r));
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
rand_get();
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 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},
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("BOOST", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macro_collections.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// CMC list are dynamic array
|
||||
#define MY_LIST_PARAMS (list_uint, list_uint_s, , , unsigned int)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, LIST, MY_LIST_PARAMS, (STR))
|
||||
|
||||
struct list_uint_s_fval list_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
struct list_uint_s *a1 = list_uint_new(1, &list_fval);
|
||||
struct list_uint_s *a2 = list_uint_new(1, &list_fval);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
list_uint_push_back(a1, rand_get() );
|
||||
list_uint_push_back(a2, rand_get() );
|
||||
}
|
||||
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += list_uint_get(a1, i ) * list_uint_get(a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
list_uint_free(a1);
|
||||
list_uint_free(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define my_cmp(a,b) ( (a) < (b) ? -1 : (a) > (b) )
|
||||
|
||||
#define MY_TMAP_PARAMS (tree_ulong, tree_ulong_s, , unsigned long, bool)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, TREEMAP, MY_TMAP_PARAMS, (STR))
|
||||
|
||||
int cmp_ulong(unsigned long a, unsigned long b) { return my_cmp(a,b); }
|
||||
|
||||
struct tree_ulong_s_fkey tree_fkey = { .cmp = cmp_ulong,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
struct tree_ulong_s_fval tree_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
struct tree_ulong_s *tree;
|
||||
tree = tree_ulong_new(&tree_fkey, &tree_fval);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
tree_ulong_insert(tree, j, true);
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (tree_ulong_get_ref(tree, j) != 0)
|
||||
s+= j;
|
||||
}
|
||||
g_result = s;
|
||||
tree_ulong_free(tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
#define hash_func(a) kh_int64_hash_func(a)
|
||||
size_t hash_ulong(unsigned long a)
|
||||
{
|
||||
return ( (a) >> 33 ) ^ (a) ^ ((a) << 11);
|
||||
}
|
||||
|
||||
#define MY_DICT_PARAMS (hmap, hmap_s, , unsigned long, unsigned long)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, HASHMAP, MY_DICT_PARAMS, (STR))
|
||||
|
||||
struct hmap_s_fkey hmap_fkey = { .cmp = cmp_ulong,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = hash_ulong,
|
||||
.pri = NULL };
|
||||
|
||||
struct hmap_s_fval hmap_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
struct hmap_s *dict = hmap_new(16, 0.7, &hmap_fkey, &hmap_fval);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
hmap_insert(dict, rand_get(), rand_get());
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *val = hmap_get_ref(dict, rand_get());
|
||||
if (val != 0)
|
||||
s += *val;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
hmap_free(dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#if 0 // Not working issue in using not integer types
|
||||
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
static inline int char_cmp (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a); }
|
||||
static inline size_t char_hash(const char_array_t a) {
|
||||
size_t hash = 0;
|
||||
const char *s = a.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
#define MY_DICTB_PARAMS (bhmap, bhmap_s, , char_array_t, char_array_t)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, HASHMAP, MY_DICT_PARAMS, (STR))
|
||||
|
||||
struct bhmap_s_fkey bhmap_fkey = { .cmp = char_cmp,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = char_hash,
|
||||
.pri = NULL };
|
||||
|
||||
struct bhmap_s_fval bhmap_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
struct bhmap_s *dict = bhmap_new(16, 0.7, &bhmap_fkey, &bhmap_fval);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
bhmap_insert(dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1.a, "%u", rand_get() );
|
||||
char_array_t *val = bhmap_get_ref(dict, s1);
|
||||
if (val != 0)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
bhmap_free(dict);
|
||||
}
|
||||
#endif
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
//{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CMC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "array.h"
|
||||
#include "slist.h"
|
||||
#include "treetable.h"
|
||||
#include "hashtable.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
Array *a1, *a2;
|
||||
enum cc_stat stat;
|
||||
|
||||
stat = array_new(&a1);
|
||||
if (stat != CC_OK) abort();
|
||||
stat = array_new(&a2);
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
stat = array_add(a1, (void*) (uintptr_t)rand_get() );
|
||||
if (stat != CC_OK) abort();
|
||||
stat = array_add(a2, (void*) (uintptr_t)rand_get() );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
void *e, *f;
|
||||
stat = array_get_at(a1, i, &e);
|
||||
if (stat != CC_OK) abort();
|
||||
stat = array_get_at(a2, i, &f);
|
||||
if (stat != CC_OK) abort();
|
||||
s += ((unsigned int)(uintptr_t) e) * ((unsigned int)(uintptr_t) f);
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
array_destroy(a1);
|
||||
array_destroy(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_list(size_t n)
|
||||
{
|
||||
SList *a1, *a2;
|
||||
enum cc_stat stat;
|
||||
|
||||
stat = slist_new(&a1);
|
||||
if (stat != CC_OK) abort();
|
||||
stat = slist_new(&a2);
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
stat = slist_add(a1, (void*) (uintptr_t)rand_get() );
|
||||
if (stat != CC_OK) abort();
|
||||
stat = slist_add(a2, (void*) (uintptr_t)rand_get() );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
unsigned int s = 0;
|
||||
SListIter iter1, iter2;
|
||||
void *e, *f;
|
||||
slist_iter_init(&iter1, a1);
|
||||
slist_iter_init(&iter2, a2);
|
||||
while (slist_iter_next(&iter1, &e) == CC_OK && slist_iter_next(&iter2, &f) == CC_OK) {
|
||||
s += ((unsigned int)(uintptr_t) e) * ((unsigned int)(uintptr_t) f);
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
slist_destroy(a1);
|
||||
slist_destroy(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static int compare(const void *a, const void *b)
|
||||
{
|
||||
const uintptr_t pa = (uintptr_t) a;
|
||||
const uintptr_t pb = (uintptr_t) b;
|
||||
return (pa < pb) ? -1 : (pa > pb);
|
||||
}
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
TreeTable *tree;
|
||||
enum cc_stat stat;
|
||||
|
||||
stat = treetable_new(compare, &tree);
|
||||
if (stat != CC_OK) abort();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
stat = treetable_add(tree, key, key);
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *e;
|
||||
void *key = (void*) (uintptr_t) rand_get();
|
||||
stat = treetable_get(tree, key, &e);
|
||||
if (stat == CC_OK)
|
||||
s += (uintptr_t)e;
|
||||
}
|
||||
g_result = s;
|
||||
treetable_destroy(tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static size_t hash_func (const void *key, int l, uint32_t seed)
|
||||
{
|
||||
const unsigned long pa = (uintptr_t) key;
|
||||
(void)l;
|
||||
return pa ^ seed;
|
||||
}
|
||||
|
||||
static int equal_func(const void *key1, const void *key2)
|
||||
{
|
||||
const unsigned long pa = (uintptr_t) key1;
|
||||
const unsigned long pb = (uintptr_t) key2;
|
||||
return (pa < pb) ? -1 : pa > pb;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
HashTable *dict;
|
||||
enum cc_stat stat;
|
||||
HashTableConf htc;
|
||||
hashtable_conf_init(&htc);
|
||||
htc.hash = hash_func;
|
||||
htc.key_compare = equal_func;
|
||||
stat = hashtable_new_conf(&htc, &dict);
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *value = (void*)(uintptr_t) rand_get();
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
stat = hashtable_add(dict, key, value );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
void *r;
|
||||
stat = hashtable_get(dict, key, &r);
|
||||
if (stat == CC_OK)
|
||||
s += (uintptr_t) r;
|
||||
}
|
||||
g_result = s;
|
||||
hashtable_destroy(dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
static int char_equal (const void* a, const void * b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb);
|
||||
}
|
||||
|
||||
static size_t char_hash(const void *a, int l, uint32_t seed)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char *s = *pa;
|
||||
(void)l ;
|
||||
size_t hash = seed;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
HashTable *dict;
|
||||
enum cc_stat stat;
|
||||
HashTableConf htc;
|
||||
hashtable_conf_init(&htc);
|
||||
htc.hash = char_hash;
|
||||
htc.key_compare = char_equal;
|
||||
stat = hashtable_new_conf(&htc, &dict);
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
char_array_t **tab = malloc (sizeof(char_array_t *)*2*n);
|
||||
if (!tab) abort();
|
||||
size_t cpt = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t *key = malloc (sizeof (char_array_t));
|
||||
char_array_t *value = malloc (sizeof (char_array_t));
|
||||
tab[cpt++] = key;
|
||||
tab[cpt++] = value;
|
||||
sprintf(*key, "%u", rand_get());
|
||||
sprintf(*value, "%u", rand_get());
|
||||
stat = hashtable_add(dict, key, value );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
void *r;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
stat = hashtable_get(dict, s1, &r);
|
||||
if (stat == CC_OK)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
hashtable_destroy(dict);
|
||||
if (cpt != 2*n) abort();
|
||||
for(size_t i = 0; i < 2*n;i++)
|
||||
free(tab[i]);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static int cmp_float(const void * a, const void *b)
|
||||
{
|
||||
const float *const*pa = a, *const*pb = b;
|
||||
return (**pa < **pb) ? -1 : (**pa > **pb);
|
||||
}
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
Array *a1;
|
||||
enum cc_stat stat;
|
||||
|
||||
stat = array_new(&a1);
|
||||
if (stat != CC_OK) abort();
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
float *v = malloc(sizeof(float));
|
||||
*v = rand_get();
|
||||
stat = array_add(a1, v );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
array_sort(a1, cmp_float);
|
||||
void *e;
|
||||
stat = array_get_at(a1, 0, &e);
|
||||
if (stat != CC_OK) abort();
|
||||
g_result = *(float*)e;
|
||||
array_destroy(a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CollectionC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
moodycamel::ConcurrentQueue<unsigned int> g_buff;
|
||||
moodycamel::ConcurrentQueue<unsigned long long> g_final;
|
||||
|
||||
static void final(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*) arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!g_final.try_dequeue(j));
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!g_buff.try_dequeue(j));
|
||||
s += j;
|
||||
}
|
||||
while (!g_final.try_enqueue(s));
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!g_buff.try_enqueue(r));
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
rand_get();
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CONCURRENT-QUEUE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "m-core.h"
|
||||
#include "common.h"
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
/********************************************************************************************/
|
||||
#define POD
|
||||
#define T uint
|
||||
#include "ctl/vector.h"
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
vec_uint a1 = vec_uint_init();
|
||||
vec_uint a2 = vec_uint_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
vec_uint_push_back(&a1, rand_get() );
|
||||
vec_uint_push_back(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *vec_uint_at(&a1, i ) * *vec_uint_at(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
vec_uint_free(&a1);
|
||||
vec_uint_free(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POD
|
||||
#define T uint
|
||||
#include "ctl/forward_list.h"
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
slist_uint a1 = slist_uint_init();
|
||||
slist_uint a2 = slist_uint_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
slist_uint_push_front(&a1, rand_get());
|
||||
slist_uint_push_front(&a2, rand_get());
|
||||
}
|
||||
unsigned int s = 0;
|
||||
slist_uint_it it1 = slist_uint_begin(&a1);
|
||||
slist_uint_it it2 = slist_uint_begin(&a2);
|
||||
for( ; !slist_uint_it_done(&it1) ;
|
||||
slist_uint_it_next(&it1), slist_uint_it_next(&it2)) {
|
||||
s += *slist_uint_it_ref(&it1) * *slist_uint_it_ref(&it2);
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
slist_uint_free(&a1);
|
||||
slist_uint_free(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POD
|
||||
#define T ulong
|
||||
#include "ctl/set.h"
|
||||
|
||||
static inline int
|
||||
int_cmp(ulong *a, ulong *b) {
|
||||
return *a < *b;
|
||||
}
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
set_ulong tree = set_ulong_init (int_cmp);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
set_ulong_insert (&tree, rand_get() );
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (set_ulong_find_node(&tree, j) != NULL)
|
||||
s+= j;
|
||||
}
|
||||
g_result = s;
|
||||
set_ulong_free(&tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
} pair_ulong;
|
||||
|
||||
// Use of M*LIB hash function
|
||||
static inline size_t
|
||||
pair_ulong_hash(pair_ulong *a)
|
||||
{
|
||||
return M_HASH_DEFAULT(a->key);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pair_ulong_equal(pair_ulong *a, pair_ulong *b)
|
||||
{
|
||||
return a->key == b->key;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pair_ulong_free(pair_ulong *a) {
|
||||
(void) a;
|
||||
}
|
||||
|
||||
static inline pair_ulong
|
||||
pair_ulong_copy(pair_ulong *self) {
|
||||
return (pair_ulong) { .key = self->key, .value = self->value };
|
||||
}
|
||||
|
||||
#define T pair_ulong
|
||||
#include <ctl/unordered_map.h>
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
umap_pair_ulong dict = umap_pair_ulong_init(pair_ulong_hash, pair_ulong_equal);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
umap_pair_ulong_insert(&dict, (pair_ulong) { key, value });
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
umap_pair_ulong_it it = umap_pair_ulong_find(&dict, (pair_ulong) { rand_get() , 0 } );
|
||||
if (!umap_pair_ulong_it_done(&it) )
|
||||
s += it.ref->value;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
umap_pair_ulong_free(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POD
|
||||
#define T float
|
||||
#include "ctl/vector.h"
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
vec_float a1 = vec_float_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
vec_float_push_back(&a1, rand_get());
|
||||
}
|
||||
vec_float_sort(&a1);
|
||||
g_result = *vec_float_at(&a1, 0);
|
||||
vec_float_free(&a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 50, "Sort", 10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CTL", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <google/dense_hash_map>
|
||||
|
||||
using google::dense_hash_map;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
dense_hash_map<unsigned long, unsigned long> dict;
|
||||
dict.set_empty_key(-1);
|
||||
dict.set_deleted_key(-2);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dense_hash_map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
namespace tr1 {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(char_array_s k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
dense_hash_map<char_array_s, char_array_s> dict;
|
||||
struct char_array_s empty, del;
|
||||
memset(&empty, 0, sizeof empty); empty.a[1] = 255;
|
||||
memset(&del, 0, sizeof del); del.a[1] = 255;
|
||||
dict.set_empty_key(empty);
|
||||
dict.set_deleted_key(del);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
dense_hash_map<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
dense_hash_map<string, string> dict;
|
||||
string empty, del;
|
||||
empty = "";
|
||||
del = "DEL";
|
||||
dict.set_empty_key(empty);
|
||||
dict.set_deleted_key(del);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dense_hash_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hash_table7.hpp"
|
||||
|
||||
using namespace emhash7;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
HashMap<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
HashMap<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
HashMap<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
HashMap<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("EMHASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "emilib/hash_map.hpp"
|
||||
|
||||
using namespace emilib;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
HashMap<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
HashMap<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
HashMap<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
HashMap<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("EMILIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// program to find longest contiguous subsequence
|
||||
// based on https://www.geeksforgeeks.org/longest-consecutive-subsequence/
|
||||
#include <iostream>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Returns length of the longest contiguous subsequence
|
||||
int cpp_find_longest(int arr[], int n)
|
||||
{
|
||||
unordered_set<int> S;
|
||||
int ans = 0;
|
||||
|
||||
// Hash all the array elements
|
||||
for (int i = 0; i < n; i++)
|
||||
S.insert(arr[i]);
|
||||
|
||||
// check each possible sequence from
|
||||
// the start then update optimal length
|
||||
for (int i = 0; i < n; i++) {
|
||||
// if current element is the starting
|
||||
// element of a sequence
|
||||
if (S.find(arr[i] - 1) == S.end()) {
|
||||
// Then check for next elements
|
||||
// in the sequence
|
||||
int j = arr[i] + 1;
|
||||
while (S.find(j) != S.end())
|
||||
j++;
|
||||
|
||||
// update optimal length if
|
||||
// this length is more
|
||||
ans = max(ans, j - arr[i]);
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
#include "m-dict.h"
|
||||
|
||||
static inline bool oor_equal_p(int a, unsigned char c) { return a == ((c == 0) ? INT_MIN : INT_MAX); }
|
||||
static inline void oor_set(int *a, unsigned char c) { *a = ((c == 0) ? INT_MIN : INT_MAX); }
|
||||
|
||||
DICT_OASET_DEF(dict_int, int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)))
|
||||
|
||||
// Returns length of the longest contiguous subsequence
|
||||
int mlib_find_longest(int arr[], int n)
|
||||
{
|
||||
dict_int_t S;
|
||||
int ans = 0;
|
||||
|
||||
dict_int_init(S);
|
||||
|
||||
// Hash all the array elements
|
||||
for (int i = 0; i < n; i++)
|
||||
dict_int_push(S, arr[i]);
|
||||
|
||||
// check each possible sequence from
|
||||
// the start then update optimal length
|
||||
for (int i = 0; i < n; i++) {
|
||||
// if current element is the starting
|
||||
// element of a sequence
|
||||
int *p = dict_int_get(S, arr[i]-1);
|
||||
if (p == NULL) {
|
||||
// Then check for next elements
|
||||
// in the sequence
|
||||
int j = arr[i] + 1;
|
||||
while (dict_int_get(S, j) != NULL)
|
||||
j++;
|
||||
|
||||
// update optimal length if
|
||||
// this length is more
|
||||
ans = M_MAX(ans, j - arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dict_int_clear(S);
|
||||
return ans;
|
||||
}
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = argc == 1 ? 10000 : atoi(argv[1]);
|
||||
int *arr = (int*) malloc(n * sizeof(int));
|
||||
for(int i = 0; i < n; i++)
|
||||
arr[i] = rand_get();
|
||||
|
||||
unsigned long long t0 = cputime();
|
||||
cout << "Length of the Longest contiguous subsequence is "
|
||||
<< cpp_find_longest(arr, n) << endl;
|
||||
|
||||
unsigned long long t1 = cputime();
|
||||
|
||||
cout << "Length of the Longest contiguous subsequence is "
|
||||
<< mlib_find_longest(arr, n) << endl;
|
||||
unsigned long long t2 = cputime();
|
||||
|
||||
cout << "Time C++: " << (t1-t0) << " Time M*LIB: " << (t2-t1) << endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// Need recent gcc
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "flat_hash_map.hpp"
|
||||
|
||||
using namespace ska;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
flat_hash_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
flat_hash_map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
flat_hash_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
flat_hash_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
flat_hash_map<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
flat_hash_map<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
flat_hash_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
flat_hash_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("FLAT-HASPMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
GArray *a1, *a2;
|
||||
a1 = g_array_new (FALSE, FALSE, sizeof (unsigned int));
|
||||
a2 = g_array_new (FALSE, FALSE, sizeof (unsigned int));
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int v = rand_get();
|
||||
g_array_append_val(a1, v );
|
||||
v = rand_get();
|
||||
g_array_append_val(a2, v );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += g_array_index(a1, unsigned int, i ) * g_array_index(a2, unsigned int, i );
|
||||
}
|
||||
g_result = s;
|
||||
g_array_free(a1, FALSE);
|
||||
g_array_free(a2, FALSE);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
GSList *a1 = NULL, *a2 = NULL;
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1 = g_slist_prepend (a1, GINT_TO_POINTER (rand_get() ));
|
||||
a2 = g_slist_prepend (a2, GINT_TO_POINTER (rand_get() ));
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(GSList *it1 = a1, *it2 = a2 ; it1 != NULL; it1 = g_slist_next(it1), it2 = g_slist_next(it2) ) {
|
||||
s += GPOINTER_TO_INT(it1->data) * GPOINTER_TO_INT(it2->data);
|
||||
n--;
|
||||
}
|
||||
g_result = s;
|
||||
g_slist_free(a1);
|
||||
g_slist_free(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static int compare(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
const unsigned long *pb = b;
|
||||
(void) user_data;
|
||||
return (*pa < *pb) ? -1 : (*pa > *pb);
|
||||
}
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
GTree *tree = g_tree_new_full(compare, NULL, free, NULL);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
// NOTE: unsigned long are not designed to work with GPOINTER_TO_INT according to documentation.
|
||||
unsigned long *p = malloc(sizeof(unsigned long));
|
||||
*p = rand_get();
|
||||
g_tree_insert(tree, p, p);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
unsigned long *p = g_tree_lookup(tree, &key);
|
||||
if (p)
|
||||
s += *p;
|
||||
}
|
||||
g_result = s;
|
||||
g_tree_destroy(tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static guint hash_func (gconstpointer a)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
return *pa;
|
||||
}
|
||||
|
||||
static gboolean equal_func(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
const unsigned long *pb = b;
|
||||
return *pa == *pb;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
GHashTable *dict = g_hash_table_new_full(hash_func, equal_func, free, free);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *key = malloc(sizeof(unsigned long));
|
||||
unsigned long *value = malloc(sizeof(unsigned long));
|
||||
*key = rand_get();
|
||||
*value = rand_get();
|
||||
g_hash_table_insert(dict, key, value );
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
unsigned long *p = g_hash_table_lookup(dict, &key);
|
||||
if (p)
|
||||
s += *p;
|
||||
}
|
||||
g_result = s;
|
||||
g_hash_table_destroy(dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
static gboolean char_equal (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
}
|
||||
|
||||
static guint char_hash(gconstpointer a)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char *s = *pa;
|
||||
guint hash = 0;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
GHashTable *dict = g_hash_table_new_full(char_hash, char_equal, free, free);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t *key = malloc (sizeof (char_array_t));
|
||||
char_array_t *value = malloc (sizeof (char_array_t));
|
||||
sprintf(*key, "%u", rand_get());
|
||||
sprintf(*value, "%u", rand_get());
|
||||
g_hash_table_insert(dict, key, value );
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
char_array_t *p = g_hash_table_lookup(dict, &s1);
|
||||
if (p)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
g_hash_table_destroy(dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static gint cmp_float(gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const float *pa = a, *pb = b;
|
||||
return (*pa < *pb) ? -1 : (*pa > *pb);
|
||||
}
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
GArray *a1 = g_array_new (FALSE, FALSE, sizeof (float));
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
float v = rand_get();
|
||||
g_array_append_val(a1, v );
|
||||
}
|
||||
g_array_sort(a1, cmp_float);
|
||||
g_result = g_array_index (a1, float, 0);
|
||||
g_array_free(a1, FALSE);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("GLIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hopscotch_map.h"
|
||||
|
||||
using namespace tsl;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
tsl::hopscotch_map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
hopscotch_map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
hopscotch_map<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
hopscotch_map<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
hopscotch_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
hopscotch_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("HOPSCOTCHMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "kvec.h"
|
||||
#include "klist.h"
|
||||
#include "kbtree.h"
|
||||
#include "khash.h"
|
||||
#include "ksort.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
kvec_t(unsigned int) a1, a2;
|
||||
|
||||
kv_init(a1);
|
||||
kv_init(a2);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
kv_push(unsigned int, a1, rand_get() );
|
||||
kv_push(unsigned int, a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += kv_A(a1, i ) * kv_A(a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
kv_destroy(a1);
|
||||
kv_destroy(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define uint_free(x)
|
||||
KLIST_INIT(list_uint, unsigned int, uint_free)
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
klist_t(list_uint) *a1, *a2;
|
||||
a1 = kl_init(list_uint);
|
||||
a2 = kl_init(list_uint);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
*kl_pushp (list_uint, a1) = rand_get();
|
||||
*kl_pushp (list_uint, a2) = rand_get();
|
||||
}
|
||||
unsigned int s = 0;
|
||||
kliter_t(list_uint) *it1, *it2;
|
||||
for(it1 = kl_begin(a1), it2 = kl_begin(a2);
|
||||
it1 != kl_end(a1);
|
||||
it1 = kl_next(it1), it2 = kl_next(it2)) {
|
||||
s += kl_val(it1) * kl_val(it2);
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
kl_destroy(list_uint, a1);
|
||||
kl_destroy(list_uint, a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define my_cmp(a,b) ( (a) < (b) ? -1 : (a) > (b) )
|
||||
|
||||
KBTREE_INIT(kTree, unsigned long, my_cmp)
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
kbtree_t(kTree) *tree;
|
||||
tree = kb_init(kTree, KB_DEFAULT_SIZE/2);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
kb_putp(kTree, tree, &j);
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (kb_getp(kTree, tree, &j) != 0)
|
||||
s+= j;
|
||||
}
|
||||
g_result = s;
|
||||
kb_destroy(kTree, tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define hash_eq(a, b) ((a) == (b))
|
||||
#define hash_func(a) kh_int64_hash_func(a)
|
||||
|
||||
KHASH_INIT(iun, unsigned long, unsigned long, 1, hash_func, hash_eq)
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
khash_t(iun) *dict = kh_init(iun);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
int ret;
|
||||
khiter_t k = kh_put(iun, dict, rand_get(), &ret);
|
||||
kh_value(dict, k) = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
khiter_t k = kh_get(iun, dict, rand_get());
|
||||
if (kh_exist(dict, k))
|
||||
s += kh_value(dict, k);
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
kh_destroy(iun, dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
|
||||
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
|
||||
static inline bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a)==0; }
|
||||
static inline size_t char_hash(const char_array_t a) {
|
||||
size_t hash = 0;
|
||||
const char *s = a.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
KHASH_INIT(iub, char_array_t, char_array_t, 1, char_hash, char_equal_p)
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
khash_t(iub) *dict = kh_init(iub);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
int ret;
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
khiter_t k = kh_put(iub, dict, s1, &ret);
|
||||
char_set(&kh_value(dict, k), s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
khiter_t k = kh_get(iub, dict, s1);
|
||||
if (kh_exist(dict, k))
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
kh_destroy(iub, dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
// NOTE: Needed for compiling
|
||||
double drand48(void) { return rand_get(); }
|
||||
|
||||
KSORT_INIT_GENERIC(float)
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
kvec_t(float) a1;
|
||||
|
||||
kv_init(a1);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
kv_push(float, a1, rand_get() );
|
||||
}
|
||||
// NOTE: Is-it the right way to do? Seems very low level!
|
||||
ks_introsort_float(n, & kv_A(a1, 0));
|
||||
g_result = kv_A(a1, 0);
|
||||
kv_destroy(a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("KLIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Needed due to compilation error in pool.h
|
||||
#include <pthread.h>
|
||||
|
||||
#include "dynamic.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
vector a1, a2;
|
||||
|
||||
vector_construct(&a1, sizeof(unsigned int));
|
||||
vector_construct(&a2, sizeof(unsigned int));
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int v = rand_get();
|
||||
vector_push_back(&a1, &v);
|
||||
v = rand_get();
|
||||
vector_push_back(&a2, &v);
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *(unsigned int*) vector_at(&a1, i) * *(unsigned int*) vector_at(&a2, i);
|
||||
}
|
||||
g_result = s;
|
||||
vector_destruct(&a1, NULL);
|
||||
vector_destruct(&a2, NULL);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static size_t hash_func (void *a)
|
||||
{
|
||||
return hash_data(a, sizeof (unsigned long));
|
||||
}
|
||||
|
||||
static int equal_func(void *a, void *b)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
if (b == NULL) {
|
||||
return *pa == -1U;
|
||||
}
|
||||
const unsigned long *pb = b;
|
||||
return *pa == *pb;
|
||||
}
|
||||
|
||||
static void set_func(void *a, void *b)
|
||||
{
|
||||
unsigned long *pa = a;
|
||||
if (b == NULL) {
|
||||
*pa = -1U;
|
||||
} else {
|
||||
const unsigned long *pb = b;
|
||||
*pa = *pb;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct map_element map_element;
|
||||
struct map_element
|
||||
{
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
map dict;
|
||||
map_construct(&dict, sizeof(map_element), set_func);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, set_func, equal_func, NULL);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
map_element *p = map_at(&dict, (map_element[]){{.key = rand_get()}}, hash_func, equal_func);
|
||||
if (p)
|
||||
s += p->value;
|
||||
}
|
||||
g_result = s;
|
||||
map_destruct(&dict, NULL, NULL);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
static int char_equal (void *a, void *b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
if (b == NULL)
|
||||
return strcmp(*pa, "") == 0;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
}
|
||||
|
||||
static size_t char_hash(void *a)
|
||||
{
|
||||
char_array_t *pa = (char_array_t *)a;
|
||||
return hash_string(*pa);
|
||||
}
|
||||
|
||||
static void char_set(void *a, void *b)
|
||||
{
|
||||
char_array_t *pa = (char_array_t *)a;
|
||||
if (b == NULL) {
|
||||
strcpy(*pa, "");
|
||||
} else {
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
strcpy(*pa,*pb);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct big_map_element big_map_element;
|
||||
struct big_map_element
|
||||
{
|
||||
char_array_t key;
|
||||
char_array_t value;
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
map dict;
|
||||
map_construct(&dict, sizeof(big_map_element), char_set);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
big_map_element el;
|
||||
sprintf(el.key, "%u", rand_get());
|
||||
sprintf(el.value, "%u", rand_get());
|
||||
map_insert(&dict, &el, char_hash, char_set, char_equal, NULL);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
big_map_element el;
|
||||
sprintf(el.key, "%u", rand_get());
|
||||
big_map_element *p = map_at(&dict, &el, char_hash, char_equal);
|
||||
if (p)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
map_destruct(&dict, NULL, NULL);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("LIBDYNAMIC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "liblfds711.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct lfds711_queue_bmm_state g_buff;
|
||||
struct lfds711_queue_bmm_state g_final;
|
||||
|
||||
static void final(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(int i = 0; i < n;i++) {
|
||||
void *p;
|
||||
while (0 == lfds711_queue_bmm_dequeue(&g_final, &p, NULL)) { m_thread_yield(); }
|
||||
j = (uintptr_t)p;
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(int i = 0; i < n;i++) {
|
||||
void *p;
|
||||
while (0 == lfds711_queue_bmm_dequeue(&g_buff, &p, NULL)) { m_thread_yield(); }
|
||||
j = (uintptr_t) p;
|
||||
s += j;
|
||||
}
|
||||
while (0 == lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL)) { m_thread_yield(); }
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (0 == lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL)) { m_thread_yield(); }
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
rand_get();
|
||||
// Init
|
||||
const size_t size = m_core_roundpow2(64*cpu_count);
|
||||
struct lfds711_queue_bmm_element g_buff_elements[size];
|
||||
struct lfds711_queue_bmm_element g_final_elements[size];
|
||||
lfds711_queue_bmm_init_valid_on_current_logical_core(&g_buff, g_buff_elements,
|
||||
size, NULL);
|
||||
lfds711_queue_bmm_init_valid_on_current_logical_core(&g_final, g_final_elements,
|
||||
size, NULL);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
lfds711_queue_bmm_cleanup(&g_final, NULL);
|
||||
lfds711_queue_bmm_cleanup(&g_buff, NULL);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("LIBLFDS", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "svector.h"
|
||||
#include "smap.h"
|
||||
#include "smset.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
srt_vector *a1 = sv_alloc ( sizeof (unsigned int), 16, NULL);
|
||||
srt_vector *a2 = sv_alloc ( sizeof (unsigned int), 16, NULL);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int value = rand_get();
|
||||
sv_push(&a1, &value );
|
||||
value = rand_get();
|
||||
sv_push(&a2, &value );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *(unsigned int*)sv_at(a1, i ) * *(unsigned int *)sv_at(a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
sv_free(&a1);
|
||||
sv_free(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
/* There is no 'unsigned long' key in smset */
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static int cmp_float (const void *a, const void *b)
|
||||
{
|
||||
const float *pa=a;
|
||||
const float *pb = b;
|
||||
return (*pa < *pb) ? -1 : *pa > *pb;
|
||||
}
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
srt_vector *a1 = sv_alloc(sizeof(float), 16, cmp_float);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int value = rand_get();
|
||||
sv_push(&a1, &value );
|
||||
}
|
||||
sv_sort(a1);
|
||||
g_result = *(float*)sv_at(a1, 0);
|
||||
sv_free(&a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 50, "sort", 10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("LIBSRT", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
-1086
File diff suppressed because it is too large.
Load diff
@@ -1,99 +0,0 @@
|
||||
#define NDEBUG
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nedtrie.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct foo_s {
|
||||
NEDTRIE_ENTRY(foo_s) link;
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
} foo_t;
|
||||
typedef struct foo_tree_s foo_tree_t;
|
||||
NEDTRIE_HEAD(foo_tree_s, foo_s);
|
||||
|
||||
static inline size_t fookeyfunct(const foo_t *r)
|
||||
{
|
||||
return r->key;
|
||||
}
|
||||
|
||||
NEDTRIE_GENERATE(static, foo_tree_s, foo_s, link, fookeyfunct, NEDTRIE_NOBBLEZEROS(foo_tree_s))
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
foo_tree_t footree;
|
||||
NEDTRIE_INIT(&footree);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
unsigned long val = rand_get();
|
||||
foo_t * p = malloc(sizeof(foo_t));
|
||||
if (p == NULL) abort();
|
||||
p->key = key;
|
||||
p->value = val;
|
||||
NEDTRIE_INSERT(foo_tree_s, &footree, p);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
foo_t key = { .key = rand_get() };
|
||||
foo_t *p = NEDTRIE_FIND(foo_tree_s, &footree, &key);
|
||||
if (p)
|
||||
s += p->value;
|
||||
}
|
||||
g_result = s;
|
||||
foo_t *r, *next;
|
||||
NEDTRIE_FOREACH_SAFE(r, foo_tree_s, &footree, next) {
|
||||
// Remove is needed, otherwise it segfault
|
||||
NEDTRIE_REMOVE(foo_tree_s, &footree, r);
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_noalloc(size_t n)
|
||||
{
|
||||
foo_tree_t footree;
|
||||
NEDTRIE_INIT(&footree);
|
||||
|
||||
foo_t *tab = malloc(n*sizeof(foo_t));
|
||||
if (tab == NULL) abort();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
unsigned long val = rand_get();
|
||||
tab[i].key = key;
|
||||
tab[i].value = val;
|
||||
NEDTRIE_INSERT(foo_tree_s, &footree, &tab[i]);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
foo_t key = { .key = rand_get() };
|
||||
foo_t *p = NEDTRIE_FIND(foo_tree_s, &footree, &key);
|
||||
if (p)
|
||||
s += p->value;
|
||||
}
|
||||
g_result = s;
|
||||
free(tab);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 45, "dict(noalloc)", 1000000, 0, test_dict_noalloc, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("NEDTRIES", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Inspired by: http://stackoverflow.com/questions/3664272/is-stdvector-so-much-slower-than-plain-arrays/3664349#3664349
|
||||
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-list.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-mempool.h"
|
||||
#include "m-string.h"
|
||||
|
||||
unsigned long g_result;
|
||||
|
||||
static unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
struct rusage rus;
|
||||
getrusage (0, &rus);
|
||||
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
|
||||
}
|
||||
|
||||
struct Pixel
|
||||
{
|
||||
unsigned char r, g, b;
|
||||
};
|
||||
|
||||
ARRAY_DEF(array_pixel, struct Pixel, (INIT(M_NOTHING_DEFAULT), INIT_SET(M_MEMCPY_DEFAULT) ))
|
||||
|
||||
void UseMlib(void)
|
||||
{
|
||||
unsigned long long t1 = cputime();
|
||||
|
||||
for(int i = 0; i < 1000; ++i)
|
||||
{
|
||||
int dimension = 999;
|
||||
|
||||
array_pixel_t pixels;
|
||||
array_pixel_init(pixels);
|
||||
array_pixel_reserve(pixels, dimension * dimension);
|
||||
|
||||
for(int i = 0; i < dimension * dimension; ++i)
|
||||
{
|
||||
const struct Pixel p = { 255, 0, 0};
|
||||
array_pixel_push_back (pixels, p);
|
||||
}
|
||||
|
||||
array_pixel_clear(pixels);
|
||||
}
|
||||
unsigned long long t2 = cputime();
|
||||
printf ("M*Lib Array: %Lu us\n", (t2-t1)/1000);
|
||||
}
|
||||
|
||||
void UseNative(void)
|
||||
{
|
||||
unsigned long long t1 = cputime();
|
||||
|
||||
for(int i = 0; i < 1000; ++i)
|
||||
{
|
||||
int dimension = 999;
|
||||
|
||||
struct Pixel * pixels = malloc(sizeof(struct Pixel) * dimension * dimension);
|
||||
|
||||
for(int i = 0 ; i < dimension * dimension; ++i)
|
||||
{
|
||||
pixels[i].r = 255;
|
||||
pixels[i].g = 0;
|
||||
pixels[i].b = 0;
|
||||
}
|
||||
|
||||
free(pixels);
|
||||
}
|
||||
unsigned long long t2 = cputime();
|
||||
printf ("Native Array: %Lu us\n", (t2-t1)/1000);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UseNative();
|
||||
UseMlib();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POTTERY_VECTOR_PREFIX vec_uint
|
||||
#define POTTERY_VECTOR_VALUE_TYPE unsigned int
|
||||
#define POTTERY_VECTOR_LIFECYCLE_BY_VALUE 1
|
||||
#include "pottery/vector/pottery_vector_static.t.h"
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
vec_uint_t a1, a2;
|
||||
|
||||
vec_uint_init(&a1);
|
||||
vec_uint_init(&a2);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
vec_uint_insert_last(&a1, rand_get() );
|
||||
vec_uint_insert_last(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *vec_uint_at(&a1, i ) * *vec_uint_at(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
vec_uint_destroy(&a1);
|
||||
vec_uint_destroy(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct foo_s {
|
||||
struct foo_s* next;
|
||||
struct foo_s* previous;
|
||||
int data;
|
||||
} foo_t;
|
||||
|
||||
#define POTTERY_DOUBLY_LINKED_LIST_PREFIX foo_list
|
||||
#define POTTERY_DOUBLY_LINKED_LIST_VALUE_TYPE foo_t
|
||||
#include "pottery/doubly_linked_list/pottery_doubly_linked_list_static.t.h"
|
||||
|
||||
static void test_list(size_t n)
|
||||
{
|
||||
foo_list_t a1, a2;
|
||||
foo_list_init(&a1);
|
||||
foo_list_init(&a2);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
foo_t *tmp = malloc(sizeof (foo_t));
|
||||
if (tmp == NULL) abort();
|
||||
tmp->data = rand_get();
|
||||
foo_list_link_last(&a1, tmp);
|
||||
|
||||
tmp = malloc(sizeof (foo_t));
|
||||
if (tmp == NULL) abort();
|
||||
tmp->data = rand_get();
|
||||
foo_list_link_last(&a2, tmp);
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(foo_t *it1 = foo_list_first(&a1), *it2 = foo_list_first(&a2) ;
|
||||
foo_list_entry_exists(&a1, it1) ;
|
||||
it1 = foo_list_next(&a1, it1), it2 = foo_list_next(&a2, it2)) {
|
||||
s += it1->data * it2->data;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
for(foo_t *it1 = foo_list_first(&a1);
|
||||
foo_list_entry_exists(&a1, it1) ;
|
||||
) {
|
||||
foo_t *tmp = it1;
|
||||
it1 = foo_list_next(&a1, it1);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
for(foo_t *it1 = foo_list_first(&a2);
|
||||
foo_list_entry_exists(&a2, it1) ;
|
||||
) {
|
||||
foo_t *tmp = it1;
|
||||
it1 = foo_list_next(&a2, it1);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// ERROR: it returns the wrong result.
|
||||
#define POTTERY_TREE_MAP_PREFIX set_ulong
|
||||
#define POTTERY_TREE_MAP_VALUE_TYPE unsigned long
|
||||
#define POTTERY_TREE_MAP_LIFECYCLE_MOVE_BY_VALUE 1
|
||||
#define POTTERY_TREE_MAP_COMPARE_THREE_WAY(a,b) ((a) < (b) ? -1 : (a) > (b))
|
||||
#define POTTERY_TREE_MAP_LIFECYCLE_DESTROY(a) (void) a
|
||||
#include "pottery/tree_map/pottery_tree_map_static.t.h"
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
set_ulong_t tree;
|
||||
set_ulong_init(&tree);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
set_ulong_insert(&tree, rand_get() );
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (set_ulong_find(&tree, &j ) != NULL)
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
set_ulong_destroy(&tree);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#include "m-core.h" // For M_HASH_DEFAULT
|
||||
typedef struct bar_s {
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
} bar_t;
|
||||
|
||||
#define POTTERY_OPEN_HASH_MAP_PREFIX map_ulong
|
||||
|
||||
#define POTTERY_OPEN_HASH_MAP_VALUE_TYPE bar_t
|
||||
#define POTTERY_OPEN_HASH_MAP_KEY_TYPE unsigned long
|
||||
#define POTTERY_OPEN_HASH_MAP_REF_KEY(v) v->key
|
||||
#define POTTERY_OPEN_HASH_MAP_KEY_HASH(x) M_HASH_DEFAULT(x)
|
||||
#define POTTERY_OPEN_HASH_MAP_KEY_EQUAL(x, y) x == y
|
||||
#define POTTERY_OPEN_HASH_MAP_LIFECYCLE_MOVE(p,q) memcpy(p, q, sizeof (bar_t))
|
||||
|
||||
#include "pottery/open_hash_map/pottery_open_hash_map_static.t.h"
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
map_ulong_t dict;
|
||||
map_ulong_init(&dict);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
bar_t * kyle = pottery_null;
|
||||
map_ulong_emplace_key(&dict, key, &kyle, pottery_null);
|
||||
kyle->key = key;
|
||||
kyle->value = value;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
bar_t *kyle = map_ulong_find(&dict, rand_get());
|
||||
if (kyle != NULL)
|
||||
s += kyle->value;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
map_ulong_destroy(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
|
||||
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
|
||||
static inline bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a)==0; }
|
||||
static inline void char_clear (char_array_t *a) { (void)a; }
|
||||
static inline size_t char_hash(const char_array_t *a) {
|
||||
size_t hash = 0;
|
||||
const char *s = a->a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
typedef struct bars_s {
|
||||
char_array_t key;
|
||||
char_array_t value;
|
||||
} bars_t;
|
||||
|
||||
#define POTTERY_OPEN_HASH_MAP_PREFIX map_achar
|
||||
|
||||
#define POTTERY_OPEN_HASH_MAP_VALUE_TYPE bars_t
|
||||
#define POTTERY_OPEN_HASH_MAP_KEY_TYPE char_array_t
|
||||
#define POTTERY_OPEN_HASH_MAP_REF_KEY(v) v->key
|
||||
#define POTTERY_OPEN_HASH_MAP_KEY_HASH(x) char_hash(&x)
|
||||
#define POTTERY_OPEN_HASH_MAP_KEY_EQUAL(x, y) char_equal_p(x, y)
|
||||
#define POTTERY_OPEN_HASH_MAP_LIFECYCLE_MOVE(p,q) memcpy(p, q, sizeof (bars_t))
|
||||
|
||||
#include "pottery/open_hash_map/pottery_open_hash_map_static.t.h"
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
map_achar_t dict;
|
||||
map_achar_init(&dict);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
bars_t * kyle = pottery_null;
|
||||
map_achar_emplace_key(&dict, s1, &kyle, pottery_null);
|
||||
char_set(&kyle->key, s1);
|
||||
char_set(&kyle->value, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
bars_t *kyle = map_achar_find(&dict, s1);
|
||||
if (kyle != NULL)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
map_achar_destroy(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POTTERY_VECTOR_PREFIX vec_float
|
||||
#define POTTERY_VECTOR_VALUE_TYPE float
|
||||
#define POTTERY_VECTOR_LIFECYCLE_BY_VALUE 1
|
||||
#include "pottery/vector/pottery_vector_static.t.h"
|
||||
|
||||
#define POTTERY_INTRO_SORT_PREFIX sort_float
|
||||
#define POTTERY_INTRO_SORT_VALUE_TYPE float
|
||||
#define POTTERY_INTRO_SORT_LIFECYCLE_MOVE_BY_VALUE 1
|
||||
#define POTTERY_INTRO_SORT_COMPARE_THREE_WAY(x, y) ( *x < *y ? -1 : *x > *y)
|
||||
#include "pottery/intro_sort/pottery_intro_sort_static.t.h"
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
vec_float_t a1;
|
||||
vec_float_init(&a1);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
vec_float_insert_last(&a1, rand_get() );
|
||||
}
|
||||
sort_float(vec_float_at(&a1, 0), n);
|
||||
g_result = *vec_float_at(&a1, 0);
|
||||
vec_float_destroy(&a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("POTTERY", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
#define NDEBUG
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "qlibc/qlibc.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
qvector_t *a1 = qvector(16, sizeof(unsigned int), QVECTOR_RESIZE_DOUBLE);
|
||||
if (!a1) abort();
|
||||
qvector_t *a2 = qvector(16, sizeof(unsigned int), QVECTOR_RESIZE_DOUBLE);
|
||||
if (!a2) abort();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int v = rand_get();
|
||||
bool success = a1->addlast(a1, &v);
|
||||
if (!success) abort();
|
||||
v = rand_get();
|
||||
success = a2->addlast(a2, &v);
|
||||
if (!success) abort();
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *(unsigned int*)a1->getat(a1, i, false) * *(unsigned int *) a2->getat(a2, i, false);
|
||||
}
|
||||
g_result = s;
|
||||
a1->free(a1);
|
||||
a2->free(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
qlist_t *a1 = qlist(0);
|
||||
qlist_t *a2 = qlist(0);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int u = rand_get(), v = rand_get();
|
||||
a1->addlast(a1, &u, sizeof(unsigned int));
|
||||
a2->addlast(a2, &v, sizeof(unsigned int));
|
||||
}
|
||||
unsigned int s = 0;
|
||||
qlist_obj_t it1, it2;
|
||||
memset(&it1, 0, sizeof (it1));
|
||||
memset(&it2, 0, sizeof (it2));
|
||||
while (a1->getnext(a1, &it1, false) == true
|
||||
&& a2->getnext(a2, &it2, false) == true) {
|
||||
s += *(unsigned int*)it1.data * *(unsigned int*)it2.data;
|
||||
}
|
||||
g_result = s;
|
||||
a1->free(a1);
|
||||
a2->free(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static int compare(const void *key1, size_t size1,
|
||||
const void *key2, size_t size2)
|
||||
{
|
||||
assert(size1 == sizeof(unsigned long));
|
||||
assert(size2 == sizeof(unsigned long));
|
||||
const unsigned long *pa = key1;
|
||||
const unsigned long *pb = key2;
|
||||
return (*pa < *pb) ? -1 : (*pa > *pb);
|
||||
}
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
qtreetbl_t *tree = qtreetbl(0);
|
||||
tree->set_compare(tree, compare);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long v = rand_get();
|
||||
tree->put_by_obj(tree,
|
||||
&v, sizeof(unsigned long), /* key */
|
||||
&v, sizeof(unsigned long) /* value */ );
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long v = rand_get();
|
||||
void *obj = tree->get_by_obj(tree, (const char*)&v, sizeof(unsigned long), NULL, 0);
|
||||
if (obj)
|
||||
s += *(unsigned long*)obj;
|
||||
}
|
||||
g_result = s;
|
||||
tree->free(tree);
|
||||
}
|
||||
|
||||
/* qlibc hash functions only accept a string as the key */
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("QLIBC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <QLinkedList>
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_list(size_t n)
|
||||
{
|
||||
QLinkedList<unsigned int> a1, a2;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
a2.push_back(rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for (QLinkedList<unsigned int>::const_iterator ci1 = a1.begin(), ci2 = a2.begin (); ci1 != a1.end(); ++ci1, ++ci2) {
|
||||
s += *ci1 * *ci2;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
QVector<unsigned int> a1, a2;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
a2.push_back(rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += a1[i] * a2[i];
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
/* NOTE: QSet doesn't perform full ordering. I can't find any container
|
||||
which is a set with total ordering within Qt. So we have to fake it. */
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
QMap<unsigned long, QHashDummyValue> tree;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
tree[rand_get()] = QHashDummyValue();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
QMap<unsigned long, QHashDummyValue>::iterator it = tree.find(rand_get());
|
||||
if (it != tree.end())
|
||||
s += it.key();
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict1(size_t n)
|
||||
{
|
||||
QMap<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
QMap<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it.value();
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
QHash<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
QHash<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it.value();
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
uint qHash(const char_array_s &k)
|
||||
{
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
QHash<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
QHash<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
QVector<float> a1;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
}
|
||||
qSort(a1.begin(), a1.end());
|
||||
g_result = a1[0];
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("QT", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
#define NDEBUG
|
||||
#define RAPIDJSON_HAS_STDSTRING 1
|
||||
|
||||
#include "rapidjson/reader.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
#include "rapidjson/encodedstream.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
vector<string> vstring;
|
||||
vector<string> vstring2;
|
||||
|
||||
static void
|
||||
bench_vector_string_init(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
char buff[100];
|
||||
snprintf(buff, sizeof(buff), "%zu", i);
|
||||
std::string s(buff);
|
||||
vstring.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_init_big(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
char buff[100];
|
||||
snprintf(buff, sizeof(buff), "%zu%zu%zu%zu%zu%zu", i, i, i, i, i, i);
|
||||
std::string s(buff);
|
||||
vstring.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_json_run(size_t n)
|
||||
{
|
||||
if (vstring.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.json", "wt");
|
||||
if (!f) abort();
|
||||
{
|
||||
// Prepare writer and output stream.
|
||||
char writeBuffer[65536];
|
||||
typedef rapidjson::AutoUTFOutputStream<unsigned, rapidjson::FileWriteStream> OutputStream;
|
||||
rapidjson::FileWriteStream os(f, writeBuffer, sizeof(writeBuffer));
|
||||
OutputStream eos(os, rapidjson::kUTF8, false);
|
||||
rapidjson::PrettyWriter<OutputStream, rapidjson::UTF8<>, rapidjson::AutoUTF<unsigned> > writer(eos);
|
||||
|
||||
writer.StartArray();
|
||||
for(size_t i = 0; i < n; i++){
|
||||
writer.String(vstring[i]);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.json", "rt");
|
||||
if (!f) abort();
|
||||
{
|
||||
char readBuffer[65536];
|
||||
rapidjson::FileReadStream is(f, readBuffer, sizeof(readBuffer));
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(is);
|
||||
for(size_t i = 0; i < d.Size(); i++) {
|
||||
rapidjson::Value& s = d[i];
|
||||
vstring2.push_back(s.GetString());
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vstring2.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_clear(void)
|
||||
{
|
||||
if (vstring != vstring2) {
|
||||
printf("Array are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
vector<unsigned long> vulong;
|
||||
vector<unsigned long> vulong2;
|
||||
|
||||
static void
|
||||
bench_vector_ulong_init(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
vulong.push_back(i*i);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_json_run(size_t n)
|
||||
{
|
||||
if (vulong.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.json", "wt");
|
||||
if (!f) abort();
|
||||
{
|
||||
// Prepare writer and output stream.
|
||||
char writeBuffer[65536];
|
||||
typedef rapidjson::AutoUTFOutputStream<unsigned, rapidjson::FileWriteStream> OutputStream;
|
||||
rapidjson::FileWriteStream os(f, writeBuffer, sizeof(writeBuffer));
|
||||
OutputStream eos(os, rapidjson::kUTF8, false);
|
||||
rapidjson::PrettyWriter<OutputStream, rapidjson::UTF8<>, rapidjson::AutoUTF<unsigned> > writer(eos);
|
||||
|
||||
writer.StartArray();
|
||||
for(size_t i = 0; i < n; i++){
|
||||
writer.Int64(vulong[i]);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.json", "rt");
|
||||
if (!f) abort();
|
||||
{
|
||||
char readBuffer[65536];
|
||||
rapidjson::FileReadStream is(f, readBuffer, sizeof(readBuffer));
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(is);
|
||||
for(size_t i = 0; i < d.Size(); i++) {
|
||||
rapidjson::Value& s = d[i];
|
||||
vulong2.push_back(s.GetInt64());
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vulong2.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_clear(void)
|
||||
{
|
||||
if (vulong != vulong2) {
|
||||
printf("Array are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{110, "serial-json STR", 10000000, bench_vector_string_init, bench_vector_string_json_run, bench_vector_string_clear},
|
||||
{111, "serial-json STR.big", 10000000, bench_vector_string_init_big, bench_vector_string_json_run, bench_vector_string_clear},
|
||||
{112, "serial-json INT", 10000000, bench_vector_ulong_init, bench_vector_ulong_json_run, bench_vector_ulong_clear}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RAPIDJSON", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rigtorp/HashMap.h"
|
||||
|
||||
using namespace rigtorp;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Hash for using unsigned long as lookup key
|
||||
struct UL_Hash {
|
||||
size_t operator()(const unsigned long v) { return v * 7; }
|
||||
};
|
||||
|
||||
// Equal comparison for using unsigned long as lookup key
|
||||
struct UL_Equal {
|
||||
bool operator()(const unsigned long lhs, const unsigned long rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal> dict(16, -1UL);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
struct BIG_Hash {
|
||||
size_t operator()(const char_array_s v) { return hash<char_array_s>()(v); }
|
||||
};
|
||||
|
||||
struct BIG_Equal {
|
||||
bool operator()(const char_array_s lhs, const char_array_s rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
char_array_s empty;
|
||||
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal> dict(16, empty);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct STR_Hash {
|
||||
size_t operator()(const string v) { return hash<string>()(v); }
|
||||
};
|
||||
|
||||
struct STR_Equal {
|
||||
bool operator()(const string lhs, const string rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
string empty;
|
||||
HashMap<string, string, STR_Hash, STR_Equal> dict(16, empty);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
HashMap<string, string, STR_Hash, STR_Equal>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RIGTORP-HASHMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <rigtorp/MPMCQueue.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
rigtorp::MPMCQueue<unsigned int> g_buff(4*64);
|
||||
rigtorp::MPMCQueue<unsigned long long> g_final(4*64);
|
||||
|
||||
static void final(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*) arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
g_final.pop(j);
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
g_buff.pop(j);
|
||||
s += j;
|
||||
}
|
||||
g_final.push(s);
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
g_buff.push(r);
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
rand_get();
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RIGTORP-MPMC-QUEUE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sparsepp/spp.h>
|
||||
|
||||
using spp::sparse_hash_map;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
sparse_hash_map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
sparse_hash_map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
sparse_hash_map<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sparse_hash_map<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
sparse_hash_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
sparse_hash_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("SPARSEPP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define i_val unsigned int
|
||||
#define i_tag uint
|
||||
#include <stc/cvec.h>
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
cvec_uint a1 = cvec_uint_init(), a2 = cvec_uint_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
cvec_uint_push_back(&a1, rand_get() );
|
||||
cvec_uint_push_back(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *cvec_uint_at(&a1, i ) * *cvec_uint_at(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cvec_uint_drop(&a1);
|
||||
cvec_uint_drop(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
#define i_val unsigned int
|
||||
#define i_tag uint
|
||||
#include <stc/clist.h>
|
||||
|
||||
static void test_list(size_t n)
|
||||
{
|
||||
clist_uint a1 = clist_uint_init(), a2 = clist_uint_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
clist_uint_push_front(&a1, rand_get() );
|
||||
clist_uint_push_front(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(clist_uint_iter it1 = clist_uint_begin(&a1), it2 = clist_uint_begin(&a2) ;
|
||||
it1.ref != clist_uint_end(&a1).ref ;
|
||||
clist_uint_next(&it1), clist_uint_next(&it2) ) {
|
||||
s += *it1.ref * *it2.ref;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
clist_uint_drop(&a1);
|
||||
clist_uint_drop(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define i_tag ulong
|
||||
#define i_key unsigned long
|
||||
#include <stc/csset.h>
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
csset_ulong tree = csset_ulong_init();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
csset_ulong_insert(&tree, rand_get() );
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (csset_ulong_get(&tree, j ) != NULL)
|
||||
s+= j;
|
||||
}
|
||||
g_result = s;
|
||||
csset_ulong_drop(&tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define i_tag ulong
|
||||
#define i_key unsigned long
|
||||
#define i_val unsigned long
|
||||
#include <stc/cmap.h>
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
cmap_ulong dict = cmap_ulong_init();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
cmap_ulong_insert_or_assign(&dict, key, value);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const cmap_ulong_value*val = cmap_ulong_get(&dict, rand_get());
|
||||
if (val != NULL)
|
||||
s += val->second;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cmap_ulong_drop(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
static inline void char_init (char_array_t *a) { a->a[0] = 0; }
|
||||
static inline void char_set (char_array_t *a, const char_array_t b) { strcpy(a->a, b.a); }
|
||||
static inline bool char_equal_p (const char_array_t *a, const char_array_t *b) { return strcmp(a->a,b->a)==0; }
|
||||
static inline void char_clear (char_array_t *a) { (void)a; }
|
||||
static inline size_t char_hash(const char_array_t *a) {
|
||||
size_t hash = 0;
|
||||
const char *s = a->a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
#define i_tag achar
|
||||
#define i_key char_array_t
|
||||
#define i_val char_array_t
|
||||
#define i_hash char_hash
|
||||
#define i_eq char_equal_p
|
||||
#include <stc/cmap.h>
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
cmap_achar dict = cmap_achar_init();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
cmap_achar_insert_or_assign(&dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
const cmap_achar_value*val = cmap_achar_get(&dict, s1);
|
||||
if (val != NULL)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
cmap_achar_drop(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define i_val float
|
||||
#define i_tag float
|
||||
#include <stc/cvec.h>
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
cvec_float a1 = cvec_float_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
cvec_float_push_back(&a1, rand_get() );
|
||||
}
|
||||
cvec_float_sort(&a1);
|
||||
g_result = *cvec_float_at(&a1, 0);
|
||||
cvec_float_drop(&a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("STC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_list(size_t n)
|
||||
{
|
||||
list<unsigned int> a1, a2;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
a2.push_back(rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for (list<unsigned int>::const_iterator ci1 = a1.begin(), ci2 = a2.begin (); ci1 != a1.end(); ++ci1, ++ci2) {
|
||||
s += *ci1 * *ci2;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
vector<unsigned int> a1, a2;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
a2.push_back(rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += a1[i] * a2[i];
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_deque(size_t n)
|
||||
{
|
||||
deque<unsigned int> a1, a2;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
a2.push_back(rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(auto it1 = a1.cbegin(), it2 = a2.cbegin();
|
||||
it1 != a1.cend() ;it1++, it2++) {
|
||||
s += *it1 * *it2;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
set<unsigned long> tree;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
tree.insert(rand_get());
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
set<unsigned long>::iterator it = tree.find(rand_get());
|
||||
if (it != tree.end())
|
||||
s += *it;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict1(size_t n)
|
||||
{
|
||||
map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
unordered_map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unordered_map<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
unordered_map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unordered_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
unordered_map<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
unordered_map<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
unordered_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = to_string(rand_get());
|
||||
string s2 = to_string(rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = to_string (rand_get());
|
||||
unordered_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_str2(size_t n)
|
||||
{
|
||||
map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = to_string(rand_get());
|
||||
string s2 = to_string(rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = to_string(rand_get());
|
||||
map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
vector<float> a1;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
}
|
||||
sort(a1.begin(), a1.end());
|
||||
g_result = a1[0];
|
||||
}
|
||||
|
||||
static void test_stable_sort(size_t n)
|
||||
{
|
||||
vector<float> a1;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
}
|
||||
stable_sort(a1.begin(), a1.end());
|
||||
g_result = a1[0];
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 21, "Deque", 100000000, 0, test_deque, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
|
||||
{ 43,"DictStr(u)", 1000000, 0, test_dict_str, 0},
|
||||
{ 44,"DictStr(m)", 1000000, 0, test_dict_str2, 0},
|
||||
{ 46, "dictLinear(u)", 1000000, 0, test_dict2_linear, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("STL", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
/*
|
||||
* M*LIB - String Bench
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#define NDEBUG
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define M_USE_SMALL_NAME 0
|
||||
#include "common.h"
|
||||
|
||||
#include "m-string.h"
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
extern "C" {
|
||||
#include "sds.h"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
#include "bstrlib.h"
|
||||
#include "bstrwrap.h"
|
||||
#include "bstraux.h"
|
||||
#endif
|
||||
|
||||
#if defined(BENCH_CAN_USE_POTTERY)
|
||||
#include "pottery/string/string.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* BENCH
|
||||
* 2000000
|
||||
* 1. Generate N strings of random numbers 32 bits.
|
||||
* 2. Concat all strings in a string usigned a random permutation of N.
|
||||
* 3. Replace "1234" in the string into "WELL" until nothing remains.
|
||||
* 4. Replace "56789" in the string into "DONE" until nothing remains.
|
||||
* Return the final string length.
|
||||
*/
|
||||
|
||||
unsigned *permutation_tab = NULL;
|
||||
|
||||
void random_permutation(unsigned n)
|
||||
{
|
||||
if (permutation_tab) free(permutation_tab);
|
||||
permutation_tab = (unsigned *) malloc(n * sizeof(unsigned));
|
||||
if (permutation_tab == NULL) abort();
|
||||
for(unsigned i = 0; i < n; i++)
|
||||
permutation_tab[i] = i;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
unsigned j = rand_get() % n;
|
||||
unsigned k = rand_get() % n;
|
||||
unsigned l = permutation_tab[j];
|
||||
permutation_tab[j] = permutation_tab[k];
|
||||
permutation_tab[k] = l;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void replace_all_stl(string &str, const char pattern[], const char replace[])
|
||||
{
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find(pattern, pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, strlen(pattern), replace);
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
}
|
||||
|
||||
size_t bench_stl(unsigned n)
|
||||
{
|
||||
vector<string> tab;
|
||||
tab.resize(n);
|
||||
for(unsigned i = 0; i < n; i++)
|
||||
tab[i] = to_string(rand_get());
|
||||
|
||||
string str;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str += tab[permutation_tab[i]];
|
||||
}
|
||||
|
||||
replace_all_stl(str, "1234", "WELL");
|
||||
replace_all_stl(str, "56789", "DONE");
|
||||
|
||||
return str.length();
|
||||
}
|
||||
|
||||
size_t bench_mlib(unsigned n)
|
||||
{
|
||||
m_string_t *tab = (m_string_t*) malloc (n * sizeof (m_string_t));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
m_string_init(tab[i]);
|
||||
m_string_set_ui(tab[i], rand_get());
|
||||
}
|
||||
// P2
|
||||
m_string_t str;
|
||||
m_string_init(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
m_string_cat(str, tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
m_string_replace_all_cstr(str, "1234", "WELL");
|
||||
m_string_replace_all_cstr(str, "56789", "DONE");
|
||||
size_t length = m_string_size(str);
|
||||
|
||||
// Clean
|
||||
m_string_clear(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
m_string_clear(tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
|
||||
static void replace_all_bstrlib(CBString &str, const char pattern[], const char replace[])
|
||||
{
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find(pattern, pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, strlen(pattern), replace);
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
}
|
||||
|
||||
size_t bench_bstrlib(unsigned n)
|
||||
{
|
||||
vector<CBString> tab;
|
||||
tab.resize(n);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
char tmp[10];
|
||||
sprintf(tmp, "%u", rand_get());
|
||||
tab[i] = tmp;
|
||||
}
|
||||
|
||||
CBString str;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str += tab[permutation_tab[i]];
|
||||
}
|
||||
|
||||
replace_all_bstrlib(str, "1234", "WELL");
|
||||
replace_all_bstrlib(str, "56789", "DONE");
|
||||
|
||||
return str.length();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
sds SDS_replace_at(sds str, size_t pos, size_t len, const char str2[])
|
||||
{
|
||||
// simple implementation as replace is not available
|
||||
sds a = sdsnewlen((const void*) str, pos);
|
||||
a = sdscat(a, str2);
|
||||
a = sdscat(a, &str[pos+len]);
|
||||
sdsfree(str);
|
||||
return a;
|
||||
}
|
||||
|
||||
sds replace_all_sds(sds str, const char pattern[], const char replace[])
|
||||
{
|
||||
size_t pos = 0;
|
||||
do {
|
||||
char *p = strstr(&str[pos], pattern);
|
||||
pos = p == NULL ? -1U : (p - str);
|
||||
if (pos != -1U) {
|
||||
str = SDS_replace_at(str, pos, strlen(pattern), replace);
|
||||
}
|
||||
} while (pos != -1U);
|
||||
return str;
|
||||
}
|
||||
|
||||
size_t bench_sds(unsigned n)
|
||||
{
|
||||
sds *tab = (sds*) malloc (n * sizeof (sds));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
tab[i] = sdsfromlonglong(rand_get());
|
||||
}
|
||||
// P2
|
||||
sds str = sdsempty();
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str = sdscat(str, tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
str = replace_all_sds(str, "1234", "WELL");
|
||||
str = replace_all_sds(str, "56789", "DONE");
|
||||
// Clean
|
||||
size_t length = sdslen(str);
|
||||
sdsfree(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
sdsfree(tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BENCH_CAN_USE_POTTERY)
|
||||
|
||||
size_t pottery_find_cstr(string_t *b, size_t index, const char *pattern)
|
||||
{
|
||||
// I cannot find such function in the API
|
||||
const char *s = string_cstr(b);
|
||||
const char *r = strstr(&s[index], pattern);
|
||||
return r == NULL ? SIZE_MAX : r - s;
|
||||
}
|
||||
|
||||
void pottery_replace_all_str(string_t *str, const char *s, const char *d)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
i = pottery_find_cstr(str, i, s);
|
||||
if (i==SIZE_MAX) return;
|
||||
string_remove(str, i, strlen(s));
|
||||
string_insert_cstr(str, i, d);
|
||||
}
|
||||
}
|
||||
|
||||
size_t bench_pottery(unsigned n)
|
||||
{
|
||||
string_t *tab = (string_t*) malloc (n * sizeof (string_t));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_init(&tab[i]);
|
||||
string_append_format(&tab[i], "%u", rand_get());
|
||||
}
|
||||
// P2
|
||||
string_t str;
|
||||
string_init(&str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_append_string(&str, &tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
|
||||
pottery_replace_all_str(&str, "1234", "WELL");
|
||||
pottery_replace_all_str(&str, "56789", "DONE");
|
||||
size_t length = string_length(&str);
|
||||
|
||||
// Clean
|
||||
string_clear(&str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_clear(&tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
unsigned length, n = atoi(argv[1]);
|
||||
unsigned select = atoi(argv[2]);
|
||||
const char *name;
|
||||
random_permutation(n);
|
||||
rand_init();
|
||||
unsigned long long t0 = cputime();
|
||||
|
||||
switch (select) {
|
||||
case 0:
|
||||
length = bench_mlib(n);
|
||||
name = "MLIB";
|
||||
break;
|
||||
case 1:
|
||||
length = bench_stl(n);
|
||||
name = "STL";
|
||||
break;
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
case 2:
|
||||
length = bench_sds(n);
|
||||
name = "SDS";
|
||||
break;
|
||||
#endif
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
case 4:
|
||||
length = bench_bstrlib(n);
|
||||
name = "BSTRLIB";
|
||||
break;
|
||||
#endif
|
||||
#if defined(BENCH_CAN_USE_POTTERY)
|
||||
case 5:
|
||||
length = bench_pottery(n);
|
||||
name = "POTTERY";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
unsigned long long t1 = cputime();
|
||||
printf("%5.5s LENGTH=%u T= %f s\n", name, length, (t1-t0) / 1000000.0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,221 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tommyds/tommy.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
tommy_array a1, a2;
|
||||
|
||||
tommy_array_init(&a1);
|
||||
tommy_array_init(&a2);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int v = rand_get();
|
||||
tommy_array_insert(&a1, (void*)(uintptr_t) v);
|
||||
v = rand_get();
|
||||
tommy_array_insert(&a2, (void*)(uintptr_t) v);
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += (uintptr_t) *tommy_array_ref(&a1, i) * (uintptr_t) *tommy_array_ref(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
tommy_array_done(&a1);
|
||||
tommy_array_done(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct list_node_s {
|
||||
unsigned int value;
|
||||
tommy_node node;
|
||||
};
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
tommy_list a1, a2;
|
||||
|
||||
tommy_list_init(&a1);
|
||||
tommy_list_init(&a2);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
struct list_node_s *obj;
|
||||
obj = malloc(sizeof(struct list_node_s));
|
||||
if (obj == NULL) abort();
|
||||
obj->value = rand_get();
|
||||
tommy_list_insert_tail(&a1, &obj->node, obj);
|
||||
obj = malloc(sizeof(struct list_node_s));
|
||||
if (obj == NULL) abort();
|
||||
obj->value = rand_get();
|
||||
tommy_list_insert_tail(&a2, &obj->node, obj);
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(tommy_node *it1 = tommy_list_head(&a1), *it2 = tommy_list_head(&a2)
|
||||
; it1 != NULL; it1 = it1->next, it2 = it2->next ) {
|
||||
struct list_node_s *obj1 = it1->data, *obj2 = it2->data;
|
||||
s += obj1->value * obj2->value;
|
||||
}
|
||||
g_result = s;
|
||||
tommy_list_foreach(&a1, free);
|
||||
tommy_list_foreach(&a2, free);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct tree_node_s {
|
||||
unsigned long value;
|
||||
tommy_tree_node node;
|
||||
} ;
|
||||
|
||||
static int compare(const void *a, const void *b)
|
||||
{
|
||||
const struct tree_node_s *pa = a;
|
||||
const struct tree_node_s *pb = b;
|
||||
return (pa->value < pb->value) ? -1 : (pa->value > pb->value);
|
||||
}
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
tommy_tree tree;
|
||||
tommy_tree_init(&tree, compare);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
struct tree_node_s *obj = malloc(sizeof(struct tree_node_s));
|
||||
if (obj == NULL) abort();
|
||||
obj->value = rand_get();
|
||||
tommy_tree_insert(&tree, &obj->node, obj);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
struct tree_node_s key = { .value = rand_get() };
|
||||
struct tree_node_s *obj = tommy_tree_search (&tree, &key);
|
||||
if (obj)
|
||||
s += obj->value;
|
||||
}
|
||||
g_result = s;
|
||||
tommy_tree_foreach(&tree, free);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct hash_i_node_s {
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
tommy_node node;
|
||||
};
|
||||
|
||||
static inline size_t hash_func (const unsigned long key)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
static int equal_func(const void* arg, const void *obj_p)
|
||||
{
|
||||
const struct hash_i_node_s *obj = obj_p;
|
||||
const unsigned long *key = arg;
|
||||
return *key != obj->key;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
tommy_hashlin dict;
|
||||
tommy_hashlin_init(&dict);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
struct hash_i_node_s *obj = malloc(sizeof (struct hash_i_node_s));
|
||||
if(!obj) abort();
|
||||
obj->key = rand_get();
|
||||
obj->value = rand_get();
|
||||
tommy_hashlin_insert(&dict, &obj->node, obj, hash_func(obj->key));
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
struct hash_i_node_s *obj = tommy_hashlin_search(&dict, &equal_func,
|
||||
&key,
|
||||
hash_func(key));
|
||||
if (obj)
|
||||
s += obj->value;
|
||||
}
|
||||
g_result = s;
|
||||
tommy_hashlin_done(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
struct hash_b_node_s {
|
||||
char_array_t key;
|
||||
char_array_t value;
|
||||
tommy_node node;
|
||||
};
|
||||
|
||||
static int char_equal (const void *arg, const void *obj_p)
|
||||
{
|
||||
const struct hash_b_node_s *obj = obj_p;
|
||||
const char_array_t *key = (const char_array_t *)arg;
|
||||
return strcmp(*key, (obj->key));
|
||||
}
|
||||
|
||||
static size_t char_hash(const char *s)
|
||||
{
|
||||
size_t hash = 0;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
tommy_hashlin dict;
|
||||
tommy_hashlin_init(&dict);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
struct hash_b_node_s *obj = malloc(sizeof (struct hash_b_node_s));
|
||||
if(!obj) abort();
|
||||
sprintf(obj->key, "%u", rand_get());
|
||||
sprintf(obj->value, "%u", rand_get());
|
||||
/* NOTE: this is not an insert_or_replace func...
|
||||
It will always insert a new node, even if the key already exists...
|
||||
Maybe searching then inserting will be a better representative?
|
||||
*/
|
||||
tommy_hashlin_insert(&dict, &obj->node, obj, char_hash(obj->key));
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
struct hash_b_node_s *obj = tommy_hashlin_search(&dict, &char_equal,
|
||||
&s1,
|
||||
char_hash(s1));
|
||||
if (obj)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
tommy_hashlin_done(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("TommyDS", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
#define NDEBUG
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/uthash.h"
|
||||
#include "include/utarray.h"
|
||||
#include "include/utlist.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static const UT_icd int_icd = {
|
||||
.sz = sizeof (unsigned int),
|
||||
.init = NULL,
|
||||
.dtor = NULL,
|
||||
.copy = NULL
|
||||
};
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
UT_array a1, a2;
|
||||
|
||||
utarray_init(&a1, &int_icd);
|
||||
utarray_init(&a2, &int_icd);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int v1 = rand_get(), v2 = rand_get();
|
||||
utarray_push_back(&a1, &v1);
|
||||
utarray_push_back(&a2, &v2);
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *(unsigned long *) utarray_eltptr(&a1, i ) * *(unsigned long *)utarray_eltptr(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
utarray_done(&a1);
|
||||
utarray_done(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct list_node_s {
|
||||
unsigned int value;
|
||||
struct list_node_s *next;
|
||||
};
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
struct list_node_s *a1 = NULL, *a2 = NULL;
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
struct list_node_s *p = malloc(sizeof(struct list_node_s));
|
||||
if (!p) abort();
|
||||
p->value = rand_get();
|
||||
LL_PREPEND(a1, p);
|
||||
p = malloc(sizeof(struct list_node_s));
|
||||
if (!p) abort();
|
||||
p->value = rand_get();
|
||||
LL_PREPEND(a2, p);
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(struct list_node_s *it1 = a1, *it2 = a2
|
||||
; it1 != NULL
|
||||
; it1 = it1->next, it2 = it2->next) {
|
||||
s += it1->value * it2->value;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
struct list_node_s *el;
|
||||
LL_FOREACH(a1, el) free(el);
|
||||
LL_FOREACH(a2, el) free(el);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct hash_i_node_s {
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
struct hash_i_node_s *dict = NULL;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long k = rand_get();
|
||||
struct hash_i_node_s *s;
|
||||
HASH_FIND(hh, dict, &k, sizeof (unsigned long), s);
|
||||
if (s == NULL) {
|
||||
s = malloc(sizeof(struct hash_i_node_s));
|
||||
if (!s) abort();
|
||||
s->key = k;
|
||||
s->value = rand_get();
|
||||
HASH_ADD(hh, dict, key, sizeof(unsigned long), s);
|
||||
}
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long k = rand_get();
|
||||
struct hash_i_node_s *sp;
|
||||
HASH_FIND(hh, dict, &k, sizeof (unsigned long), sp);
|
||||
if (sp != NULL)
|
||||
s += sp->value;
|
||||
}
|
||||
g_result = s;
|
||||
struct hash_i_node_s *current, *tmp;
|
||||
HASH_ITER(hh, dict, current, tmp) {
|
||||
HASH_DEL(dict,current); /* delete it (users advances to next) */
|
||||
free(current); /* free it */
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
|
||||
struct hash_b_node_s {
|
||||
char_array_t key;
|
||||
char_array_t value;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
struct hash_b_node_s *dict = NULL;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t k;
|
||||
sprintf(k.a, "%u", rand_get());
|
||||
struct hash_b_node_s *s;
|
||||
HASH_FIND(hh, dict, &k, sizeof (char_array_t), s);
|
||||
if (s == NULL) {
|
||||
s = malloc(sizeof(struct hash_b_node_s));
|
||||
if (!s) abort();
|
||||
memcpy(s->key.a, k.a, sizeof(s->key));
|
||||
sprintf(s->value.a, "%u", rand_get());
|
||||
HASH_ADD(hh, dict, key, sizeof(char_array_t), s);
|
||||
}
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t k;
|
||||
sprintf(k.a, "%u", rand_get());
|
||||
struct hash_b_node_s *sp;
|
||||
HASH_FIND(hh, dict, &k, sizeof (char_array_t), sp);
|
||||
if (sp != NULL)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
struct hash_b_node_s *current, *tmp;
|
||||
HASH_ITER(hh, dict, current, tmp) {
|
||||
HASH_DEL(dict,current); /* delete it (users advances to next) */
|
||||
free(current); /* free it */
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static const UT_icd float_icd = {
|
||||
.sz = sizeof (float),
|
||||
.init = NULL,
|
||||
.dtor = NULL,
|
||||
.copy = NULL
|
||||
};
|
||||
|
||||
static int cmp(const void *a1, const void *a2)
|
||||
{
|
||||
const float *f1 = a1;
|
||||
const float *f2 = a2;
|
||||
return (*f1 < *f2) ? -1 : (*f1 > *f2);
|
||||
}
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
UT_array a1;
|
||||
|
||||
utarray_init(&a1, &float_icd);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
float v1 = rand_get();
|
||||
utarray_push_back(&a1, &v1);
|
||||
}
|
||||
utarray_sort(&a1, cmp);
|
||||
g_result = *(float *)utarray_eltptr(&a1, 0);
|
||||
utarray_done(&a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "sort", 10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("UT-HASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "m-core.h"
|
||||
#include "common.h"
|
||||
|
||||
#define NAME umap_ulong
|
||||
#define KEY_TY unsigned long
|
||||
#define VAL_TY unsigned long
|
||||
#include "verstable.h"
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
umap_ulong dict;
|
||||
umap_ulong_init(&dict);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
umap_ulong_insert(&dict, key, value);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
umap_ulong_itr it = umap_ulong_get(&dict, rand_get());
|
||||
if (!umap_ulong_is_end(it) )
|
||||
s += it.data->val;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
umap_ulong_cleanup(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifndef CHAR_ARRAY_SIZE
|
||||
#define CHAR_ARRAY_SIZE 256
|
||||
#endif
|
||||
typedef char char_array_t[CHAR_ARRAY_SIZE];
|
||||
typedef struct { char_array_t a; } char_encap;// Cannot use char_array_t directly
|
||||
static bool char_equal_p (const char_encap a, const char_encap b) { return strcmp(a.a,b.a)==0; }
|
||||
static size_t char_hash(const char_encap a) { return m_core_hash (a.a, strlen(a.a)); }
|
||||
|
||||
#define NAME umap_char_array
|
||||
#define KEY_TY char_encap
|
||||
#define VAL_TY char_encap
|
||||
#define HASH_FN char_hash
|
||||
#define CMPR_FN char_equal_p
|
||||
#include "verstable.h"
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
umap_char_array dict;
|
||||
umap_char_array_init(&dict);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_encap s1, s2;
|
||||
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
umap_char_array_insert(&dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_encap s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
umap_char_array_itr it = umap_char_array_get(&dict, s1);
|
||||
if (!umap_char_array_is_end(it) )
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
umap_char_array_cleanup(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CTL", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "xxhash.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static unsigned long *g_p;
|
||||
|
||||
static void test_hash_prepare(size_t n)
|
||||
{
|
||||
g_p = malloc (n * sizeof(unsigned long));
|
||||
if (g_p == NULL) abort();
|
||||
for(size_t i = 0; i < n ; i++)
|
||||
g_p[i] = ((rand_get()*RAND_MAX + rand_get())*RAND_MAX+rand_get())*RAND_MAX + rand_get();
|
||||
}
|
||||
|
||||
|
||||
static void test_hash_final(void)
|
||||
{
|
||||
free(g_p);
|
||||
}
|
||||
|
||||
static void test_hash(size_t n)
|
||||
{
|
||||
g_result = XXH64(g_p, sizeof(unsigned long)*n, 0);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 70,"XXHASH64", 100000000, test_hash_prepare, test_hash, test_hash_final}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("XXHASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
-306
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
|
||||
// Maximum supported value for repeat argument
|
||||
#define MAX_REPEAT 10000
|
||||
|
||||
// The global result
|
||||
unsigned long g_result;
|
||||
|
||||
// The rand value
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
M_THREAD_ATTR
|
||||
#endif
|
||||
unsigned int randValue = 0;
|
||||
|
||||
void compiler_barrier(void *p)
|
||||
{
|
||||
// Do nothing, but let the compiler thinks that we did.
|
||||
(void) p;
|
||||
// Can be used for FLTO building
|
||||
// asm volatile("": "+r" (p) : :"memory");
|
||||
}
|
||||
|
||||
struct parse_opt_s {
|
||||
int test_function;
|
||||
double from, to, step, grow;
|
||||
double within;
|
||||
unsigned repeat;
|
||||
bool graph, best, best_within, average, quiet;
|
||||
};
|
||||
|
||||
static void
|
||||
parse_config(struct parse_opt_s *opt, int argc, const char *argv[])
|
||||
{
|
||||
char *end;
|
||||
|
||||
opt->test_function = 0;
|
||||
opt->from = 0.0;
|
||||
opt->to = 0.0;
|
||||
opt->step = 0.0;
|
||||
opt->grow = 1.1;
|
||||
opt->graph = false;
|
||||
opt->best = false;
|
||||
opt->best_within = false;
|
||||
opt->average = false;
|
||||
opt->quiet = false;
|
||||
opt->repeat = 1;
|
||||
opt->within = .05;
|
||||
|
||||
for(int i = 1; i < argc ; i++)
|
||||
{
|
||||
if (argv[i][0] == '-') {
|
||||
if (strcmp(argv[i], "--from") == 0){
|
||||
i++;
|
||||
opt->from = strtod(argv[i], &end);
|
||||
opt->to = opt->from;
|
||||
} else if (strcmp(argv[i], "--to") == 0) {
|
||||
i++;
|
||||
opt->to = strtod(argv[i], &end);
|
||||
opt->grow = 1.1;
|
||||
} else if (strcmp(argv[i], "--step") == 0) {
|
||||
i++;
|
||||
opt->step = strtod(argv[i], &end);
|
||||
opt->grow = 0;
|
||||
} else if (strcmp(argv[i], "--grow") == 0) {
|
||||
i++;
|
||||
opt->grow = strtod(argv[i], &end);
|
||||
opt->step = 0;
|
||||
} else if (strcmp(argv[i], "--repeat") == 0) {
|
||||
i++;
|
||||
opt->repeat = strtol(argv[i], &end, 10);
|
||||
opt->repeat = (opt->repeat > MAX_REPEAT) ? MAX_REPEAT : opt->repeat;
|
||||
} else if (strcmp(argv[i], "--graph") == 0) {
|
||||
opt->graph = true;
|
||||
} else if (strcmp(argv[i], "--best") == 0) {
|
||||
opt->best = true;
|
||||
opt->average = false;
|
||||
opt->best_within = false;
|
||||
} else if (strcmp(argv[i], "--best-within") == 0) {
|
||||
opt->best_within = true;
|
||||
opt->best = false;
|
||||
opt->average = false;
|
||||
i++;
|
||||
opt->within = strtod(argv[i], &end) / 100.0;
|
||||
} else if (strcmp(argv[i], "--average") == 0) {
|
||||
opt->average = true;
|
||||
opt->best = false;
|
||||
opt->best_within = false;
|
||||
} else if (strcmp(argv[i], "--quiet") == 0) {
|
||||
opt->quiet = true;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Option unkown: %s.\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
if (*end != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot parse %s. Expected float\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
} else {
|
||||
opt->test_function = strtol (argv[i], &end, 10);
|
||||
if (*end != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot parse %s. Expected function number\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
select_config(int func, size_t n, const config_func_t functions[])
|
||||
{
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
if (functions[i].num == func)
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "ERROR: Function number %d not found. Available functions are:\n", func);
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
fprintf(stderr, " %d: %s\n", functions[i].num, functions[i].funcname);
|
||||
}
|
||||
fprintf(stderr,
|
||||
"USAGE: FUNC_NUMBER [--from number --to number (--grow number | --step number)] [--graph]\n"
|
||||
"[--repeat number] [--best|--average|--best-within] [--quiet]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Utility function for qsort
|
||||
static int cmp_double(const void *pa, const void *pb)
|
||||
{
|
||||
const double *a = (const double *) pa, *b = (const double *)pb;
|
||||
return (*a < *b) ? -1 : *a > *b;
|
||||
}
|
||||
|
||||
/* Get the longest sequence of values within 'f'% */
|
||||
static int
|
||||
get_sequence(int i, int n, double tab[], double f)
|
||||
{
|
||||
int j;
|
||||
for(j = i + 1; j < n; j++) {
|
||||
if (tab[j] > f * tab[i])
|
||||
break;
|
||||
}
|
||||
return j-i;
|
||||
}
|
||||
|
||||
/* Return the minimum of the longest sequence of values with 'f'% */
|
||||
static double
|
||||
get_best_within(int n, double tab[], double f)
|
||||
{
|
||||
qsort(tab, n, sizeof(double), cmp_double);
|
||||
int best_i = 0;
|
||||
int best_seq = get_sequence(0, n, tab, f);
|
||||
for(int i = 1; i < n && (n-i) > best_seq ; i++) {
|
||||
int seq = get_sequence(i, n, tab, f);
|
||||
if (seq > best_seq) {
|
||||
best_i = i;
|
||||
best_seq = seq;
|
||||
}
|
||||
}
|
||||
return tab[best_i];
|
||||
}
|
||||
|
||||
// Integer sqrt function (to avoid depending on libm)
|
||||
static unsigned integer_sqrt(unsigned n)
|
||||
{
|
||||
// Find greatest shift.
|
||||
int shift = 2;
|
||||
unsigned nShifted = n >> shift;
|
||||
// We check for nShifted being n, since some implementations
|
||||
// perform shift operations modulo the word size.
|
||||
while (nShifted != 0 && nShifted != n){
|
||||
shift += 2;
|
||||
nShifted = n >> shift;
|
||||
}
|
||||
shift -= 2;
|
||||
// Find digits of result.
|
||||
unsigned result = 0;
|
||||
while (shift >= 0) {
|
||||
result = result << 1;
|
||||
unsigned candidateResult = result + 1;
|
||||
if (candidateResult*candidateResult <= n >> shift) {
|
||||
result = candidateResult;
|
||||
}
|
||||
shift -= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[])
|
||||
{
|
||||
// Init & parse arguments
|
||||
// NUMBER
|
||||
// --from NUMBER --to NUMBER --step NUMBER --grow NUMBER
|
||||
// --graph --best --average --quiet
|
||||
// --repeat N
|
||||
struct parse_opt_s arg;
|
||||
parse_config(&arg, argc, argv);
|
||||
int i = select_config(arg.test_function, n, functions);
|
||||
double from = (arg.from == 0) ? functions[i].default_n : arg.from;
|
||||
double to = (arg.to == 0) ? functions[i].default_n : arg.to;
|
||||
|
||||
// Open plot file if needed
|
||||
FILE *graph_file = NULL;
|
||||
if (arg.graph) {
|
||||
char filename[100];
|
||||
sprintf(filename, "plot-%s-%d.dat", library, arg.test_function);
|
||||
graph_file = fopen(filename, "wt");
|
||||
if (!graph_file) {
|
||||
fprintf(stderr, "ERROR: Cannot create the file '%s'\n", filename);
|
||||
exit(-2);
|
||||
}
|
||||
fprintf(graph_file, "# plotting %s-%d : %s\n# N T", library, arg.test_function, functions[i].funcname);
|
||||
}
|
||||
|
||||
// Do the bench
|
||||
for(double n = from; n <= to ; n = ((arg.grow == 0) ? n + arg.step : n * arg.grow))
|
||||
{
|
||||
double best = (1.0/0.0);
|
||||
double avg = 0.0;
|
||||
double variance = 0.0;
|
||||
static double measure[MAX_REPEAT];
|
||||
double ba = 0.0;
|
||||
|
||||
if (functions[i].init != NULL)
|
||||
functions[i].init(n);
|
||||
|
||||
// Measure the time of the test_function
|
||||
for(unsigned r = 0; r < arg.repeat; r++) {
|
||||
double t0 = test_function( (arg.graph|arg.best|arg.average|arg.quiet) ? NULL : functions[i].funcname, (size_t) n, functions[i].func);
|
||||
best = (t0 < best) ? t0 : best;
|
||||
avg += t0;
|
||||
variance += t0 * t0;
|
||||
measure[r] = t0;
|
||||
}
|
||||
|
||||
// Closure of the tests
|
||||
if (functions[i].clear != NULL)
|
||||
functions[i].clear();
|
||||
|
||||
// Finish computing the different times
|
||||
if (arg.repeat > 1) {
|
||||
avg /= arg.repeat;
|
||||
variance = (variance - arg.repeat * avg * avg) / (arg.repeat - 1);
|
||||
ba = get_best_within(arg.repeat, measure, 1. + arg.within);
|
||||
} else {
|
||||
ba = avg;
|
||||
}
|
||||
|
||||
// The average value shall be around best_within (and greater)
|
||||
// otherwise the result doesn't seem reliable.
|
||||
bool reliable = (ba <= avg && ba * (1.0+arg.within/2) + 0.5 > avg);
|
||||
|
||||
// Print the result
|
||||
if (arg.graph) {
|
||||
fprintf(graph_file, "%f %f\n", n, arg.average ? avg : arg.best ? best : ba);
|
||||
} else if (arg.quiet) {
|
||||
printf("%ld\n", (long)(arg.average ? avg : arg.best ? best : reliable ? ba : -1));
|
||||
} else if (arg.repeat > 1) {
|
||||
if (arg.average == false && arg.best_within == false)
|
||||
printf ("%20.20s time %lu ms for n = %lu *** BEST ***\n", functions[i].funcname, (unsigned long) best, (unsigned long) n);
|
||||
if (arg.best == false && arg.best_within == false)
|
||||
printf ("%20.20s time %lu ms +/- %u ms for n = %lu *** AVERAGE ***\n", functions[i].funcname, (unsigned long) avg, 2*integer_sqrt(variance), (unsigned long) n);
|
||||
if (arg.best == false && arg.average == false)
|
||||
printf ("%20.20s time %lu ms for n = %lu *** BEST within %d%% *** %s\n", functions[i].funcname, (unsigned long) ba, (unsigned long) n, (int)(100*arg.within), reliable ? "" : "(unreliable result)");
|
||||
}
|
||||
}
|
||||
|
||||
// Close the FILE if needed
|
||||
if (arg.graph) {
|
||||
fclose(graph_file);
|
||||
printf("File plot-%s-%d.dat generated.\n"
|
||||
"Run in gnuplot the following command:\n"
|
||||
"\tplot 'plot-%s-%d.dat' with linespoints linestyle 1\n",
|
||||
library, arg.test_function, library, arg.test_function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __COMMON_BENCH__H
|
||||
#define __COMMON_BENCH__H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Needed for M_THREAD_ATTR
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
#include "m-thread.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) \
|
||||
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/types.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define USE_SYSCTL
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
// Number of elements of a table
|
||||
#define numberof(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
/* Get the current CPU time in microsecond. */
|
||||
#if defined(_WIN32)
|
||||
/* WINDOWS variant */
|
||||
static inline unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
LARGE_INTEGER freq, val;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&val);
|
||||
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
|
||||
}
|
||||
#else
|
||||
/* UNIX variant */
|
||||
static inline unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
// Multi thread: Uses real time
|
||||
struct timeval tv;
|
||||
gettimeofday (&tv, NULL);
|
||||
return tv.tv_sec * 1000000ULL + tv.tv_usec;
|
||||
#else
|
||||
// Single thread: Uses process usage time
|
||||
struct rusage rus;
|
||||
getrusage (0, &rus);
|
||||
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// The result of the bench
|
||||
extern unsigned long g_result;
|
||||
|
||||
// The rand value
|
||||
extern
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
M_THREAD_ATTR
|
||||
#endif
|
||||
unsigned int randValue;
|
||||
|
||||
// Pseudo rand value generator.
|
||||
// Not very random but sufficient and fast enough for the bench
|
||||
static inline void rand_init(void)
|
||||
{
|
||||
randValue = 0;
|
||||
}
|
||||
static inline unsigned int rand_get(void)
|
||||
{
|
||||
randValue = randValue * 31421U + 6927U;
|
||||
return randValue;
|
||||
}
|
||||
|
||||
// The function that performs the bench
|
||||
static inline double
|
||||
test_function(const char *str, size_t n, void (*func)(size_t))
|
||||
{
|
||||
double result;
|
||||
unsigned long long start, end;
|
||||
rand_init();
|
||||
start = cputime();
|
||||
(*func)(n);
|
||||
end = cputime();
|
||||
result = (double)(end-start) / 1000.0;
|
||||
if (str != NULL) {
|
||||
printf ("%20.20s time %.2f ms for n = %lu [r=%lu]\n", str, result, (unsigned long) n, g_result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Configuration table
|
||||
typedef struct {
|
||||
int num;
|
||||
const char *funcname;
|
||||
size_t default_n;
|
||||
void (*init)(size_t);
|
||||
void (*func)(size_t);
|
||||
void (*clear)(void);
|
||||
} config_func_t;
|
||||
|
||||
// Perform a parsing of the given arguments and do the bench according to the table
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[]);
|
||||
|
||||
/* Return the number of CPU of the system */
|
||||
static inline int get_cpu_count(void)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#elif defined(USE_SYSCTL)
|
||||
int nm[2];
|
||||
int count = 0;
|
||||
size_t len = sizeof (count);
|
||||
nm[0] = CTL_HW;
|
||||
nm[1] = HW_NCPU;
|
||||
sysctl(nm, 2, &count, &len, NULL, 0);
|
||||
return MAX(1, count);
|
||||
#elif defined (_SC_NPROCESSORS_ONLN)
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void compiler_barrier(void *p);
|
||||
|
||||
#endif
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* MLIB - TEST module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define NDEBUG
|
||||
#include "m-dict.h"
|
||||
|
||||
static inline bool oor_equal_p(int64_t k, unsigned char n)
|
||||
{
|
||||
return k == (int)-n-1;
|
||||
}
|
||||
static inline void oor_set(int64_t *k, unsigned char n)
|
||||
{
|
||||
*k = (int)-n-1;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_int64, int64_t, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)), int64_t, M_DEFAULT_OPLIST)
|
||||
|
||||
static int64_t get_rand(void)
|
||||
{
|
||||
int64_t n;
|
||||
do {
|
||||
n = (rand() * RAND_MAX + rand()) * RAND_MAX + rand();
|
||||
} while (n == -1 || n == -2);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Let's push value into the dict until the system abandons!
|
||||
static void find_max(void)
|
||||
{
|
||||
dict_int64_t dict;
|
||||
dict_int64_init(dict);
|
||||
size_t n = 0;
|
||||
while (true) {
|
||||
dict_int64_set_at (dict, get_rand(), get_rand());
|
||||
n++;
|
||||
if ((n & ((1<<20)-1)) == 0) {
|
||||
printf ("n=%zu\n", n);
|
||||
}
|
||||
}
|
||||
dict_int64_clear(dict);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
find_max();
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
CC=cc -std=c11
|
||||
CXX=c++ -std=c++11
|
||||
CFLAGS=-O2 -march=native -Wall
|
||||
CPPFLAGS=-I../..
|
||||
|
||||
all: k-nucleotide
|
||||
|
||||
k-nucleotide: k-nucleotide.exe data.txt
|
||||
./k-nucleotide.exe < data.txt
|
||||
|
||||
k-nucleotide.exe: nucleotide.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) nucleotide.c -o k-nucleotide.exe -lpthread
|
||||
|
||||
data.txt: fasta.exe
|
||||
./fasta.exe 25000000 > data.txt
|
||||
|
||||
fasta.exe: fasta.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -fopenmp fasta.c -o fasta.exe
|
||||
|
||||
clean:
|
||||
rm -f *.exe *~ *.o data.txt
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
// The Computer Language Benchmarks Game
|
||||
// http://benchmarksgame.alioth.debian.org/
|
||||
//
|
||||
// contributed by Jeremy Zerfas
|
||||
// rewritten by Аноним Легионов, inspired by fasta Rust #2 program
|
||||
// use two OpenMP locks instead of one critical section
|
||||
// decouples IO activity from random number generation
|
||||
//
|
||||
// modified by Josh Goldfoot, adding use of a buffer for fasta_repeat
|
||||
|
||||
|
||||
// This controls the width of lines that are output by this program.
|
||||
#define MAXIMUM_LINE_WIDTH 60
|
||||
|
||||
// This program will generate the random nucleotide sequences in parallel which
|
||||
// are worked on in blocks of lines. The number of lines in those blocks is
|
||||
// controlled by this setting.
|
||||
#define LINES_PER_BLOCK 1024
|
||||
|
||||
#define CHARACTERS_PER_BLOCK (MAXIMUM_LINE_WIDTH*LINES_PER_BLOCK)
|
||||
|
||||
#define THREADS_TO_USE 4
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
// intptr_t should be the native integer type on most sane systems.
|
||||
typedef intptr_t intnative_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char letter;
|
||||
float probability;
|
||||
} nucleotide_info;
|
||||
|
||||
|
||||
// Repeatedly print string_To_Repeat until it has printed
|
||||
// number_Of_Characters_To_Create. The output is also wrapped to
|
||||
// MAXIMUM_LINE_WIDTH columns.
|
||||
static char* make_Sequence_Buffer(const char string_To_Repeat[])
|
||||
{
|
||||
const intnative_t string_To_Repeat_Length = strlen(string_To_Repeat);
|
||||
|
||||
// JG: Changed to that this writes a sequence to a buffer, which is used
|
||||
intnative_t number_Of_Characters_To_Create = string_To_Repeat_Length * MAXIMUM_LINE_WIDTH;
|
||||
char* buffer = (char*) malloc(number_Of_Characters_To_Create +
|
||||
number_Of_Characters_To_Create / MAXIMUM_LINE_WIDTH + 1);
|
||||
if (buffer == NULL)
|
||||
exit(-1);
|
||||
char* bufferOffset = buffer;
|
||||
|
||||
// Create an extended_String_To_Repeat which is a copy of string_To_Repeat
|
||||
// but extended with another copy of the first MAXIMUM_LINE_WIDTH characters
|
||||
// of string_To_Repeat appended to the end. Later on this allows us to
|
||||
// generate a line of output just by doing simple memory copies using an
|
||||
// appropriate offset into extended_String_To_Repeat.
|
||||
char extended_String_To_Repeat[string_To_Repeat_Length + MAXIMUM_LINE_WIDTH];
|
||||
for (intnative_t column = 0; column<string_To_Repeat_Length + MAXIMUM_LINE_WIDTH;
|
||||
column++)
|
||||
extended_String_To_Repeat[column] =
|
||||
string_To_Repeat[column%string_To_Repeat_Length];
|
||||
intnative_t offset = 0;
|
||||
|
||||
char line[MAXIMUM_LINE_WIDTH + 1];
|
||||
line[MAXIMUM_LINE_WIDTH] = '\n';
|
||||
|
||||
for (intnative_t current_Number_Of_Characters_To_Create =
|
||||
number_Of_Characters_To_Create;
|
||||
current_Number_Of_Characters_To_Create>0;)
|
||||
{
|
||||
// Figure out the length of the line we need to write. If it's less than
|
||||
// MAXIMUM_LINE_WIDTH then we also need to add a line feed in the right
|
||||
// spot too.
|
||||
intnative_t line_Length = MAXIMUM_LINE_WIDTH;
|
||||
if (current_Number_Of_Characters_To_Create<MAXIMUM_LINE_WIDTH)
|
||||
{
|
||||
line_Length = current_Number_Of_Characters_To_Create;
|
||||
line[line_Length] = '\n';
|
||||
}
|
||||
|
||||
memcpy(line, extended_String_To_Repeat + offset, line_Length);
|
||||
|
||||
// Update the offset, reducing it by string_To_Repeat_Length if
|
||||
// necessary.
|
||||
offset += line_Length;
|
||||
if (offset>string_To_Repeat_Length)
|
||||
offset -= string_To_Repeat_Length;
|
||||
|
||||
// "Output" that line to our buffer and update the
|
||||
// current_Number_Of_Characters_To_Create.
|
||||
memcpy(bufferOffset, line, line_Length + 1); // JG: used to be fwrite(line, line_Length + 1, 1, stdout);
|
||||
bufferOffset += line_Length + 1;
|
||||
current_Number_Of_Characters_To_Create -= line_Length;
|
||||
}
|
||||
*bufferOffset = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void repeat_And_Wrap_String(const char string_To_Repeat[], intnative_t number_Of_Characters_To_Create)
|
||||
{
|
||||
/* JG: fasta_repeat repeats every len(alu) * line-length = 287 * 61 = 17507 characters.
|
||||
So, calculate this once, then just print that buffer over and over. */
|
||||
char* sequence = make_Sequence_Buffer(string_To_Repeat);
|
||||
intnative_t sequenceLen = (intnative_t) strlen(sequence);
|
||||
intnative_t outputBytes = number_Of_Characters_To_Create + number_Of_Characters_To_Create / 60;
|
||||
while (outputBytes >= sequenceLen) {
|
||||
fwrite(sequence, sequenceLen, 1, stdout);
|
||||
outputBytes -= sequenceLen;
|
||||
}
|
||||
if (outputBytes > 0) {
|
||||
fwrite(sequence, outputBytes, 1, stdout);
|
||||
printf("\n");
|
||||
}
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
|
||||
// Generate a pseudorandom number from 0 to max using a linear
|
||||
// congruential generator.
|
||||
#define IM 139968
|
||||
#define IA 3877
|
||||
#define IC 29573
|
||||
uint32_t seed = 42;
|
||||
|
||||
int rng_tid; //Thread ID
|
||||
int rng_tnum = 1; //Thread number
|
||||
intnative_t rng_cnt = 0;
|
||||
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t rng_lock;
|
||||
#define RNG_LOCK_INIT() omp_init_lock(&rng_lock)
|
||||
#define RNG_LOCK() omp_set_lock(&rng_lock)
|
||||
#define RNG_FREE() omp_unset_lock(&rng_lock)
|
||||
#else
|
||||
#define RNG_LOCK_INIT() do{}while(0)
|
||||
#define RNG_LOCK() do{}while(0)
|
||||
#define RNG_FREE() do{}while(0)
|
||||
#endif
|
||||
|
||||
static void rng_init(void)
|
||||
{
|
||||
RNG_LOCK_INIT();
|
||||
rng_tid = 0;
|
||||
}
|
||||
|
||||
static intnative_t rng_gen_blk(uint32_t * buf, intnative_t len, int curr_tid)
|
||||
{
|
||||
intnative_t gen_cnt = -1;//Error by default
|
||||
RNG_LOCK();
|
||||
|
||||
if (rng_tid == curr_tid)
|
||||
{
|
||||
if (++rng_tid >= rng_tnum)
|
||||
{
|
||||
rng_tid = 0;
|
||||
}
|
||||
|
||||
gen_cnt = (len<rng_cnt) ? len : rng_cnt;
|
||||
rng_cnt -= gen_cnt;
|
||||
|
||||
len = gen_cnt;
|
||||
|
||||
while (0 != len--)
|
||||
{
|
||||
seed = (seed*IA + IC) % IM;
|
||||
*(buf++) = seed;//This is stupid actually!
|
||||
}
|
||||
}
|
||||
|
||||
RNG_FREE();
|
||||
return gen_cnt;
|
||||
}
|
||||
|
||||
int out_tid; //Thread ID
|
||||
int out_tnum = 1; //Thread number
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t out_lock;
|
||||
#define OUT_LOCK_INIT() omp_init_lock(&out_lock)
|
||||
#define OUT_LOCK() omp_set_lock(&out_lock)
|
||||
#define OUT_FREE() omp_unset_lock(&out_lock)
|
||||
#else
|
||||
#define OUT_LOCK_INIT() do{}while(0)
|
||||
#define OUT_LOCK() do{}while(0)
|
||||
#define OUT_FREE() do{}while(0)
|
||||
#endif
|
||||
|
||||
static void out_init(void)
|
||||
{
|
||||
OUT_LOCK_INIT();
|
||||
rng_tid = 0;
|
||||
}
|
||||
|
||||
static intnative_t out_write(char * buf, intnative_t len, int curr_tid)
|
||||
{
|
||||
intnative_t wr_cnt = -1;//Error by default
|
||||
OUT_LOCK();
|
||||
|
||||
if (out_tid == curr_tid)
|
||||
{
|
||||
if (++out_tid >= out_tnum)
|
||||
{
|
||||
out_tid = 0;
|
||||
}
|
||||
wr_cnt = fwrite(buf, len, 1, stdout);
|
||||
}
|
||||
|
||||
OUT_FREE();
|
||||
return wr_cnt; //-1 - thread error, 0 - IO error, 1 - ОК
|
||||
}
|
||||
|
||||
static void generate_And_Wrap_Pseudorandom_DNA_Sequence(
|
||||
const nucleotide_info nucl_info[],
|
||||
const intnative_t nucl_num,
|
||||
const intnative_t char_num)
|
||||
{
|
||||
uint32_t cumul_p[nucl_num];
|
||||
float cumul_acc = 0.0;
|
||||
|
||||
for (intnative_t i = 0; i<nucl_num; i++)
|
||||
{
|
||||
cumul_acc += nucl_info[i].probability;
|
||||
cumul_p[i] = 1ul + (uint32_t)(cumul_acc*(float)IM); //Compensate rounding errors on test file
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
intnative_t tnum = omp_get_num_procs();
|
||||
if (tnum>THREADS_TO_USE) tnum = THREADS_TO_USE;
|
||||
omp_set_num_threads(tnum);
|
||||
rng_tnum = tnum;
|
||||
out_tnum = tnum;
|
||||
#endif
|
||||
rng_tid = 0;
|
||||
out_tid = 0;
|
||||
rng_cnt = char_num;
|
||||
|
||||
#pragma omp parallel
|
||||
{
|
||||
char block[CHARACTERS_PER_BLOCK + LINES_PER_BLOCK];
|
||||
char * line;
|
||||
uint32_t rnd[CHARACTERS_PER_BLOCK], r;
|
||||
intnative_t cnt, col, prid, nid, ncnt;
|
||||
int cur_tid;
|
||||
#ifdef _OPENMP
|
||||
cur_tid = omp_get_thread_num();
|
||||
#else
|
||||
cur_tid = 0;
|
||||
#endif
|
||||
while (1)
|
||||
{
|
||||
do
|
||||
{
|
||||
cnt = rng_gen_blk(rnd, CHARACTERS_PER_BLOCK, cur_tid);
|
||||
} while (-1 == cnt);
|
||||
|
||||
if (0 == cnt)
|
||||
{
|
||||
break;//Work finished!
|
||||
}
|
||||
|
||||
line = block;
|
||||
|
||||
for (col = 0, prid = 0; prid < cnt; prid++)
|
||||
{
|
||||
r = rnd[prid];
|
||||
|
||||
ncnt = 0;
|
||||
for (nid = 0; nid < nucl_num; nid++)
|
||||
{
|
||||
if (cumul_p[nid] <= r)
|
||||
{
|
||||
ncnt++;
|
||||
}
|
||||
}
|
||||
|
||||
*line++ = nucl_info[ncnt].letter;
|
||||
if (++col >= MAXIMUM_LINE_WIDTH)
|
||||
{
|
||||
col = 0;
|
||||
*line++ = '\n';
|
||||
}
|
||||
}
|
||||
//Check if we need to end the line
|
||||
if (0 != col)
|
||||
{
|
||||
//Last iteration didn't end the line, so finish the job.
|
||||
*line++ = '\n';
|
||||
}
|
||||
//Print results
|
||||
do
|
||||
{
|
||||
cnt = out_write(block, line - block, cur_tid);
|
||||
} while (-1 == cnt);
|
||||
//Check fot IO error
|
||||
if (0 == cnt)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
const intnative_t n = atoi(argv[1]);
|
||||
|
||||
fputs(">ONE Homo sapiens alu\n", stdout);
|
||||
const char homo_Sapiens_Alu[] =
|
||||
"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTC"
|
||||
"AGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCG"
|
||||
"TGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGG"
|
||||
"AGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
|
||||
repeat_And_Wrap_String(homo_Sapiens_Alu, 2 * n);
|
||||
|
||||
rng_init();
|
||||
out_init();
|
||||
|
||||
fputs(">TWO IUB ambiguity codes\n", stdout);
|
||||
nucleotide_info iub_Nucleotides_Information[] =
|
||||
{
|
||||
{ 'a', 0.27 },{ 'c', 0.12 },{ 'g', 0.12 },{ 't', 0.27 },{ 'B', 0.02 },
|
||||
{ 'D', 0.02 },{ 'H', 0.02 },{ 'K', 0.02 },{ 'M', 0.02 },{ 'N', 0.02 },
|
||||
{ 'R', 0.02 },{ 'S', 0.02 },{ 'V', 0.02 },{ 'W', 0.02 },{ 'Y', 0.02 }
|
||||
};
|
||||
generate_And_Wrap_Pseudorandom_DNA_Sequence(iub_Nucleotides_Information,
|
||||
sizeof(iub_Nucleotides_Information) / sizeof(nucleotide_info), 3 * n);
|
||||
|
||||
fputs(">THREE Homo sapiens frequency\n", stdout);
|
||||
nucleotide_info homo_Sapien_Nucleotides_Information[] =
|
||||
{
|
||||
{ 'a', 0.3029549426680 },{ 'c', 0.1979883004921 },
|
||||
{ 'g', 0.1975473066391 },{ 't', 0.3015094502008 }
|
||||
};
|
||||
generate_And_Wrap_Pseudorandom_DNA_Sequence(homo_Sapien_Nucleotides_Information,
|
||||
sizeof(homo_Sapien_Nucleotides_Information) / sizeof(nucleotide_info), 5 * n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
// The Computer Language Benchmarks Game
|
||||
// http://benchmarksgame.alioth.debian.org/
|
||||
//
|
||||
// Original version Contributed by Jeremy Zerfas
|
||||
// M*LIB port contributed by Patrick Pélissier
|
||||
#define NDEBUG
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "m-worker.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-bptree.h"
|
||||
|
||||
// This controls the maximum length for each set of oligonucleotide frequencies
|
||||
// and each oligonucleotide count output by this program.
|
||||
#define MAXIMUM_OUTPUT_LENGTH 4096
|
||||
|
||||
// Number of outputs to display
|
||||
#define MAXMUM_NUMBER_OUTPUT 7
|
||||
|
||||
/* Define the needed containers */
|
||||
/* Define Open Addressing dictionary of uint64_t --> uint32_t. Need to define how to handle out of bound data. */
|
||||
static inline bool my_oor_equal_p(uint64_t k, unsigned char n)
|
||||
{
|
||||
return k == ~(uint64_t) n;
|
||||
}
|
||||
static inline void my_oor_set(uint64_t *k, unsigned char n)
|
||||
{
|
||||
*k = ~(uint64_t)n;
|
||||
}
|
||||
static inline size_t my_hash(uint64_t key)
|
||||
{
|
||||
return ((key) ^ (key)>>7);
|
||||
}
|
||||
DICT_OA_DEF2(dict_oligonucleotide,
|
||||
uint64_t, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)),
|
||||
uint32_t, M_DEFAULT_OPLIST)
|
||||
#define M_OPL_dict_oligonucleotide_t() DICT_OPLIST(dict_oligonucleotide, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)), M_DEFAULT_OPLIST)
|
||||
|
||||
// Define a B*TREE-21 of uint32_t --> uint64_t and register its oplist
|
||||
#define TREE_CMP(a,b) ((a) > (b) ? -1 : (a) < (b)) /* reverse natural order */
|
||||
BPTREE_DEF2(tree_dict_oligonucleotide, 21, uint32_t, M_OPEXTEND(M_DEFAULT_OPLIST, CMP(TREE_CMP)), uint64_t, M_DEFAULT_OPLIST)
|
||||
#define M_OPL_tree_dict_oligonucleotide_t() BPTREE_OPLIST2(tree_dict_oligonucleotide, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
/* Define the data to analyse (a plain array) */
|
||||
ARRAY_DEF(polynucleotide, unsigned char)
|
||||
|
||||
// Macro to convert a nucleotide character to a code. Note that upper and lower
|
||||
// case ASCII letters only differ in the fifth bit from the right and we only
|
||||
// need the three least significant bits to differentiate the letters 'A', 'C',
|
||||
// 'G', and 'T'. Spaces in this array/string will never be used as long as
|
||||
// characters other than 'A', 'C', 'G', and 'T' aren't used.
|
||||
#define CODE_FOR_NUCLEOTIDE(nucleotide) (" \0 \1\3 \2"[nucleotide & 0x7])
|
||||
|
||||
// And one more macro to convert the codes back to nucleotide characters.
|
||||
#define NUCLEOTIDE_FOR_CODE(code) ("ACGT"[code & 0x3])
|
||||
|
||||
static worker_t worker;
|
||||
|
||||
// Read from the given stream the data and fill in the array with the converted one.
|
||||
static void polynucleotide_init_stream(polynucleotide_t p, FILE *stream)
|
||||
{
|
||||
char buffer[MAXIMUM_OUTPUT_LENGTH], c;
|
||||
|
||||
polynucleotide_init(p);
|
||||
polynucleotide_reserve(p, MAXIMUM_OUTPUT_LENGTH);
|
||||
// Find the start of the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stream) && memcmp(">THREE", buffer, 6));
|
||||
// Start reading and encoding the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stream) && buffer[0]!='>'){
|
||||
for(size_t i = 0; (c = buffer[i]) !='\0'; i++) {
|
||||
if (c != '\n') {
|
||||
polynucleotide_push_back(p, CODE_FOR_NUCLEOTIDE(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the function which does 95% of the work.
|
||||
/* NOTE: paralelisation is not done by cutting the input set in two.
|
||||
This is not efficient as the size of the output table will be the same
|
||||
as for the whole table. */
|
||||
static void init_hash(dict_oligonucleotide_t hash_Table, const size_t size, const unsigned char tab[size], const size_t desiredLength, const size_t offset, const size_t skip)
|
||||
{
|
||||
uint64_t key = 0;
|
||||
const uint64_t mask = (1ULL<<(2*desiredLength))-1;
|
||||
assert (2*desiredLength < 64 && desiredLength < size);
|
||||
assert ((1 == skip || 4 == skip) && offset < size);
|
||||
|
||||
// For the first several nucleotides we only need to append them to key in
|
||||
// preparation for the insertion of complete oligonucleotides to hash.
|
||||
for(size_t i = 0; i < desiredLength - 1 + offset; i++)
|
||||
key = (key<<2) | tab[i];
|
||||
key &= mask;
|
||||
|
||||
dict_oligonucleotide_t hash;
|
||||
dict_oligonucleotide_init(hash);
|
||||
|
||||
// Add all the complete oligonucleotides of
|
||||
// desiredLength to hash, updating their count
|
||||
if (skip == 1) {
|
||||
for(size_t i = desiredLength-1+offset ; i < size; i++) {
|
||||
key = (key<<2) | tab[i+0];
|
||||
key &= mask;
|
||||
uint32_t *p = dict_oligonucleotide_get(hash, key);
|
||||
if (p != NULL) {
|
||||
(*p) ++;
|
||||
} else {
|
||||
dict_oligonucleotide_set_at(hash, key, 1);
|
||||
}
|
||||
}
|
||||
} else { // skip = 4
|
||||
for(size_t i = desiredLength-1+offset ; i < size; i+=4) {
|
||||
for (size_t j = 0; j < 4; j++)
|
||||
key = (key<<2) | tab[i+j];
|
||||
key &= mask;
|
||||
uint32_t *p = dict_oligonucleotide_get(hash, key);
|
||||
if (p != NULL) {
|
||||
(*p) ++;
|
||||
} else {
|
||||
dict_oligonucleotide_set_at(hash, key, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
dict_oligonucleotide_init_move(hash_Table, hash);
|
||||
}
|
||||
|
||||
static void compute_hash(dict_oligonucleotide_t hash_Table, const polynucleotide_t p, const size_t desiredLength)
|
||||
{
|
||||
size_t size = polynucleotide_size(p);
|
||||
unsigned char *tab = polynucleotide_get(p, 0);
|
||||
if (desiredLength < 8) {
|
||||
init_hash(hash_Table, size, tab, desiredLength, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
worker_sync_t block; // Define a synchronization block
|
||||
worker_start(block, worker);
|
||||
dict_oligonucleotide_t hash_Table3, *p_hash_Table3 = &hash_Table3;
|
||||
dict_oligonucleotide_t hash_Table2, *p_hash_Table2 = &hash_Table2;
|
||||
dict_oligonucleotide_t hash_Table1, *p_hash_Table1 = &hash_Table1;
|
||||
WORKER_SPAWN(block, (p_hash_Table1, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table1, size, tab, desiredLength, 3, 4);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (p_hash_Table2, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table2, size, tab, desiredLength, 2, 4);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (p_hash_Table3, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table3, size, tab, desiredLength, 1, 4);
|
||||
}, (/*no output*/));
|
||||
init_hash(hash_Table, size, tab, desiredLength, 0, 4);
|
||||
worker_sync(block);
|
||||
|
||||
// Merge all hashtables
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table1);
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table2);
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table3);
|
||||
// Clear extra hashtables
|
||||
dict_oligonucleotide_clear(hash_Table3);
|
||||
dict_oligonucleotide_clear(hash_Table2);
|
||||
dict_oligonucleotide_clear(hash_Table1);
|
||||
}
|
||||
|
||||
// Generate frequencies for all oligonucleotides in polynucleotide that are of
|
||||
// desiredLength and then save it to output.
|
||||
static void compute_freq(const polynucleotide_t p, const size_t desiredLength, char output[])
|
||||
{
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
compute_hash(hash_Table, p, desiredLength);
|
||||
|
||||
// Order key is the count frequency (inverse value & key)
|
||||
tree_dict_oligonucleotide_t tree;
|
||||
tree_dict_oligonucleotide_init(tree);
|
||||
for M_EACH(item, hash_Table, dict_oligonucleotide_t) {
|
||||
tree_dict_oligonucleotide_set_at (tree, item->value, item->key);
|
||||
}
|
||||
|
||||
// Create Output
|
||||
char oligonucleotide[64];
|
||||
const double invSum = 100.0 / (double) (polynucleotide_size(p) - desiredLength + 1);
|
||||
int output_Position = 0;
|
||||
for M_EACH(item, tree, tree_dict_oligonucleotide_t) {
|
||||
// Convert the key for the oligonucleotide to a string.
|
||||
uint64_t freq = *item->value_ptr;
|
||||
uint32_t count = *item->key_ptr;
|
||||
for(size_t j = desiredLength-1; j < desiredLength; j--){
|
||||
oligonucleotide[j]=NUCLEOTIDE_FOR_CODE(freq);
|
||||
freq>>=2;
|
||||
}
|
||||
oligonucleotide[desiredLength]='\0';
|
||||
// Output the frequency for oligonucleotide to output.
|
||||
output_Position+=snprintf(output+output_Position, MAXIMUM_OUTPUT_LENGTH-output_Position,
|
||||
"%s %.3f\n", oligonucleotide, invSum * count);
|
||||
}
|
||||
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
tree_dict_oligonucleotide_clear(tree);
|
||||
}
|
||||
|
||||
// Generate a count for the number of times oligonucleotide appears in
|
||||
// polynucleotide and then save it to output.
|
||||
static void compute_count(polynucleotide_t p, const char oligonucleotide[], char output[])
|
||||
{
|
||||
const size_t oligonucleotide_Length = strlen(oligonucleotide);
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
compute_hash(hash_Table, p, oligonucleotide_Length);
|
||||
|
||||
// Generate the key for oligonucleotide.
|
||||
uint64_t key=0;
|
||||
for(size_t i=0; i<oligonucleotide_Length; i++)
|
||||
key = (key<<2) | CODE_FOR_NUCLEOTIDE(oligonucleotide[i]);
|
||||
|
||||
// Output the count for oligonucleotide to output.
|
||||
uint32_t *val = dict_oligonucleotide_get(hash_Table, key);
|
||||
uintmax_t count= val== NULL ? 0 : *val;
|
||||
snprintf(output, MAXIMUM_OUTPUT_LENGTH, "%ju\t%s", count, oligonucleotide);
|
||||
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Initialize the different worker threads by auto-detecting the number of cores of the system
|
||||
worker_init(worker, -1, 0, NULL);
|
||||
|
||||
// Read the input
|
||||
polynucleotide_t p, *ptr = &p;
|
||||
polynucleotide_init_stream(p, stdin);
|
||||
|
||||
// Prepare outputs
|
||||
char outputBuffer[MAXMUM_NUMBER_OUTPUT][MAXIMUM_OUTPUT_LENGTH];
|
||||
char (*output)[MAXIMUM_OUTPUT_LENGTH] = outputBuffer;
|
||||
|
||||
// Do the following functions in parallel.
|
||||
worker_sync_t block; // Define a synchronization block
|
||||
worker_start(block, worker);
|
||||
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATTTTAATTTATAGT", output[6]);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATTTTAATT", output[5]);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATT", output[4]);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTA", output[3]);
|
||||
}, (/*no output*/));
|
||||
compute_count(*ptr, "GGT", output[2]);
|
||||
compute_freq(*ptr, 2, output[1]);
|
||||
compute_freq(p, 1, output[0]);
|
||||
worker_sync(block);
|
||||
|
||||
// Output the results to stdout.
|
||||
for(int i=0; i<MAXMUM_NUMBER_OUTPUT; i++)
|
||||
printf("%s\n", outputBuffer[i]);
|
||||
|
||||
polynucleotide_clear(p);
|
||||
worker_clear(worker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
-940
@@ -1,940 +0,0 @@
|
||||
/**
|
||||
* Memory pool benchmark.
|
||||
*
|
||||
* Results (the better values for each measure from at least 3 runs)for
|
||||
* Intel Core i7-4650U CPU 1.70GHz, 8GB RAM:
|
||||
*
|
||||
* small object size: 24
|
||||
* big object size: 240
|
||||
* huge object size: 8305
|
||||
*
|
||||
* boost::pool (Small): 258ms
|
||||
* boost::pool w/ free (Small): 165ms
|
||||
* boost::pool (Big): 174ms
|
||||
* boost::pool w/ free (Big): 154ms
|
||||
* boost::pool cr. & destr.: 133ms
|
||||
*
|
||||
* ngx_pool (Small): 305ms
|
||||
* ngx_pool (Big): 132ms
|
||||
* ngx_pool w/ free (Mix): 542ms
|
||||
* ngx_pool cr. & destr.: 98ms
|
||||
*
|
||||
* tfw_pool (Small): 279ms
|
||||
* tfw_pool w/ free (Small): 101ms
|
||||
* tfw_pool (Big): 106ms
|
||||
* tfw_pool w/ free (Big): 50ms
|
||||
* tfw_pool w/ free (Mix): 107ms
|
||||
* tfw_pool cr. & destr.: 53ms
|
||||
*
|
||||
* Copyright (C) 2015 Alexander Krizhanovsky (ak@natsys-lab.com).
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
* See http://www.gnu.org/licenses/lgpl.html .
|
||||
*
|
||||
* From https://github.com/natsys/blog/tree/master/mempool
|
||||
*/
|
||||
#define NDEBUG
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_BOOST
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef USE_GLIB
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
static const size_t PAGE_SIZE = 4096;
|
||||
|
||||
// sizeof(TfwStr)
|
||||
struct Small {
|
||||
long l[3];
|
||||
|
||||
std::string
|
||||
operator+(const char *str)
|
||||
{ return std::string(str) + " (Small)"; }
|
||||
};
|
||||
|
||||
// size of common HTTP headers table
|
||||
struct Big {
|
||||
Small s[10];
|
||||
|
||||
std::string
|
||||
operator+(const char *str)
|
||||
{ return std::string(str) + " (Big)"; }
|
||||
};
|
||||
|
||||
// very big allocation
|
||||
struct Huge {
|
||||
char c[PAGE_SIZE * 2 + 113];
|
||||
|
||||
std::string
|
||||
operator+(const char *str)
|
||||
{ return std::string(str) + " (Huge)"; }
|
||||
};
|
||||
|
||||
static const size_t N = 20 * 1000 * 1000;
|
||||
|
||||
void
|
||||
benchmark(std::string &&desc, std::function<void ()> cb)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
// steady_clock has the same resolution as high_resolution_clock
|
||||
auto t(steady_clock::now());
|
||||
|
||||
cb();
|
||||
|
||||
auto dt = steady_clock::now() - t;
|
||||
std::cout << std::setw(30) << std::right << desc << ": "
|
||||
<< duration_cast<milliseconds>(dt).count()
|
||||
<< "ms" << std::endl;
|
||||
}
|
||||
|
||||
#define touch_obj(o) \
|
||||
if (__builtin_expect(!o, 0)) { \
|
||||
std::cerr << "failed alloc" << std::endl; \
|
||||
break; \
|
||||
} else { \
|
||||
*(long *)o = 1; \
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
* Boost::pool
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
#ifdef USE_BOOST
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_boost_pool()
|
||||
{
|
||||
boost::object_pool<T> p;
|
||||
|
||||
benchmark(std::move(T() + "boost::pool"), [&]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = p.malloc();
|
||||
touch_obj(o);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_boost_pool_free()
|
||||
{
|
||||
boost::object_pool<T> p;
|
||||
|
||||
benchmark(std::move(T() + "boost::pool w/ free"), [&]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = p.malloc();
|
||||
touch_obj(o);
|
||||
if (__builtin_expect(!(i & 3), 0))
|
||||
p.free(o);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_boost_pool_create_and_destroy()
|
||||
{
|
||||
benchmark(std::move(std::string("boost::pool cr. & destr.")), []() {
|
||||
for (size_t i = 0; i < N / 100; ++i) {
|
||||
if (__builtin_expect(!(i & 3), 0)) {
|
||||
boost::object_pool<Big> p;
|
||||
for (size_t j = 0; j < 100; ++j) {
|
||||
Big *o = p.malloc();
|
||||
touch_obj(o);
|
||||
}
|
||||
} else {
|
||||
boost::object_pool<Small> p;
|
||||
for (size_t j = 0; j < 100; ++j) {
|
||||
Small *o = p.malloc();
|
||||
touch_obj(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
* malloc/free
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
template<class T>
|
||||
void
|
||||
benchmark_mallocfree()
|
||||
{
|
||||
benchmark(std::move(T() + "mallocfree"), [&]() {
|
||||
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T*)malloc(sizeof(T));
|
||||
ptrs[i] = o;
|
||||
touch_obj(o);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
free(ptrs[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_mallocfree_free()
|
||||
{
|
||||
benchmark(std::move(T() + "mallocfree w/ free"), [&]() {
|
||||
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T*)malloc(sizeof(T));
|
||||
ptrs[i] = o;
|
||||
touch_obj(o);
|
||||
|
||||
if (__builtin_expect(!(i & 3), 0)) {
|
||||
free(o);
|
||||
ptrs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
free(ptrs[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
* GLib slices
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
#ifdef USE_GLIB
|
||||
template<class T>
|
||||
void
|
||||
benchmark_glib_slices()
|
||||
{
|
||||
benchmark(std::move(T() + "GLib slices"), [&]() {
|
||||
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T*)g_slice_alloc(sizeof(T));
|
||||
ptrs[i] = o;
|
||||
touch_obj(o);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
g_slice_free1(sizeof(T), ptrs[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_glib_slices_free()
|
||||
{
|
||||
benchmark(std::move(T() + "GLib slices w/ free"), [&]() {
|
||||
static __thread T *ptrs[N * sizeof(Small) / sizeof(T)];
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T*)g_slice_alloc(sizeof(T));
|
||||
ptrs[i] = o;
|
||||
touch_obj(o);
|
||||
|
||||
if (__builtin_expect(!(i & 3), 0)) {
|
||||
g_slice_free1(sizeof(T), o);
|
||||
ptrs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
g_slice_free1(sizeof(T), ptrs[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
* Nginx pool
|
||||
* Simplified version of src/core/ngx_palloc.c from nginx-1.9.5 with
|
||||
* some code adjustments to be built in this benchmark.
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
struct ngx_pool_t;
|
||||
typedef uintptr_t ngx_uint_t;
|
||||
|
||||
#define NGX_POOL_ALIGNMENT 16
|
||||
#define NGX_ALIGNMENT sizeof(unsigned long)
|
||||
#define ngx_align_ptr(p, a) \
|
||||
(u_char *)(((uintptr_t)(p) + ((uintptr_t)a - 1)) & ~((uintptr_t)a - 1))
|
||||
#define NGX_MAX_ALLOC_FROM_POOL ((size_t)PAGE_SIZE - 1)
|
||||
|
||||
struct ngx_pool_data_t {
|
||||
unsigned char *last;
|
||||
unsigned char *end;
|
||||
ngx_pool_t *next;
|
||||
unsigned int failed;
|
||||
};
|
||||
|
||||
struct ngx_pool_large_t {
|
||||
ngx_pool_large_t *next;
|
||||
void *alloc;
|
||||
};
|
||||
|
||||
struct ngx_pool_t {
|
||||
ngx_pool_data_t d;
|
||||
size_t max;
|
||||
ngx_pool_t *current;
|
||||
ngx_pool_large_t *large;
|
||||
};
|
||||
|
||||
ngx_pool_t *
|
||||
ngx_create_pool(size_t size)
|
||||
{
|
||||
ngx_pool_t *p;
|
||||
|
||||
if (posix_memalign((void **)&p, NGX_POOL_ALIGNMENT, size))
|
||||
return NULL;
|
||||
|
||||
p->d.last = (unsigned char *)p + sizeof(ngx_pool_t);
|
||||
p->d.end = (unsigned char *)p + size;
|
||||
p->d.next = NULL;
|
||||
p->d.failed = 0;
|
||||
|
||||
size = size - sizeof(ngx_pool_t);
|
||||
p->max = (size < NGX_MAX_ALLOC_FROM_POOL)
|
||||
? size : NGX_MAX_ALLOC_FROM_POOL;
|
||||
|
||||
p->current = p;
|
||||
p->large = NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
ngx_destroy_pool(ngx_pool_t *pool)
|
||||
{
|
||||
ngx_pool_t *p, *n;
|
||||
ngx_pool_large_t *l;
|
||||
|
||||
for (l = pool->large; l; l = l->next) {
|
||||
if (l->alloc)
|
||||
free(l->alloc);
|
||||
}
|
||||
|
||||
for (p = pool, n = pool->d.next; ; p = n, n = n->d.next) {
|
||||
free(p);
|
||||
if (n == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
ngx_palloc_block(ngx_pool_t *pool, size_t size)
|
||||
{
|
||||
unsigned char *m;
|
||||
size_t psize = (size_t)(pool->d.end - (unsigned char *)pool);
|
||||
ngx_pool_t *p, *p_new;
|
||||
|
||||
if (posix_memalign((void **)&m, NGX_POOL_ALIGNMENT, psize))
|
||||
return NULL;
|
||||
p_new = (ngx_pool_t *)m;
|
||||
|
||||
p_new->d.end = m + psize;
|
||||
p_new->d.next = NULL;
|
||||
p_new->d.failed = 0;
|
||||
|
||||
m += sizeof(ngx_pool_data_t);
|
||||
m = ngx_align_ptr(m, NGX_ALIGNMENT);
|
||||
p_new->d.last = m + size;
|
||||
|
||||
for (p = pool->current; p->d.next; p = p->d.next) {
|
||||
if (p->d.failed++ > 4)
|
||||
pool->current = p->d.next;
|
||||
}
|
||||
p->d.next = p_new;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void *ngx_palloc(ngx_pool_t *pool, size_t size);
|
||||
|
||||
static void *
|
||||
ngx_palloc_large(ngx_pool_t *pool, size_t size)
|
||||
{
|
||||
void *p;
|
||||
unsigned int n = 0;
|
||||
ngx_pool_large_t *large;
|
||||
|
||||
p = malloc(size);
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
|
||||
for (large = pool->large; large; large = large->next) {
|
||||
if (large->alloc == NULL) {
|
||||
large->alloc = p;
|
||||
return p;
|
||||
}
|
||||
if (n++ > 3)
|
||||
break;
|
||||
}
|
||||
|
||||
large = (ngx_pool_large_t *)ngx_palloc(pool, sizeof(ngx_pool_large_t));
|
||||
if (large == NULL) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
large->alloc = p;
|
||||
large->next = pool->large;
|
||||
pool->large = large;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
ngx_palloc(ngx_pool_t *pool, size_t size)
|
||||
{
|
||||
unsigned char *m;
|
||||
ngx_pool_t *p;
|
||||
|
||||
if (size <= pool->max) {
|
||||
p = pool->current;
|
||||
do {
|
||||
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
|
||||
if ((size_t)(p->d.end - m) >= size) {
|
||||
p->d.last = m + size;
|
||||
return m;
|
||||
}
|
||||
p = p->d.next;
|
||||
} while (p);
|
||||
|
||||
return ngx_palloc_block(pool, size);
|
||||
}
|
||||
|
||||
return ngx_palloc_large(pool, size);
|
||||
}
|
||||
|
||||
void
|
||||
ngx_pfree(ngx_pool_t *pool, void *p)
|
||||
{
|
||||
ngx_pool_large_t *l;
|
||||
|
||||
for (l = pool->large; l; l = l->next) {
|
||||
if (p == l->alloc) {
|
||||
free(l->alloc);
|
||||
l->alloc = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_ngx_pool()
|
||||
{
|
||||
ngx_pool_t *p = ngx_create_pool(PAGE_SIZE);
|
||||
assert(p);
|
||||
|
||||
benchmark(std::move(T() + "ngx_pool"), [=]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T *)ngx_palloc(p, sizeof(T));
|
||||
touch_obj(o);
|
||||
}
|
||||
});
|
||||
|
||||
ngx_destroy_pool(p);
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_ngx_pool_mix_free()
|
||||
{
|
||||
ngx_pool_t *p = ngx_create_pool(PAGE_SIZE);
|
||||
assert(p);
|
||||
|
||||
benchmark(std::move(std::string("ngx_pool w/ free (Mix)")), [=]() {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (__builtin_expect(!(i & 0xfff), 0)) {
|
||||
Huge *o;
|
||||
o = (Huge *)ngx_palloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
ngx_pfree(p, o);
|
||||
}
|
||||
else if (__builtin_expect(!(i & 3), 0)) {
|
||||
Big *o = (Big *)ngx_palloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
ngx_pfree(p, o);
|
||||
}
|
||||
else {
|
||||
Small *o = (Small *)ngx_palloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
ngx_pfree(p, o);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ngx_destroy_pool(p);
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_ngx_pool_create_and_destroy()
|
||||
{
|
||||
benchmark(std::move(std::string("ngx_pool cr. & destr.")), []() {
|
||||
for (size_t i = 0; i < N / 100; ++i) {
|
||||
ngx_pool_t *p = ngx_create_pool(PAGE_SIZE);
|
||||
for (size_t j = 0; j < 100; ++j) {
|
||||
if (__builtin_expect(!(i & 3), 0)) {
|
||||
Big *o;
|
||||
o = (Big *)ngx_palloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
} else {
|
||||
Small *o;
|
||||
o = (Small *)ngx_palloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
}
|
||||
}
|
||||
ngx_destroy_pool(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
* Tempesta FW pool
|
||||
* Simplified and ported to user-space from
|
||||
* https://github.com/natsys/tempesta/blob/master/tempesta_fw/pool.c
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* Emulation of Linux buddy allocator.
|
||||
*/
|
||||
#define GFP_ATOMIC 0
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
#define likely(x) __builtin_expect(x, 1)
|
||||
#define unlikely(x) __builtin_expect(x, 0)
|
||||
#define free_pages(p, o) free((void *)(p))
|
||||
#define get_order(n) (assert((n) < PAGE_SIZE * 128), \
|
||||
(n) < PAGE_SIZE ? 0 \
|
||||
: (n) < PAGE_SIZE * 2 ? 1 \
|
||||
: (n) < PAGE_SIZE * 4 ? 2 \
|
||||
: (n) < PAGE_SIZE * 8 ? 3 \
|
||||
: (n) < PAGE_SIZE * 16 ? 4 \
|
||||
: (n) < PAGE_SIZE * 32 ? 5 \
|
||||
: (n) < PAGE_SIZE * 64 ? 6 \
|
||||
: 7)
|
||||
|
||||
static inline unsigned long
|
||||
__get_free_pages(int flags __attribute__((unused)), int order)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (posix_memalign((void **)&p, PAGE_SIZE, PAGE_SIZE << order))
|
||||
return 0;
|
||||
|
||||
return (unsigned long)p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory pool chunk descriptor.
|
||||
*
|
||||
* @next - pointer to next memory chunk;
|
||||
* @order - order of number of pages in the chunk;
|
||||
* @off - current chunk offset;
|
||||
*/
|
||||
struct TfwPoolChunk {
|
||||
TfwPoolChunk *next;
|
||||
unsigned int order;
|
||||
unsigned int off;
|
||||
};
|
||||
|
||||
/**
|
||||
* Memory pool descriptor.
|
||||
*
|
||||
* @curr - current chunk to allocate memory from;
|
||||
* @order,@off - cached members of @curr;
|
||||
*/
|
||||
typedef struct {
|
||||
TfwPoolChunk *curr;
|
||||
unsigned int order;
|
||||
unsigned int off;
|
||||
} TfwPool;
|
||||
|
||||
#define TFW_POOL_CHUNK_SZ(p) (PAGE_SIZE << (p)->order)
|
||||
#define TFW_POOL_CHUNK_BASE(c) ((unsigned long)(c) & PAGE_MASK)
|
||||
#define TFW_POOL_CHUNK_END(p) (TFW_POOL_CHUNK_BASE((p)->curr) + (p)->off)
|
||||
#define TFW_POOL_ALIGN_SZ(n) (((n) + 7) & ~7UL)
|
||||
#define TFW_POOL_HEAD_OFF (TFW_POOL_ALIGN_SZ(sizeof(TfwPool)) \
|
||||
+ TFW_POOL_ALIGN_SZ(sizeof(TfwPoolChunk)))
|
||||
#define TFW_POOL_PGCACHE_SZ 256
|
||||
|
||||
static unsigned int pg_next;
|
||||
static unsigned long pg_cache[TFW_POOL_PGCACHE_SZ];
|
||||
|
||||
static unsigned long
|
||||
tfw_pool_alloc_pages(unsigned int order)
|
||||
{
|
||||
if (likely(pg_next && !order))
|
||||
return pg_cache[--pg_next];
|
||||
return __get_free_pages(GFP_ATOMIC, order);
|
||||
}
|
||||
|
||||
static void
|
||||
tfw_pool_free_pages(unsigned long addr, unsigned int order)
|
||||
{
|
||||
if (likely(pg_next < TFW_POOL_PGCACHE_SZ && !order)) {
|
||||
pg_cache[pg_next++] = addr;
|
||||
} else {
|
||||
free_pages(addr, order);
|
||||
}
|
||||
}
|
||||
|
||||
TfwPool *
|
||||
__tfw_pool_new(size_t n)
|
||||
{
|
||||
TfwPool *p;
|
||||
TfwPoolChunk *c;
|
||||
unsigned int order;
|
||||
|
||||
order = get_order(TFW_POOL_ALIGN_SZ(n) + TFW_POOL_HEAD_OFF);
|
||||
|
||||
c = (TfwPoolChunk *)tfw_pool_alloc_pages(order);
|
||||
if (unlikely(!c))
|
||||
return NULL;
|
||||
|
||||
p = (TfwPool *)((char *)c + TFW_POOL_ALIGN_SZ(sizeof(*c)));
|
||||
c->next = NULL;
|
||||
|
||||
p->order = order;
|
||||
p->off = TFW_POOL_HEAD_OFF;
|
||||
p->curr = c;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
tfw_pool_alloc(TfwPool *p, size_t n)
|
||||
{
|
||||
void *a;
|
||||
|
||||
n = TFW_POOL_ALIGN_SZ(n);
|
||||
|
||||
if (unlikely(p->off + n > TFW_POOL_CHUNK_SZ(p))) {
|
||||
TfwPoolChunk *c, *curr = p->curr;
|
||||
unsigned int off = TFW_POOL_ALIGN_SZ(sizeof(TfwPoolChunk)) + n;
|
||||
unsigned int order = get_order(off);
|
||||
|
||||
c = (TfwPoolChunk *)tfw_pool_alloc_pages(order);
|
||||
if (!c)
|
||||
return NULL;
|
||||
c->next = curr;
|
||||
|
||||
curr->order = p->order;
|
||||
curr->off = p->off;
|
||||
|
||||
p->order = order;
|
||||
p->off = off;
|
||||
p->curr = c;
|
||||
|
||||
return (void *)TFW_POOL_ALIGN_SZ((unsigned long)(c + 1));
|
||||
}
|
||||
|
||||
a = (char *)TFW_POOL_CHUNK_END(p);
|
||||
p->off += n;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
tfw_pool_free(TfwPool *p, void *ptr, size_t n)
|
||||
{
|
||||
n = TFW_POOL_ALIGN_SZ(n);
|
||||
|
||||
/* Stack-like usage is expected. */
|
||||
if (unlikely((char *)ptr + n != (char *)TFW_POOL_CHUNK_END(p)))
|
||||
return;
|
||||
|
||||
p->off -= n;
|
||||
|
||||
/* Free empty chunk which doesn't contain the pool header. */
|
||||
if (unlikely(p->off == TFW_POOL_ALIGN_SZ(sizeof(TfwPoolChunk)))) {
|
||||
TfwPoolChunk *next = p->curr->next;
|
||||
tfw_pool_free_pages(TFW_POOL_CHUNK_BASE(p->curr), p->order);
|
||||
p->curr = next;
|
||||
p->order = next->order;
|
||||
p->off = next->off;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tfw_pool_destroy(TfwPool *p)
|
||||
{
|
||||
TfwPoolChunk *c, *next;
|
||||
|
||||
for (c = p->curr; c; c = next) {
|
||||
next = c->next;
|
||||
tfw_pool_free_pages(TFW_POOL_CHUNK_BASE(c), c->order);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_tfw_pool()
|
||||
{
|
||||
TfwPool *p = __tfw_pool_new(0);
|
||||
assert(p);
|
||||
|
||||
benchmark(std::move(T() + "tfw_pool"), [=]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T *)tfw_pool_alloc(p, sizeof(T));
|
||||
touch_obj(o);
|
||||
}
|
||||
});
|
||||
|
||||
tfw_pool_destroy(p);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_tfw_pool_free()
|
||||
{
|
||||
TfwPool *p = __tfw_pool_new(0);
|
||||
assert(p);
|
||||
|
||||
benchmark(std::move(T() + "tfw_pool w/ free"), [&]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(T); ++i) {
|
||||
T *o = (T *)tfw_pool_alloc(p, sizeof(T));
|
||||
touch_obj(o);
|
||||
if (__builtin_expect(!(i & 3), 0))
|
||||
tfw_pool_free(p, o, sizeof(T));
|
||||
}
|
||||
});
|
||||
|
||||
tfw_pool_destroy(p);
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_tfw_pool_mix_free()
|
||||
{
|
||||
TfwPool *p = __tfw_pool_new(0);
|
||||
assert(p);
|
||||
|
||||
benchmark(std::move(std::string("tfw_pool w/ free (Mix)")), [=]() {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (__builtin_expect(!(i & 0xfff), 0)) {
|
||||
Huge *o;
|
||||
o = (Huge *)tfw_pool_alloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
tfw_pool_free(p, o, sizeof(*o));
|
||||
}
|
||||
else if (__builtin_expect(!(i & 3), 0)) {
|
||||
Big *o = (Big *)tfw_pool_alloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
tfw_pool_free(p, o, sizeof(*o));
|
||||
}
|
||||
else {
|
||||
Small *o;
|
||||
o = (Small *)tfw_pool_alloc(p, sizeof(*o));
|
||||
touch_obj(o);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tfw_pool_destroy(p);
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_tfw_pool_create_and_destroy()
|
||||
{
|
||||
benchmark(std::move(std::string("tfw_pool cr. & destr.")), []() {
|
||||
for (size_t i = 0; i < N / 100; ++i) {
|
||||
TfwPool *p = __tfw_pool_new(0);
|
||||
for (size_t j = 0; j < 100; ++j) {
|
||||
if (__builtin_expect(!(i & 3), 0)) {
|
||||
Big *o;
|
||||
o = (Big *)tfw_pool_alloc(p,
|
||||
sizeof(*o));
|
||||
touch_obj(o);
|
||||
} else {
|
||||
Small *o;
|
||||
o = (Small *)tfw_pool_alloc(p,
|
||||
sizeof(*o));
|
||||
touch_obj(o);
|
||||
}
|
||||
}
|
||||
tfw_pool_destroy(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/* Adding M*LIB mempool */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "m-mempool.h"
|
||||
|
||||
MEMPOOL_DEF(mempool_small, Small)
|
||||
MEMPOOL_DEF(mempool_big, Big)
|
||||
MEMPOOL_DEF(mempool_huge, Huge)
|
||||
|
||||
void
|
||||
benchmark_mempool_small()
|
||||
{
|
||||
mempool_small_t p;
|
||||
mempool_small_init (p);
|
||||
mempool_small_t *ptr = &p; // C++ lambda !
|
||||
|
||||
benchmark(std::move(Small() + "M*LIB mempool"), [=]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(Small); ++i) {
|
||||
Small *o = mempool_small_alloc(*ptr);
|
||||
touch_obj(o);
|
||||
}
|
||||
});
|
||||
|
||||
mempool_small_clear(p);
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_mempool_big()
|
||||
{
|
||||
mempool_big_t p;
|
||||
mempool_big_init (p);
|
||||
mempool_big_t *ptr = &p; // C++ lambda !
|
||||
|
||||
benchmark(std::move(Big() + "M*LIB mempool"), [=]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(Big); ++i) {
|
||||
Big *o = mempool_big_alloc(*ptr);
|
||||
touch_obj(o);
|
||||
}
|
||||
});
|
||||
|
||||
mempool_big_clear(p);
|
||||
}
|
||||
|
||||
void
|
||||
benchmark_mempool_mix_free()
|
||||
{
|
||||
mempool_small_t s, *ptr_s = &s;
|
||||
mempool_big_t b, *ptr_b = &b;
|
||||
mempool_huge_t h, *ptr_h = &h;
|
||||
|
||||
mempool_small_init (s);
|
||||
mempool_big_init (b);
|
||||
mempool_huge_init (h);
|
||||
|
||||
benchmark(std::move(std::string("M*LIB mempool w/ free (Mix)")), [=]() {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (__builtin_expect(!(i & 0xfff), 0)) {
|
||||
Huge *o = mempool_huge_alloc (*ptr_h);
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
mempool_huge_free (*ptr_h, o);
|
||||
}
|
||||
else if (__builtin_expect(!(i & 3), 0)) {
|
||||
Big *o = mempool_big_alloc(*ptr_b);
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
mempool_big_free (*ptr_b, o);
|
||||
}
|
||||
else {
|
||||
Small *o = mempool_small_alloc (*ptr_s);
|
||||
touch_obj(o);
|
||||
if (!(i & 1))
|
||||
mempool_small_free (*ptr_s, o);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mempool_small_clear (s);
|
||||
mempool_big_clear (b);
|
||||
mempool_huge_clear (h);
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
* Main part of the benchmark
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
main()
|
||||
{
|
||||
std::cout << std::setw(35) << std::right << "small object size: "
|
||||
<< sizeof(Small) << std::endl;
|
||||
std::cout << std::setw(35) << std::right << "big object size: "
|
||||
<< sizeof(Big) << std::endl;
|
||||
std::cout << std::setw(35) << std::right << "huge object size: "
|
||||
<< sizeof(Huge) << "\n" << std::endl;
|
||||
|
||||
// Warm up malloc().
|
||||
static const size_t n = N * sizeof(Big);
|
||||
void *p = malloc(n);
|
||||
assert(p);
|
||||
memset(p, 0, n);
|
||||
free(p);
|
||||
|
||||
benchmark_mallocfree<Small>();
|
||||
benchmark_mallocfree_free<Small>();
|
||||
benchmark_mallocfree<Big>();
|
||||
benchmark_mallocfree_free<Big>();
|
||||
std::cout << std::endl;
|
||||
|
||||
#ifdef USE_GLIB
|
||||
benchmark_glib_slices<Small>();
|
||||
benchmark_glib_slices_free<Small>();
|
||||
benchmark_glib_slices<Big>();
|
||||
benchmark_glib_slices_free<Big>();
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
|
||||
#ifdef USE_BOOST
|
||||
benchmark_boost_pool<Small>();
|
||||
benchmark_boost_pool_free<Small>();
|
||||
benchmark_boost_pool<Big>();
|
||||
benchmark_boost_pool_free<Big>();
|
||||
benchmark_boost_pool_create_and_destroy();
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
|
||||
benchmark_ngx_pool<Small>();
|
||||
benchmark_ngx_pool<Big>();
|
||||
benchmark_ngx_pool_mix_free();
|
||||
benchmark_ngx_pool_create_and_destroy();
|
||||
std::cout << std::endl;
|
||||
|
||||
benchmark_mempool_small();
|
||||
benchmark_mempool_big();
|
||||
benchmark_mempool_mix_free();
|
||||
std::cout << std::endl;
|
||||
|
||||
benchmark_tfw_pool<Small>();
|
||||
benchmark_tfw_pool_free<Small>();
|
||||
benchmark_tfw_pool<Big>();
|
||||
benchmark_tfw_pool_free<Big>();
|
||||
benchmark_tfw_pool_mix_free();
|
||||
benchmark_tfw_pool_create_and_destroy();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
CC= gcc
|
||||
CXX= g++
|
||||
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
|
||||
CXXFLAGS= $(CFLAGS)
|
||||
LDFLAGS=
|
||||
OBJS=
|
||||
LIBS= -lm
|
||||
|
||||
all: run
|
||||
|
||||
benchmarks:
|
||||
git clone https://github.com/attractivechaos/benchmarks.git
|
||||
|
||||
build: benchmarks
|
||||
mkdir -p benchmarks/udb/mlib
|
||||
cp test-mlib.c benchmarks/udb/mlib/test.c
|
||||
cp Makefile-mlib benchmarks/udb/mlib/Makefile
|
||||
mkdir -p benchmarks/udb/mlib-oa
|
||||
cp test-mlib-oa.c benchmarks/udb/mlib-oa/test.c
|
||||
cp Makefile-mlib benchmarks/udb/mlib-oa/Makefile
|
||||
touch build
|
||||
|
||||
all-recur: build
|
||||
cd benchmarks/udb; \
|
||||
wdir=`pwd`; \
|
||||
list_c=`find . -name 'test.c'`; \
|
||||
list_cc=`find . -name 'test.cc'`; \
|
||||
for t in $$list_c $$list_cc ; do \
|
||||
subdir=`dirname $$t`; \
|
||||
cd $$subdir; \
|
||||
$(MAKE) CC="$(CC)" CXX="$(CXX)" DFLAGS="$(DFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" || exit 1; \
|
||||
cd $$wdir; \
|
||||
done;
|
||||
|
||||
run: all-recur
|
||||
@cd benchmarks/udb; \
|
||||
echo ; echo "LIBRARY | INT TIME | STR TIME" ; \
|
||||
for i in `find . -name 'test'` ; do \
|
||||
printf "%s|" `dirname $$i|sed -e 's+./++g'`; \
|
||||
$$i -is | awk ' \
|
||||
/\[int data\] # element/ { nbInt= $$5 } \
|
||||
/\[int data\] CPU time/ {timeInt= $$5 } \
|
||||
/\[str data\] # element/ { nbStr= $$5 } \
|
||||
/\[str data\] CPU time/ {timeStr= $$5 } \
|
||||
END { \
|
||||
if (nbInt != 624444) timeInt="BRK" ; \
|
||||
if (nbStr != 624444) timeStr="BRK" ; \
|
||||
print timeInt,"|" ,timeStr \
|
||||
}' ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf benchmarks build
|
||||
@@ -1,22 +0,0 @@
|
||||
CC= gcc
|
||||
CFLAGS= -g -Wall -O2 -march=native
|
||||
OBJS= ../benchmark/benchmark.o ../runit/runlib.o test.o
|
||||
DFLAGS=
|
||||
INCLUDES= -I../benchmark -I../runit -I../../../../..
|
||||
LIBS=
|
||||
|
||||
.SUFFIXES:.c .o
|
||||
|
||||
.c.o:
|
||||
$(CC) -std=gnu11 -DNDEBUG -c $(INCLUDES) $(CFLAGS) $(DFLAGS) $< -o $@
|
||||
|
||||
all:test
|
||||
|
||||
test:$(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
cleanlocal:
|
||||
rm -f *.o a.out *~ test
|
||||
|
||||
clean:cleanlocal
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-mempool.h"
|
||||
|
||||
static inline bool oor_equal_p(int k, unsigned char n)
|
||||
{
|
||||
return k == (int)-n - 1;
|
||||
}
|
||||
|
||||
static inline void oor_set(int *k, unsigned char n)
|
||||
{
|
||||
*k = (int)-n - 1;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_int,
|
||||
int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
static const char *const oor[2] = { "EMPTY", "DELETED" };
|
||||
|
||||
static inline bool str_oor_equal_p(const char *k, unsigned char n)
|
||||
{
|
||||
return k == oor[n];
|
||||
}
|
||||
|
||||
static inline void str_oor_set(const char * *k, unsigned char n)
|
||||
{
|
||||
*k = oor[n];
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_str, const char *,
|
||||
M_OPEXTEND (M_CSTR_OPLIST, OOR_EQUAL(str_oor_equal_p), OOR_SET(str_oor_set M_IPTR)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
|
||||
int test_int(int N, const unsigned *data)
|
||||
{
|
||||
int ret;
|
||||
M_LET(h, DICT_OPLIST(dict_int)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_int_get (h, data[i]) == NULL) {
|
||||
dict_int_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_int_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_int_size(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_str(int N, char * const *data)
|
||||
{
|
||||
int ret;
|
||||
M_LET(h, DICT_OPLIST(dict_str)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_str_get (h, data[i]) == NULL) {
|
||||
dict_str_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_str_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_str_size(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return udb_benchmark(argc, argv, test_int, test_str);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-mempool.h"
|
||||
|
||||
DICT_STOREHASH_DEF2(dict_int,
|
||||
int, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(mpool_int), MEMPOOL_LINKAGE(static)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_str,
|
||||
const char *, M_OPEXTEND (M_CSTR_OPLIST, MEMPOOL(mpool_str), MEMPOOL_LINKAGE(static)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
int test_int(int N, const unsigned *data)
|
||||
{
|
||||
int ret;
|
||||
dict_int_list_pair_mempool_init(mpool_int);
|
||||
M_LET(h, DICT_OPLIST(dict_int)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_int_get (h, data[i]) == NULL) {
|
||||
dict_int_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_int_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_int_size(h);
|
||||
}
|
||||
dict_int_list_pair_mempool_clear(mpool_int);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_str(int N, char * const *data)
|
||||
{
|
||||
int ret;
|
||||
dict_str_list_pair_mempool_init(mpool_str);
|
||||
M_LET(h, DICT_OPLIST(dict_str)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_str_get (h, data[i]) == NULL) {
|
||||
dict_str_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_str_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_str_size(h);
|
||||
}
|
||||
dict_str_list_pair_mempool_clear(mpool_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return udb_benchmark(argc, argv, test_int, test_str);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
CC= gcc
|
||||
CXX= g++
|
||||
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
|
||||
CXXFLAGS= $(CFLAGS)
|
||||
LDFLAGS=
|
||||
OBJS=
|
||||
LIBS=
|
||||
|
||||
all: run
|
||||
|
||||
udb2:
|
||||
git clone https://github.com/attractivechaos/udb2.git
|
||||
|
||||
build: udb2
|
||||
mkdir -p udb2/mlib
|
||||
cp test-mlib.c udb2/mlib/test.c
|
||||
cp Makefile-mlib udb2/mlib/Makefile
|
||||
touch build
|
||||
|
||||
all-recur: build
|
||||
cd udb2/stb && make CC="gcc -lm"
|
||||
cd udb2 && for dir in `ls | egrep -v '^(Makefile|common.c|_)'`; do cd $$dir && make && cd -; done
|
||||
|
||||
run: all-recur
|
||||
@cd udb2; \
|
||||
echo ; echo RUNNING BENCH ; echo ; \
|
||||
for i in `find . -name 'run-test'` ; do \
|
||||
echo ; echo `dirname $$i|sed -e 's+./++g'`; \
|
||||
$$i ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf udb2 build *~
|
||||
@@ -1,8 +0,0 @@
|
||||
all:run-test
|
||||
|
||||
run-test:test.c ../common.c
|
||||
$(CC) -std=gnu99 -O2 -Wall -I../../../.. $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f run-test* *~
|
||||
rm -rf mlib
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "../common.c"
|
||||
#define M_USE_DICT_OA_PROBING(s) 1
|
||||
#include "m-dict.h"
|
||||
|
||||
static inline bool oor_equal_p(unsigned int k, unsigned char n)
|
||||
{
|
||||
return k == (unsigned int)n;
|
||||
}
|
||||
static inline void oor_set(unsigned int *k, unsigned char n)
|
||||
{
|
||||
*k = (unsigned int)n;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_oa_uint,
|
||||
unsigned int, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(hash_fn), OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
unsigned int, M_DEFAULT_OPLIST)
|
||||
|
||||
uint32_t test_int(uint32_t n, uint32_t x0)
|
||||
{
|
||||
uint32_t i, x, z = 0;
|
||||
dict_oa_uint_t h;
|
||||
dict_oa_uint_init(h);
|
||||
for (i = 0, x = x0; i < n; ++i) {
|
||||
x = hash32(x);
|
||||
unsigned int key = get_key(n, x);
|
||||
unsigned int *ptr = dict_oa_uint_get_at(h, key);
|
||||
(*ptr)++;
|
||||
z += *ptr;
|
||||
}
|
||||
fprintf(stderr, "# unique keys: %d; checksum: %u\n", (int) dict_oa_uint_size(h), z);
|
||||
z = dict_oa_uint_size(h);
|
||||
dict_oa_uint_clear(h);
|
||||
return z;
|
||||
}
|
||||
+31
-1
@@ -81,4 +81,34 @@ V0.6.*: * Rename M_DEFAULT_OPLIST in M_BASIC_OPLIST
|
||||
so except if you are using the prefixed version of the strings
|
||||
or the bounded strings API, you won't need to upgrade your code.
|
||||
|
||||
V0.7.*: * Rename m-mutex.h header in m-thread.h
|
||||
V0.7.2: * Rename m-mutex.h header in m-thread.h
|
||||
|
||||
V0.7.3 : * DICT_STOREHASH_DEF2 / DICT_STOREHASH_DEF2_AS are deprecated.
|
||||
Use DICT_DEF2 / DICT_DEF2_AS
|
||||
|
||||
V0.8.0 : * Memory interface massive compatibility break to allow more efficient integration of custom allocators.
|
||||
The prototypes of NEW / DEL REALLOC / FREE operators are incompatible:
|
||||
* Add the old number of elements of the array as argument of REALLOC
|
||||
* Add the number of elements of the array and its base type as arguments of FREE.
|
||||
* Add the context argument (which shall be ignored in not specific build)
|
||||
(You can ignore the new arguments which are only hints provided to memory functions).
|
||||
Also impact M_MEMORY_NEW / M_MEMORY_DEL / M_MEMORY_REALLOC & M_MEMORY_FREE
|
||||
M_MEMORY_FULL: replace the failed size allocation size argument into the base type and the number of elements that failed to be allocated.
|
||||
* m-buffer:
|
||||
* non-thread safe queue & stack are moved into their dedicated header (m-queue)
|
||||
* BUFFER_THREAD_UNSAFE policy is no longer supported.
|
||||
* Disabling BUFFER_BLOCKING policy is no longer supported (default policy is now forced).
|
||||
* _push / _pop now returns now void like other containers instead of a bool (it was undocumented)
|
||||
* Remove MEMPOOL support from list & R/B tree
|
||||
* m-mempool: Header is obsolete (and no longer useful at all as lists & RB Tree no longer can use it. To remove). Use new M_USE_CONTEXT interface to pass local pool context.
|
||||
* M*LIB becomes stricter for the INIT_MOVE operator:
|
||||
The Emulation layer using CLEAR+INIT_SET methods, when there is no INIT_MOVE operator, has been removed:
|
||||
either default INIT_MOVE operator (operator '=') is working, or a method is provided for INIT_MOVE or it is disabled.
|
||||
If it is an array of size 1, you can use M_COPY_A1_DEFAULT as a default method.
|
||||
(It's because supporting emulation layer with new memory context parameter was complicated)
|
||||
* Remove obsole headers: m-c-mempool, m-mempool, m-shared, m-i-shared, -mconcurrent
|
||||
|
||||
V0.8.1 : * m-algo: Update interface of _transform to be more flexible: adding user context pointer, and filtering of the input elements.
|
||||
* m-algo: Update interface of _reduce to be more user friendly: return value indicates if the reduction is empty or not.
|
||||
* m-algo: Rename _map_reduce into _transform_reduce and update its interface to be more flexible: adding user context pointer, and filtering of the input elements and return value indicates if the reduction is empty or not.
|
||||
* Rename _push_raw method by _push_back_raw for all containers (clearer on the semantic)
|
||||
+134
-43
@@ -1,26 +1,59 @@
|
||||
*Long term issues*
|
||||
==========================================================
|
||||
|
||||
* TODO #33 : Handling of partially constructed object :ANOMALY:
|
||||
* DONE #34 : Pass old size arguments to memory allocator :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
|
||||
Some custom allocators don't store the old size of an object to maximize memory usage
|
||||
used by the allocated objects. On realloc, they need the old size argument.
|
||||
This is inline with the GMP allocator which provides the old size argument.
|
||||
There is no difficulty in the data structure to compute the old size of the object
|
||||
(it is quite easily available).
|
||||
It is needed for REALLOC operator (and FREE)
|
||||
|
||||
** Solution #1
|
||||
|
||||
Add the oldsize argument to the REALLOC and FREE operators.
|
||||
|
||||
** Solution #2
|
||||
|
||||
Create a local variable m_local_old_size before calling the REALLOC or FREE operators
|
||||
Modify GAIA interface to link OLDSIZE patttern to m_local_old_size
|
||||
Need to handle the warnings correctly.
|
||||
|
||||
** Tradeof analysis
|
||||
|
||||
Solution #1 is simpler but breaks API. However custom allocators are not common.
|
||||
Solution #2 keeps API as it is but increases code complexity by adding more hacks into the codebase.
|
||||
|
||||
* TODO #33 : Handling of partially constructed object :ANOMALY:
|
||||
** State
|
||||
|
||||
Support of partially constructed object needs special code to handle it
|
||||
correctly (register a correct cleanup function in case of exception).
|
||||
|
||||
Current containers don't support such features.
|
||||
|
||||
> NOTE:
|
||||
An exception for init_set supposes that the object is *not* initialized.
|
||||
An exception for set supposes that the object *remains* initialized.
|
||||
Some containers perform an init_set using init+set
|
||||
others perform a set using a clear+init_set
|
||||
The property of init_set (resp. set) is broken if an exception is raised in the set (resp. init_set):
|
||||
the object is not let in the expected uninitialized (resp. initialized) state.
|
||||
|
||||
** Actions
|
||||
|
||||
Add such support in M*LIB containers.
|
||||
Need to analyse each function for execption safety.
|
||||
|
||||
It can be difficult. For example, for array _init_set
|
||||
function due to the copy of the object one per one. Each copied sub object
|
||||
may throw an exception, so we always need to have the container in a correct
|
||||
state so that it can be cleanup.
|
||||
|
||||
* TODO #32 : User specialization fields in the container :ENHANCEMENT:
|
||||
|
||||
* DONE #32 : User specialization fields in the container :ENHANCEMENT:
|
||||
** State
|
||||
|
||||
Some methods of a contained object may need to have some options that
|
||||
@@ -47,24 +80,51 @@ of the GAIA by adding support for identification of specialization like this:
|
||||
==> self shall be the destination?
|
||||
|
||||
- How to initialize the specialization field of the container? provide custom initialization functions? It seems to be the hard point.
|
||||
* INIT: Force a default to NULL ?
|
||||
* INIT_SET: Force the same value than the src? We may want another pool!
|
||||
* INIT_MOVE: Force the same value than the src. Mandatory
|
||||
* MOVE: Assume the same value. Mandatory
|
||||
* SET: Use the already define pool
|
||||
* INIT_WITH: Force a default to NULL?
|
||||
==> init_emplace ?
|
||||
-- How can we handle containers that allocate on initialization? It cannot, so this solution seems broken
|
||||
==> Modify constructor prototype to add allocators (except for MOVE/INIT_MOVE)
|
||||
* INIT(dest) ==> INIT(dest, context)
|
||||
* INIT(dest, src) ==> INIT_SET(dest, context, src)
|
||||
* INIT_WITH(dest, ...) ==> INIT_WITH(dest, context, ...)
|
||||
* INIT emplace(dest [custom]) ==> INIT_emplace(dest, context, [custom])
|
||||
==> How to pass context? Inherited from master container? A CONTEXT may be present in the parent container but the child may not accept CONTEXT... Same than the Heap Allocator is not the same between different types... The allocator of the parent may be different from the one of the child... ==> Force context to be the same ?
|
||||
==> If a type A uses CONTEXT, all containers constructed from A shall use CONTEXT too
|
||||
-- Need to use an oplist for context or force basic type?
|
||||
Use GAIA interface and a local variable with force naming : m_local_context for example.
|
||||
|
||||
* TODO #31 : Uniformize parametrisation options of containers :ENHANCEMENT:
|
||||
** Proposed Solution:
|
||||
|
||||
New operator USER_CONTEXT created. If operator USER_CONTEXT exist:
|
||||
+ add a USER_CONTEXT user_data in the data structure.
|
||||
+ for INIT & INIT_WITH, add a new parameter to the function to pass the context to initialize
|
||||
+ for INIT_SET & INIT_MOVE & MOVE, initialize it with the source object (FIXME: This may not be what we want for INIT_SET if we want to copy an object from an allocator to another)
|
||||
+ for each functions that use an operator that may use the USER_CONTEXT, add a local variable m_local_context and initialize to the user context
|
||||
==> Limit user context to pointer or integer (Good enough)
|
||||
+ Expand GAIA interface to handle specially the term USER_CONTEXT: if it exists, expands it to m_local_context. The user will therefore be able to expand its operator with the user provided local context.
|
||||
+ Update OPLIST of data structure to change the call to INIT & INIT_WITH to use GAIA/USER_CONTEXT if the basic type has a USER_CONTEXT
|
||||
+ To be checked: EMPLACE_TYPE ?
|
||||
+ Create M_USING_CONTEXT to initialize a local variable m_local_context and initalize it with the user context to be used y the M_LET macro:
|
||||
M_USING_CONTEXT(int *, &y)
|
||||
M_LET(x, object_t) {
|
||||
|
||||
}
|
||||
|
||||
Done in branch feature/user-data-v2 with a full working implementation for array
|
||||
|
||||
** Solution limitation:
|
||||
|
||||
INIT & INIT_WITH interface change for the user. This however is an acceptable limitation.
|
||||
|
||||
Adding a user context to each data structure will increase a lot the size of the data structure in case of recursive data structures: For example, an array of string_t. Each string_t will need a user data context for it to work properly, so we'll need N*sizeof(user data context) memory just to make it work. This becomes even more problematic if we chain more data structures.
|
||||
And in general, we use custom allocators to be faster and use less memory: this will defeat the purpose of custom allocators a lot.
|
||||
|
||||
Another limitation is that string_t / bitset_t are not supported correctly. We can make this change globally but it will prevent mixing different codebase that uses the string_t together (one expects a user context, the other not).
|
||||
|
||||
** Alternative #1
|
||||
|
||||
Passing user context arguments to each function call seems to be a little bit better.
|
||||
It might however reduces code optimization as one more register will be user everywhere in the calling chain, which may huge.
|
||||
|
||||
** Alternative #2
|
||||
|
||||
The custom allocator could have a thread default allocator as a global variable (thread attribute). It seems to be a better solution, more scalable.
|
||||
The custom allocator will need to be able to switch quickly between scrach arena and permanent arena. This seems to be easy.
|
||||
|
||||
However switching allocators puts a burden to the user:
|
||||
the user needs to properly be sure to call the destructor with the custom allocator set to be the same as when it was created.
|
||||
This might not even be possible in case of exceptions.
|
||||
|
||||
* TODO #31 : Uniformize parametrization options of containers :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
|
||||
@@ -141,31 +201,33 @@ Currently, each container supports 3 serialization methods:
|
||||
|
||||
Generic serialiation connect the container format to the serialization object constraints.
|
||||
It is done through a vtable. As such there is a performance penality and it avoids proper inlining.
|
||||
It is however quite flexible and decouple the data structure from the serialization format.
|
||||
|
||||
** Evolution
|
||||
|
||||
Old format should be deprecated and shall use the generic serialization interface.
|
||||
Old format should be deprecated and the functions implemented it shall use the generic serialization interface.
|
||||
|
||||
Serialization object shall provide an OPLIST of serialization.
|
||||
For example CORE will provide OLD Format oplist and serial-json will provide JSON format oplist.
|
||||
Serialization object shall provide a special OPLIST of serialization.
|
||||
For example M-CORE will provide OLD Format oplist and M-SERIAL-JSON will provide JSON format oplist.
|
||||
Each oplist will provide the suffix needed for the serialization, and the interface
|
||||
(see already existing interface).
|
||||
|
||||
Then a container will generate specialized serialization methods for each provided oplist.
|
||||
|
||||
Pros:
|
||||
** Pros:
|
||||
|
||||
- Faster
|
||||
- In the M*LIB philosphy: much like other oplist usage.
|
||||
|
||||
Cons:
|
||||
** Cons:
|
||||
|
||||
- compatibility breakage.
|
||||
- increase code complexity.
|
||||
|
||||
Open point:
|
||||
** Open point:
|
||||
|
||||
- how can a user add a new serialization object?
|
||||
- Can we make a generic serialization object to support migration path?
|
||||
- how can a user add a new serialization object? ==> See solution implemented by M-GENERIC.
|
||||
- Can we make a generic serialization object to support migration path? Seems possible.
|
||||
|
||||
** Example
|
||||
|
||||
@@ -224,9 +286,11 @@ of the M_CHAIN_INIT are called.
|
||||
|
||||
* TODO #28 : Separate generation of interface to implementation :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
Enable support for generating an interface only for the headers
|
||||
and an implementation only for the source code.
|
||||
|
||||
** Analysis
|
||||
Try to keep API compatibility
|
||||
==> Only modify renamed macro with M_ prefix by giving a new mandatory
|
||||
argument for such generation.
|
||||
@@ -268,7 +332,7 @@ a fixed size will be good to have too for such interface.
|
||||
- Uniformization of the interface (See #31)
|
||||
- Provide static size for all QUEUE.
|
||||
|
||||
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
|
||||
* DONE #26 : Hash-Map for huge objects :ENHANCEMENT:
|
||||
|
||||
Specialize Hashmap for huge objects on supported hardware:
|
||||
|
||||
@@ -303,19 +367,25 @@ unsigned get_small_hash(int64_t x) {
|
||||
|
||||
Can also (should?) use SIMD to test for several hash entries at the same times
|
||||
In which case a complete new implementation will be needed
|
||||
Note: SIMD doesn't seem to be a win if not handled properly.
|
||||
Since the first guess of a good hashtable shall give the right entry > 90% of the time.
|
||||
So 90% of the times SIMD will pay the cost of reading not needed memory.
|
||||
It might still be a win, but proper tuning needs to be done.
|
||||
|
||||
* TODO #25 : Support of error return model for error handling. :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
Find a way to support error return code for the API in case of allocation
|
||||
failure.
|
||||
|
||||
** Analysis
|
||||
Any service that returns void shall return a "int" (or another type).
|
||||
In case of allocation failure, it shall return an error.
|
||||
M_CALL macro shall stop its execution if the service returns an error code
|
||||
and the error code represents an error (avoid rewritting everything)
|
||||
and throw back the error code (stopping the execution flow).
|
||||
|
||||
Services returning already something shall not be modified.
|
||||
Services returning already something shall not be modified but returns the error code embedded (like a NULL pointer).
|
||||
|
||||
This model should be applied at the container level only and not globally.
|
||||
Different containers may need different levels of error handling.
|
||||
@@ -367,19 +437,22 @@ RETCODE/RETCODE
|
||||
|
||||
If really needed, the macro can be avoided and code can be hand written.
|
||||
|
||||
Open points:
|
||||
** Open points:
|
||||
|
||||
- How to handle warnings on unused labels?
|
||||
- What about M_LET / M_EACH? Maybe only supports those.
|
||||
|
||||
* TODO #24 : New MIN-MAX-HEAP container :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
See https://en.wikipedia.org/wiki/Min-max_heap
|
||||
as DPRIORITY_QUEUE_DEF ?
|
||||
|
||||
** Analysis
|
||||
NOTE: Needs for such container?
|
||||
On hold until a user needs it.
|
||||
|
||||
* TODO #23 : Strict MOVE semantic to clarify :ENHANCEMENT:
|
||||
* DONE #23 : Strict MOVE semantic to clarify :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
|
||||
@@ -387,7 +460,7 @@ Some type may need to have a force MOVE semantic (for example, they can store
|
||||
pointer to themselves). Currently the INIT_MOVE & MOVE operators are more
|
||||
a help for performance than a strict semantic usage.
|
||||
|
||||
** ARRAY container
|
||||
** ARRAY container constraint
|
||||
|
||||
The ARRAY container doesn't support strict MOVE semantic for example.
|
||||
It is not a simple matter as it performs a realloc of the table, thus
|
||||
@@ -410,9 +483,9 @@ For example for tuple, it shall
|
||||
|
||||
** DO_INIT_MOVE operator
|
||||
|
||||
DO_INIT_MOVE macro is not also fully working for structure
|
||||
DO_INIT_MOVE macro is also fully working for structure
|
||||
defined with [1] tricks but without an explicit INIT_MOVE / MOVE
|
||||
operators as it uses MOVE_DEFAULT which is not (fully compatible).
|
||||
operators as it uses MOVE_DEFAULT which is not compatible.
|
||||
==> Analyse limitation and possible constraint usages.
|
||||
|
||||
Being able to define a correct default for INIT_MOVE will be really good
|
||||
@@ -426,12 +499,13 @@ will transform the argument to T*, and the type of the argument doesn't match
|
||||
what is expecting resulting in a move of the pointers, not a move of the design data.
|
||||
|
||||
Defining this type seems possible with C11 _Generic and a TYPE in the oplist,
|
||||
but without C11 _Generic I don't see any way to define such macro.
|
||||
but without C11 _Generic I don't see any way to define such macro
|
||||
and we still need to target C99 for such basic feature.
|
||||
|
||||
Without a way to write such a macro, the ticket seems pretty much a dead end.
|
||||
|
||||
* TODO #20 : New: Bucket priority queue :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
Add a new kind of priority queue.
|
||||
See https://en.wikipedia.org/wiki/Bucket_queue
|
||||
|
||||
@@ -446,19 +520,33 @@ except that we can scan 64 entries at a time).
|
||||
Check if we can use BITSET, or introduce fixed size BITSET or use ad-hock
|
||||
implementation.
|
||||
|
||||
* TODO #19 : New: Intrusive Red Black Tree :ENHANCEMENT:
|
||||
** Analysis
|
||||
NOTE: Needs for such container?
|
||||
On hold until a user needs it.
|
||||
|
||||
* CANC #19 : New: Intrusive Red Black Tree :ENHANCEMENT:
|
||||
** State
|
||||
Add intrusive red black tree.
|
||||
Look also for AVL tree (NOTE: Is there a performance difference between the two?)
|
||||
|
||||
* TODO #18 : Missing methods :ENHANCEMENT:
|
||||
** Analysis
|
||||
Only needed for unmovable objects for which B+Tree cannot do the job.
|
||||
But standard Read/Black Tree will do the job just fine.
|
||||
There is really no need for it.
|
||||
==> Cancelled
|
||||
|
||||
* TODO #18 : Missing methods :ENHANCEMENT:
|
||||
** State
|
||||
Some containers don't have all the methods they should.
|
||||
See the cells in yellow here:
|
||||
http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Container.html
|
||||
|
||||
** Analysis
|
||||
Analyzed each missing methods and fill in the gap
|
||||
|
||||
* TODO #17 : New: Ressource handler :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
A global 'ressouce handler' which shall associated a unique handle to a ressource.
|
||||
The handle shall be unique for the ressource and shall not be reused.
|
||||
It is typically a 64 bits integers always incremented (even if the program
|
||||
@@ -484,8 +572,9 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
|
||||
|
||||
How to handle multiple resource ?
|
||||
|
||||
* variant: Pro : easy. Con: Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
|
||||
* embedded the type in the ressource handler: Con: more work, API more complex. Pro: Memory usage seems better.
|
||||
** Analysis
|
||||
- use of variant: Pro : easy. Con: Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
|
||||
- embedded the type in the ressource handler: Con: more work, API more complex. Pro: Memory usage seems better.
|
||||
|
||||
* TODO #16 : New: Lock Free List :ENHANCEMENT:
|
||||
|
||||
@@ -510,6 +599,8 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
|
||||
- needs to be logically deleted : needs a previous field
|
||||
(NULL if not logically deleted) ? TBC
|
||||
|
||||
NOTE: m-c-mempool doesn't seem to be fully robust. random failure of the test cases appear (more notably with Visual C++, but it is still quite rare).
|
||||
|
||||
* DONE #14 : Memory allocation enhancement :ENHANCEMENT:
|
||||
|
||||
Enhancement of the memory allocation scheme to find way to deal properly with advanced allocators:
|
||||
@@ -547,7 +638,7 @@ It is a kind of object inheritance where the container inherits some extra data
|
||||
Duplicate with #32 which is more generic ==> Closed
|
||||
|
||||
* TODO #12 : New: Atomic shared pointer :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
Add an extension to the SHARED_PTR API:
|
||||
|
||||
- New type atomic_shared_ptr
|
||||
@@ -621,7 +712,7 @@ Other alternative solution is to use the bit 0 to mark the pointer as being upda
|
||||
Other implementation seems to have it hard to be lock-free: cf. https://github.com/llvm-mirror/libcxx/commit/5fec82dc0db3623546038e4a86baa44f749e554f
|
||||
|
||||
* TODO #5 : New: Concurrent dictionary Container :ENHANCEMENT:
|
||||
|
||||
** State
|
||||
Implement a more efficient dictionary than lock + std dictionary for all operations when dealing with threads.
|
||||
See https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ param2=""
|
||||
while test $# -gt 0 ; do
|
||||
case $1 in
|
||||
-c) param2="$param2 -c" ;;
|
||||
-o) param2="$param2 -o $2" ; shift ;;
|
||||
*\.c) param1="$param1 $1" ;;
|
||||
*) param1="$param1 $1" ; param2="$param2 $1" ;;
|
||||
esac
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 106 KiB |
+4
-4
@@ -43,12 +43,12 @@ static void my_array_free(void *ptr)
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// Allocate for single object
|
||||
# define M_MEMORY_ALLOC(type) my_alloc (sizeof (type))
|
||||
# define M_MEMORY_DEL(ptr) my_free(ptr, sizeof *ptr)
|
||||
# define M_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type))
|
||||
# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr)
|
||||
|
||||
// Allocate for array of objects
|
||||
# define M_MEMORY_REALLOC(type, ptr, n) my_array_alloc (sizeof (type), ptr, n)
|
||||
# define M_MEMORY_FREE(ptr) my_array_free(ptr)
|
||||
# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n)
|
||||
# define M_MEMORY_FREE(ctx, type, ptr, n) my_array_free(ptr)
|
||||
|
||||
#include "m-list.h"
|
||||
#include "m-array.h"
|
||||
|
||||
+4
-4
@@ -23,8 +23,8 @@ static void my_free(void *ptr, size_t s)
|
||||
}
|
||||
|
||||
// Allocate for single object
|
||||
# define MY_MEMORY_ALLOC(type) my_alloc (sizeof (type))
|
||||
# define MY_MEMORY_DEL(ptr) my_free(ptr, sizeof *ptr)
|
||||
# define MY_MEMORY_ALLOC(ctx, type) my_alloc (sizeof (type))
|
||||
# define MY_MEMORY_DEL(ctx, ptr) my_free(ptr, sizeof *ptr)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -76,8 +76,8 @@ static void my_array_free(void *ptr)
|
||||
}
|
||||
|
||||
// Allocate for array of objects
|
||||
# define MY_MEMORY_REALLOC(type, ptr, n) my_array_alloc (sizeof (type), ptr, n)
|
||||
# define MY_MEMORY_FREE(ptr) my_array_free(ptr)
|
||||
# define MY_MEMORY_REALLOC(ctx, type, ptr, o, n) my_array_alloc (sizeof (type), ptr, n)
|
||||
# define MY_MEMORY_FREE(ctx, type, ptr, n) my_array_free(ptr)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -13,8 +13,10 @@ ALGO_DEF(array_mpz, ARRAY_OPLIST(array_mpz, M_CLASSIC_OPLIST(mpz)))
|
||||
static inline void my_mpz_inc(mpz_t *d, const mpz_t a){
|
||||
mpz_add(*d, *d, a);
|
||||
}
|
||||
static inline void my_mpz_sqr(mpz_t *d, const mpz_t a){
|
||||
static inline bool my_mpz_sqr(mpz_t *d, const mpz_t a, void *data){
|
||||
(void) data;
|
||||
mpz_mul(*d, a, a);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
@@ -31,7 +33,7 @@ int main(void)
|
||||
}
|
||||
|
||||
/* Compute z = sum (a[i]^2) */
|
||||
array_mpz_map_reduce (&z, a, my_mpz_inc, my_mpz_sqr);
|
||||
array_mpz_transform_reduce (&z, a, my_mpz_inc, my_mpz_sqr, NULL);
|
||||
gmp_printf ("Z=%Zd\n", z);
|
||||
|
||||
array_mpz_clear(a);
|
||||
|
||||
+4
-4
@@ -61,11 +61,11 @@ static void astar(array_point_t path, const string_t maze, int width)
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
point_emplace(next, current->x + deltas[i].x, current->y + deltas[i].y, 0);
|
||||
if (string_get_char(maze, point_index(next, width)) != '#') {
|
||||
int *new_cost = cost_point_get(costs, current);
|
||||
int new_cost = *cost_point_get(costs, current);
|
||||
int *cost = cost_point_get(costs, next);
|
||||
if (cost == NULL || *new_cost < *cost) {
|
||||
cost_point_set_at(costs, next, *new_cost);
|
||||
next->priority = *new_cost + abs(goal->x - next->x) + abs(goal->y - next->y);
|
||||
if (cost == NULL || new_cost < *cost) {
|
||||
cost_point_set_at(costs, next, new_cost);
|
||||
next->priority = new_cost + abs(goal->x - next->x) + abs(goal->y - next->y);
|
||||
prio_point_push(front, next);
|
||||
step_point_set_at(from, next, current);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "m-bptree.h"
|
||||
|
||||
BPTREE_MULTI_DEF2(multimap, 5, int, M_BASIC_OPLIST, int, M_BASIC_OPLIST)
|
||||
#define M_OPL_map_multimap_t() BPTREE_OPLIST2(multimap, M_BASIC_OPLIST, M_BASIC_OPLIST)
|
||||
|
||||
// Fill in with multiple value
|
||||
static void
|
||||
fill_in(multimap_t map)
|
||||
{
|
||||
for(int i = 1 ; i < 100; i++)
|
||||
multimap_set_at(map, i, i);
|
||||
for(int i = 1 ; i < 100; i+=2)
|
||||
multimap_set_at(map, i, i*i);
|
||||
for(int i = 1 ; i < 100; i+=3)
|
||||
multimap_set_at(map, i, i*i*i);
|
||||
}
|
||||
|
||||
// Count the number of occurence of a key
|
||||
static int
|
||||
count(multimap_t map, int key)
|
||||
{
|
||||
int n = 0;
|
||||
multimap_it_t it;
|
||||
// it_from is O(n*ln n) complexity
|
||||
// it_while_p and next are O(1) complexity
|
||||
for(multimap_it_from(it, map, key); multimap_it_while_p(it, key); multimap_next(it))
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Print the map in details
|
||||
static void
|
||||
print(multimap_t map)
|
||||
{
|
||||
for(int i = 0; i < 101;i++) {
|
||||
printf("Key=%d Count=%d ", i, count(map, i));
|
||||
multimap_it_t it;
|
||||
for(multimap_it_from(it, map, i); multimap_it_while_p(it, i); multimap_next(it)) {
|
||||
printf("Value=%d ", *multimap_ref(it)->value_ptr );
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
multimap_t map;
|
||||
multimap_init(map);
|
||||
fill_in(map);
|
||||
print(map);
|
||||
multimap_clear(map);
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "m-buffer.h"
|
||||
#include "m-shared.h"
|
||||
#include "m-shared-ptr.h"
|
||||
#include "m-thread.h"
|
||||
|
||||
/* Global variable to stop scheduling */
|
||||
@@ -99,8 +99,8 @@ SHARED_PTR_DEF(shared_bigdata, bigdata_t)
|
||||
* - we change the semantics of _push / _pop into buffer as push a new item and pop it as a move (ideal semantics for shared_ptr interface).
|
||||
*/
|
||||
#define MY_QUEUE_SIZE 10
|
||||
BUFFER_DEF(buffer_bigdata, shared_bigdata_t, MY_QUEUE_SIZE,
|
||||
BUFFER_QUEUE|BUFFER_PUSH_INIT_POP_MOVE, SHARED_PTR_OPLIST(shared_bigdata))
|
||||
BUFFER_DEF(buffer_bigdata, shared_bigdata_t *, MY_QUEUE_SIZE,
|
||||
BUFFER_QUEUE|BUFFER_PUSH_INIT_POP_MOVE, SHARED_PTR_OPLIST(shared_bigdata, M_OPL_bigdata_t()))
|
||||
|
||||
/* Let's build a Thread synchros tree:
|
||||
*
|
||||
@@ -134,8 +134,7 @@ static void thread1 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
shared_bigdata_init_new (ptr);
|
||||
shared_bigdata_t *ptr = shared_bigdata_new ();
|
||||
perform_acquisition(*shared_bigdata_ref(ptr));
|
||||
buffer_bigdata_push(buf_t1tot2, ptr);
|
||||
buffer_bigdata_push(buf_t1tot3, ptr);
|
||||
@@ -146,7 +145,7 @@ static void thread2 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
shared_bigdata_t *ptr;
|
||||
buffer_bigdata_pop(&ptr, buf_t1tot2); // NOTE: Pop has been configured to init ptr
|
||||
perform_computation2(*shared_bigdata_ref(ptr));
|
||||
shared_bigdata_clear(ptr);
|
||||
@@ -156,7 +155,7 @@ static void thread3 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
shared_bigdata_t *ptr;
|
||||
buffer_bigdata_pop(&ptr, buf_t1tot3); // NOTE: Pop has been configured to init ptr
|
||||
perform_computation3(*shared_bigdata_ref(ptr));
|
||||
buffer_bigdata_push(buf_t3tot4, ptr);
|
||||
@@ -167,7 +166,7 @@ static void thread4 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
shared_bigdata_t *ptr;
|
||||
buffer_bigdata_pop(&ptr, buf_t3tot4); // NOTE: Pop has been configured to init ptr
|
||||
perform_computation4(*shared_bigdata_ref(ptr));
|
||||
shared_bigdata_clear(ptr);
|
||||
|
||||
+11
-10
@@ -4,44 +4,45 @@
|
||||
#include "m-dict.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Define a dictionnary of string --> int
|
||||
// Define a dictionary of string --> int
|
||||
// named 'dict_str'
|
||||
DICT_DEF2(dict_str, string_t, STRING_OPLIST, int, M_BASIC_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Declare the dictionnary 'h'
|
||||
// Declare the dictionary 'h'
|
||||
dict_str_t h;
|
||||
int max = 1;
|
||||
// Declare the string 'key'
|
||||
string_t key;
|
||||
|
||||
// Initialize the dictionnary & the string
|
||||
// Initialize the dictionary & the string
|
||||
dict_str_init(h);
|
||||
string_init (key);
|
||||
|
||||
// While there is some data in the stream
|
||||
while (!feof(stdin))
|
||||
while (true)
|
||||
{
|
||||
// Read a complete line from the stream and fill in the string 'key'
|
||||
string_fgets (key, stdin, STRING_READ_PURE_LINE);
|
||||
// If the 'key' exists in the dictionnary
|
||||
if (feof(stdin)) break;
|
||||
// If the 'key' exists in the dictionary
|
||||
int *v = dict_str_get (h, key);
|
||||
if (v != NULL) {
|
||||
// Increment the counter in the dictionnary
|
||||
// Increment the counter in the dictionary
|
||||
(*v)++;
|
||||
max = M_MAX(*v, max);
|
||||
} else {
|
||||
// Update the dictionnary with h[key] = 1
|
||||
// Update the dictionary with h[key] = 1
|
||||
dict_str_set_at(h, key, 1);
|
||||
}
|
||||
}
|
||||
printf ("Max occurence = %d\n", max);
|
||||
printf ("Max occurrence = %d\n", max);
|
||||
|
||||
// Iterate on the dictionnary
|
||||
// Iterate on the dictionary
|
||||
dict_str_it_t it;
|
||||
for(dict_str_it(it, h); !dict_str_end_p(it); dict_str_next(it)) {
|
||||
// Get a constant reference on the pair stored in the dictionnary
|
||||
// Get a constant reference on the pair stored in the dictionary
|
||||
const struct dict_str_pair_s *pair = dict_str_cref(it);
|
||||
printf ("Key=%s Value=%d\n",
|
||||
string_get_cstr(pair->key), pair->value);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "m-i-list.h"
|
||||
|
||||
typedef struct data_s {
|
||||
M_ILIST_INTERFACE(list1, struct data_s);
|
||||
char *ptr;
|
||||
int n;
|
||||
} data_t;
|
||||
|
||||
static void
|
||||
data_init(data_t *p)
|
||||
{
|
||||
M_ILIST_INIT_FIELD(list1, *p);
|
||||
p->ptr = malloc(100);
|
||||
p->n = 100;
|
||||
}
|
||||
|
||||
static void
|
||||
data_clear(data_t *p)
|
||||
{
|
||||
free(p->ptr);
|
||||
p->n = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
data_init_set(data_t *p, data_t s)
|
||||
{
|
||||
p->ptr = malloc(s.n);
|
||||
p->n = s.n;
|
||||
memcpy(p->ptr, s.ptr, s.n);
|
||||
}
|
||||
|
||||
#define DATA_OP (INIT(API_2(data_init)), INIT_SET(API_2(data_init_set)), CLEAR(API_2(data_clear)) )
|
||||
|
||||
M_ILIST_DEF(list1, data_t, DATA_OP)
|
||||
|
||||
data_t pool[100];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
list1_t l;
|
||||
|
||||
list1_init(l);
|
||||
data_init(&pool[0]);
|
||||
list1_push_back(l, &pool[0]);
|
||||
data_init(&pool[1]);
|
||||
list1_push_back(l, &pool[1]);
|
||||
|
||||
list1_it_t it;
|
||||
list1_it(it, l);
|
||||
data_init(&pool[2]);
|
||||
list1_insert(l, it, &pool[2]);
|
||||
|
||||
list1_clear(l);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
+5
-2
@@ -208,11 +208,14 @@ dict_read_from_file(dict_mph_t dict, array_string_t arr, const char filename[])
|
||||
// Not clean as items of 'dict' points are linked to items of 'arr'
|
||||
// and the containers are not aware of this link.
|
||||
array_string_push_back (arr, str);
|
||||
dict_mph_set_at(dict, string_get_cstr(*array_string_back(arr)), line);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now 'arr' won't change anymore and we can get reliable pointers from it.
|
||||
for M_EACH(item, arr, array_string_t) {
|
||||
dict_mph_set_at(dict, string_get_cstr(*item), line);
|
||||
line++;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include "m-tree.h"
|
||||
|
||||
TREE_DEF(tree, int)
|
||||
|
||||
// Compute sum of the tree using iterator and a pre-order walk
|
||||
static int tree_sum_iter(tree_t t)
|
||||
{
|
||||
int s = 0;
|
||||
for(tree_it_t it = tree_it(t); !tree_end_p(it); tree_next(&it)) {
|
||||
s += *tree_cref(it);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Compute sum of the tree using recursive call
|
||||
static int tree_sum_recur(tree_it_t it)
|
||||
{
|
||||
if (tree_end_p(it))
|
||||
return 0;
|
||||
if (tree_leaf_p(it))
|
||||
return *tree_cref(it);
|
||||
assert( tree_degree(it) == 2);
|
||||
return *tree_cref(it) + tree_sum_recur(tree_down(it)) + tree_sum_recur(tree_right(tree_down(it)));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tree_t t;
|
||||
tree_init(t);
|
||||
tree_it_t r = tree_set_root(t, 1);
|
||||
tree_it_t l = tree_insert_child(r, 2);
|
||||
tree_insert_child(r, 5);
|
||||
tree_insert_child(l, 3);
|
||||
tree_insert_child(l, 4);
|
||||
printf("sum = %d vs %d\n", tree_sum_iter(t), tree_sum_recur(tree_it(t)));
|
||||
tree_clear(t);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdio.h>
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-string.h"
|
||||
|
||||
ARRAY_DEF(array_int, int)
|
||||
#define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST)
|
||||
ALGO_DEF(array_int, array_int_t)
|
||||
|
||||
static bool callback1(int *out, int in, void *data)
|
||||
{
|
||||
int *cpt = data;
|
||||
bool ret = true;
|
||||
if (*cpt == -1 && in == 11)
|
||||
*cpt = 0; // Start when first reach 11
|
||||
ret &= (*cpt >= 0); // Skip initial numbers unequal 11
|
||||
ret &= (in % 2 != 0); // Collect odd numbers
|
||||
(*cpt) += ret;
|
||||
ret &= (*cpt <= 5); // Only take five numbers
|
||||
*out = in * in; // Square each number
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void callback_add_int(int *out, int in)
|
||||
{
|
||||
*out += in;
|
||||
}
|
||||
|
||||
static void demo(void)
|
||||
{
|
||||
M_LET( (t, M_SEQ(0, 30)), vector, array_int_t) {
|
||||
printf("t="); array_int_out_str(stdout, t);
|
||||
int b = -1;
|
||||
array_int_transform(vector, t, callback1, &b);
|
||||
printf("\nvector="); array_int_out_str(stdout, vector); printf("\n");
|
||||
array_int_reduce(&b, vector, callback_add_int);
|
||||
printf("Sum=%d\n", b);
|
||||
}
|
||||
}
|
||||
|
||||
ARRAY_DEF(array_str, string_t)
|
||||
#define M_OPL_array_str_t() ARRAY_OPLIST(array_str, M_STRING_OPLIST)
|
||||
ALGO_DEF(array_str, array_str_t)
|
||||
|
||||
static bool callback2(string_t *out, const string_t in, void *data)
|
||||
{
|
||||
char *cp = data, c = *cp;
|
||||
if (string_search_char(in, c) == M_STRING_FAILURE)
|
||||
return false;
|
||||
string_set(*out, in);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void demo_split(void)
|
||||
{
|
||||
M_LET( (s, ("This is a sentence in C11.")), string_t)
|
||||
M_LET(words, f_words, array_str_t) {
|
||||
array_str_split(words, s, ' ');
|
||||
array_str_transform(f_words, words, callback2, &(char){'i'});
|
||||
printf("f_words="); array_str_out_str(stdout, f_words); printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
demo();
|
||||
demo_split();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#if HAVE_GMP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gmp.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-list.h"
|
||||
#include "m-generic.h"
|
||||
|
||||
// Register an organization for generic: USER
|
||||
// Each index (here '1') shall be unique for the whole program
|
||||
#define M_GENERIC_ORG_1() (USER)
|
||||
// Register a component for the organization USER: CORE
|
||||
// Each index (here '1') shall be unique for the whole organization
|
||||
#define M_GENERIC_ORG_USER_COMP_1() (CORE)
|
||||
|
||||
// Register the oplist of a mpz_t. It is a classic oplist.
|
||||
// Adding support for emplace and initialization
|
||||
// Adding the ability to output its value for serialization/printing
|
||||
// Adding type information for handling correctly the generic association
|
||||
#define M_OPL_mpz_t() M_OPEXTEND(M_CLASSIC_OPLIST(mpz), \
|
||||
INIT_WITH(mpz_init_set_ui), EMPLACE_TYPE(unsigned int), \
|
||||
OUT_STR( API( mpz_out_str, VOID, ARG1, 10, ARG2)), \
|
||||
GENTYPE(__mpz_struct *), TYPE(mpz_t) )
|
||||
// Register the oplist as a generic type of the component CORE of the organization USER
|
||||
// Each index (here '1') shall be unique for the whole component
|
||||
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_1() M_OPL_mpz_t()
|
||||
|
||||
// Define an instance of an array of mpz_t (both type and function)
|
||||
ARRAY_DEF(array_mpz, mpz_t)
|
||||
// Register the oplist of the created instance of array of mpz_t
|
||||
#define M_OPL_array_mpz_t() ARRAY_OPLIST(array_mpz, M_OPL_mpz_t())
|
||||
// Register the oplist as a generic type of the component CORE of the organization USER
|
||||
// Each index (here '10') shall be unique for the whole component
|
||||
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_10() M_OPL_array_mpz_t()
|
||||
|
||||
// Define an instance of a list of mpz_t (both type and function)
|
||||
LIST_DEF(list_mpz, mpz_t)
|
||||
// Register the oplist of the created instance of array of mpz_t
|
||||
#define M_OPL_list_mpz_t() LIST_OPLIST(list_mpz, M_OPL_mpz_t())
|
||||
// Register the oplist as a generic type of the component CORE of the organization USER
|
||||
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_11() M_OPL_list_mpz_t()
|
||||
|
||||
// Define an instance of a list of unsigned int (both type and function)
|
||||
LIST_DEF(list_uint, unsigned)
|
||||
// Register the oplist of the created instance of list of unsigned
|
||||
#define M_OPL_list_uint_t() LIST_OPLIST(list_uint, M_BASIC_OPLIST)
|
||||
// Register the oplist as a generic type of the component CORE of the organization USER
|
||||
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_12() M_OPL_list_uint_t()
|
||||
|
||||
// Define an instance of a array of unsigned int (both type and function)
|
||||
ARRAY_DEF(array_uint, unsigned)
|
||||
// Register the oplist of the created instance of list of unsigned
|
||||
#define M_OPL_array_uint_t() ARRAY_OPLIST(array_uint, M_BASIC_OPLIST)
|
||||
// Register the oplist as a generic type of the component CORE of the organization USER
|
||||
#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_13() M_OPL_array_uint_t()
|
||||
|
||||
int main(void) {
|
||||
|
||||
print("LIST UINT = ");
|
||||
// Initialize list as list_uint_t and push 17 & 42 in it.
|
||||
let( (list, 17, 42), list_uint_t) {
|
||||
push(list, 1742);
|
||||
// Iterate over all elements of the list of unsigned int
|
||||
for each (item, list) {
|
||||
// Print the element
|
||||
print(*item, " ");
|
||||
}
|
||||
// Print the full list too
|
||||
print("\n","List=", list, "\n");
|
||||
}
|
||||
// Now list has been cleared.
|
||||
|
||||
print("Array UINT = ");
|
||||
// Initialize list as array_uint_t and push 17 & 42 in it.
|
||||
let( (list, 17, 42), array_uint_t) {
|
||||
push(list, 1742);
|
||||
// Iterate over all elements of the array of unsigned int
|
||||
for each (item, list) {
|
||||
// Print the element
|
||||
print(*item, " ");
|
||||
}
|
||||
// Print the full array too
|
||||
print("\n","List=", list, "\n");
|
||||
}
|
||||
// Now array has been cleared.
|
||||
|
||||
print("LIST MPZ = ");
|
||||
// Initialize list as array_mpz_t and push_emplace mpz_t from 17 & 42 in it.
|
||||
// The parenthesis around 17 in (17) means to emplace
|
||||
let( (list, (17), (42)), list_mpz_t) {
|
||||
list_mpz_emplace_back(list, 1742); // Generic emplace is not supported yet
|
||||
// Iterate over all elements of the array of mpz_t
|
||||
for each (item, list) {
|
||||
// Print the element
|
||||
// Since we registered the out_str of a mpz_t, we can display it directly
|
||||
print(*item, " ");
|
||||
}
|
||||
// Print the full list too
|
||||
print("\n","List=", list, "\n");
|
||||
}
|
||||
|
||||
print("ARRAY MPZ = ");
|
||||
// Same as before but for array
|
||||
let( (list, (17), (42)), array_mpz_t) {
|
||||
array_mpz_emplace_back(list, 1742);
|
||||
for each (item, list) {
|
||||
print(*item, " ");
|
||||
}
|
||||
print("\n","List=", list, "\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#else
|
||||
int main(void) {}
|
||||
#endif
|
||||
+12
-10
@@ -35,6 +35,7 @@ mpz_fast_hash(const mpz_t z)
|
||||
#define M_OPL_mpz_t() \
|
||||
(INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), \
|
||||
INIT_WITH(API_1(M_INIT_WITH_THROUGH_EMPLACE_TYPE)), \
|
||||
INIT_MOVE(M_COPY_A1_DEFAULT) \
|
||||
EQUAL( API( mpz_cmp, EQ(0), ARG1, ARG2)), \
|
||||
CMP(mpz_cmp), \
|
||||
HASH(mpz_fast_hash), \
|
||||
@@ -77,7 +78,8 @@ mpfr_fast_hash (const mpfr_t f)
|
||||
/* Define the OPLIST of a mpfr_t and register it globally */
|
||||
#define M_OPL_mpfr_t() \
|
||||
(INIT(mpfr_init), INIT_SET(API(mpfr_init_set, VOID, ARG1, ARG2, MPFR_RNDN)), SET(API(mpfr_set, VOID, ARG1, ARG2, MPFR_RNDN)), CLEAR(mpfr_clear), \
|
||||
INIT_WITH(API_1(M_INIT_WITH_THROUGH_EMPLACE_TYPE)), \
|
||||
INIT_WITH(API_1(M_INIT_WITH_THROUGH_EMPLACE_TYPE)), \
|
||||
INIT_MOVE(M_COPY_A1_DEFAULT) \
|
||||
EQUAL( API( mpfr_cmp, EQ(0), ARG1, ARG2)), \
|
||||
CMP(mpfr_cmp), \
|
||||
HASH(mpfr_fast_hash), \
|
||||
@@ -225,15 +227,15 @@ int main(void)
|
||||
M_PRINT("\nThe array is equal to ", (array, my_array_t), "\n");
|
||||
} /* All variables are automatically cleared beyond this point */
|
||||
|
||||
// Define an array of mpz_t as { 1, 2, 1 } (with auto promotion from integer / string / double)
|
||||
// The auto-promotion uses _Generic feature and is available only in C11.
|
||||
M_LET ( (az, (1), ("2"), (1.0)), array_mpz_t)
|
||||
M_LET ( roots, array_mpfr_t) {
|
||||
// Compute roots
|
||||
compute_roots(roots, az);
|
||||
// Display of roots:
|
||||
M_PRINT("Roots of ", (az, array_mpz_t), " are ", (roots, array_mpfr_t), "\n");
|
||||
}
|
||||
// Define an array of mpz_t as { 1, 2, 1 } (with auto promotion from integer / string / double)
|
||||
// The auto-promotion uses _Generic feature and is available only in C11.
|
||||
M_LET ( (az, (1), ("2"), (1.0)), array_mpz_t)
|
||||
M_LET ( roots, array_mpfr_t) {
|
||||
// Compute roots
|
||||
compute_roots(roots, az);
|
||||
// Display of roots:
|
||||
M_PRINT("Roots of ", (az, array_mpz_t), " are ", (roots, array_mpfr_t), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Disable small name usage
|
||||
#define M_USE_SMALL_NAME 0
|
||||
|
||||
#include "m-algo.h"
|
||||
#include "m-array.h"
|
||||
#include "m-atomic.h"
|
||||
#include "m-bitset.h"
|
||||
#include "m-bptree.h"
|
||||
#include "m-bstring.h"
|
||||
#include "m-buffer.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-funcobj.h"
|
||||
#include "m-generic.h"
|
||||
#include "m-genint.h"
|
||||
#include "m-i-list.h"
|
||||
#include "m-list.h"
|
||||
#include "m-prioqueue.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-serial-bin.h"
|
||||
#include "m-serial-json.h"
|
||||
#include "m-shared-ptr.h"
|
||||
#include "m-snapshot.h"
|
||||
#include "m-string.h"
|
||||
#include "m-thread.h"
|
||||
#include "m-tree.h"
|
||||
#include "m-try.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-variant.h"
|
||||
#include "m-worker.h"
|
||||
|
||||
M_ARRAY_DEF(array, m_string_t)
|
||||
M_ALGO_DEF(array, M_ARRAY_OPLIST(array, M_STRING_OPLIST))
|
||||
M_BPTREE_DEF2(bptree, 11, m_string_t, m_string_t)
|
||||
M_BUFFER_DEF(queue, m_string_t, 10, M_BUFFER_QUEUE)
|
||||
M_LIST_DEF(list, m_string_t)
|
||||
M_LIST_DUAL_PUSH_DEF(list2, m_string_t)
|
||||
M_DEQUE_DEF(deque, m_string_t)
|
||||
M_DICT_DEF2(dict, m_string_t, m_string_t)
|
||||
M_DICT_OA_DEF2(dict_oa, m_string_t, m_string_t)
|
||||
M_DICT_SET_DEF(dict_set, m_string_t)
|
||||
M_DICT_OASET_DEF(dict_oa_set, m_string_t)
|
||||
M_RBTREE_DEF(rbtree, m_string_t)
|
||||
M_FUNC_OBJ_INS_DEF(compare_by /* name of the instance */,
|
||||
array_cmp_obj /* name of the interface */,
|
||||
(a, b) /* name of the input parameters of the function like object. The type are inherited from the interface. */, {
|
||||
/* code of the function object */
|
||||
if (m_string_equal_cstr_p(self->sort_field, "normal"))
|
||||
return m_string_cmp(a, b);
|
||||
else
|
||||
return -m_string_cmp(a, b);
|
||||
},
|
||||
/* Additional fields stored in the function object */
|
||||
(sort_field, m_string_t) )
|
||||
M_PRIOQUEUE_DEF(prio_queue, m_string_t)
|
||||
M_SHARED_PTR_DEF(shared_string, m_string_t)
|
||||
M_SNAPSHOT_SPSC_DEF(snap1, m_string_t)
|
||||
M_SNAPSHOT_SPMC_DEF(snap2, m_string_t)
|
||||
M_SNAPSHOT_MPMC_DEF(snap3, m_string_t)
|
||||
M_TREE_DEF(tree, m_string_t)
|
||||
M_TUPLE_DEF2(tuple, (a, m_string_t))
|
||||
M_VARIANT_DEF2(variant, (a, m_string_t))
|
||||
M_WORKER_SPAWN_DEF2(worker, (filename, m_string_t) )
|
||||
|
||||
int main(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
// Define the memory context.
|
||||
typedef int *context_t;
|
||||
|
||||
// Define the realloc / free that accepts the memory context.
|
||||
static void *my_realloc(void *ptr, size_t old, size_t new, size_t base, context_t context)
|
||||
{
|
||||
(void) context;
|
||||
(void) old;
|
||||
return realloc(ptr, new*base);
|
||||
}
|
||||
|
||||
static void my_free(void *ptr, size_t old, size_t base, context_t context)
|
||||
{
|
||||
(void) context;
|
||||
(void) old;
|
||||
(void) base;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
// Request M*LIB to generate functions accepting a memory context parameter that will be passed to the memory functions
|
||||
# define M_USE_CONTEXT context_t
|
||||
|
||||
// Overloaded memory functions to use the custom ones.
|
||||
# define M_MEMORY_REALLOC(ctx, type, ptr, o, n) my_realloc(ptr, o, n, sizeof (type), ctx)
|
||||
# define M_MEMORY_FREE(ctx, type, ptr, o) my_free(ptr, o, sizeof (*ptr), ctx)
|
||||
|
||||
# define M_MEMORY_ALLOC(ctx, type) my_realloc(NULL, 0, 1, sizeof (type), ctx)
|
||||
# define M_MEMORY_DEL(ctx, ptr) my_free(ptr, 1, sizeof (*ptr), ctx)
|
||||
|
||||
// Now we can include M*LIB functions.
|
||||
#include "m-string.h"
|
||||
#include "m-bitset.h"
|
||||
#include "m-bstring.h"
|
||||
#include "m-genint.h"
|
||||
#include "m-algo.h"
|
||||
context_t my_pool = NULL;
|
||||
extern int f(string_t, bitset_t b, bstring_t y);
|
||||
|
||||
int f(string_t x, bitset_t b, bstring_t y)
|
||||
{
|
||||
string_init(x);
|
||||
string_set_str(my_pool, x, "Hello world");
|
||||
string_clear(my_pool, x);
|
||||
|
||||
bitset_init(b);
|
||||
bitset_push_back(my_pool, b, true);
|
||||
bitset_clear(my_pool, b);
|
||||
|
||||
bstring_init(y);
|
||||
bstring_push_back(my_pool, y, 0);
|
||||
bstring_clear(my_pool, y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "m-array.h"
|
||||
|
||||
ARRAY_DEF(array, int)
|
||||
ALGO_DEF(array, ARRAY_OPLIST(array, M_BASIC_OPLIST))
|
||||
ARRAY_DEF(array_str, string_t)
|
||||
ALGO_DEF(array_str, ARRAY_OPLIST(array_str, STRING_OPLIST))
|
||||
|
||||
#include "m-prioqueue.h"
|
||||
|
||||
PRIOQUEUE_DEF(prio, int)
|
||||
PRIOQUEUE_DEF(prio_str, string_t)
|
||||
ALGO_DEF(prio_str, PRIOQUEUE_OPLIST(prio_str, STRING_OPLIST))
|
||||
|
||||
#include "m-rbtree.h"
|
||||
|
||||
RBTREE_DEF(rbtree, int)
|
||||
RBTREE_DEF(rbtree_str, string_t)
|
||||
ALGO_DEF(rbtree_str, RBTREE_OPLIST(rbtree_str, STRING_OPLIST))
|
||||
|
||||
#include "m-list.h"
|
||||
|
||||
LIST_DEF(list, int)
|
||||
ALGO_DEF(list, LIST_OPLIST(list, M_BASIC_OPLIST))
|
||||
LIST_DEF(list_str, string_t)
|
||||
ALGO_DEF(list_str, LIST_OPLIST(list_str, M_STRING_OPLIST))
|
||||
|
||||
LIST_DUAL_PUSH_DEF(list2, int)
|
||||
LIST_DUAL_PUSH_DEF(list2_str, string_t)
|
||||
ALGO_DEF(list2_str, LIST_OPLIST(list2_str, M_STRING_OPLIST))
|
||||
|
||||
#include "m-bptree.h"
|
||||
|
||||
BPTREE_DEF2(bptree, 5, int, int)
|
||||
BPTREE_DEF2(bptree_str, 8, string_t, string_t)
|
||||
ALGO_DEF(bptree_str, M_BPTREE_OPLIST2(bptree_str, M_STRING_OPLIST, M_STRING_OPLIST))
|
||||
|
||||
#include "m-dict.h"
|
||||
|
||||
DICT_DEF2(dict, int, int)
|
||||
DICT_DEF2(dict_str, string_t, string_t)
|
||||
ALGO_DEF(dict_str, DICT_OPLIST(dict_str, M_STRING_OPLIST, M_STRING_OPLIST))
|
||||
|
||||
DICT_SET_DEF(set, int)
|
||||
DICT_SET_DEF(set_str, string_t)
|
||||
ALGO_DEF(set_str, DICT_SET_OPLIST(set_str, M_STRING_OPLIST))
|
||||
|
||||
static inline bool oor_equal_p(int k, char n)
|
||||
{
|
||||
return k == INT_MIN + n;
|
||||
}
|
||||
|
||||
static inline int oor_set(char n)
|
||||
{
|
||||
return INT_MIN + n;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_oa,
|
||||
int, M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_4(oor_set))),
|
||||
int, M_BASIC_OPLIST)
|
||||
DICT_OA_DEF2(dict2_str, string_t, string_t)
|
||||
ALGO_DEF(dict2_str, DICT_OPLIST(dict2_str, M_STRING_OPLIST, M_STRING_OPLIST))
|
||||
|
||||
DICT_OASET_DEF(set_oa,
|
||||
int, M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(API_4(oor_set))) )
|
||||
DICT_OASET_DEF(set2_str, string_t)
|
||||
ALGO_DEF(set2_str, DICT_SET_OPLIST(set2_str, M_STRING_OPLIST))
|
||||
|
||||
#include "m-buffer.h"
|
||||
|
||||
BUFFER_DEF(buffer1, int, 10, M_BUFFER_QUEUE)
|
||||
BUFFER_DEF(buffer2, int, 0, M_BUFFER_STACK)
|
||||
|
||||
M_QUEUE_MPMC_DEF(queue1, int, M_BUFFER_STACK)
|
||||
M_QUEUE_SPSC_DEF(queue2, int, M_BUFFER_STACK)
|
||||
|
||||
BUFFER_DEF(buffer11, string_t, 10, M_BUFFER_QUEUE)
|
||||
BUFFER_DEF(buffer22, string_t, 0, M_BUFFER_STACK)
|
||||
|
||||
M_QUEUE_MPMC_DEF(queue11, string_t, M_BUFFER_STACK)
|
||||
M_QUEUE_SPSC_DEF(queue22, string_t, M_BUFFER_STACK)
|
||||
|
||||
#include "m-deque.h"
|
||||
|
||||
M_DEQUE_DEF(deque, int)
|
||||
M_DEQUE_DEF(deque_str, string_t)
|
||||
M_ALGO_DEF(deque_str, M_DEQUE_OPLIST(deque_str, M_STRING_OPLIST))
|
||||
|
||||
#include "m-snapshot.h"
|
||||
|
||||
M_SNAPSHOT_SPSC_DEF(snap1, int)
|
||||
M_SNAPSHOT_SPMC_DEF(snap2, int)
|
||||
M_SNAPSHOT_MPMC_DEF(snap3, string_t)
|
||||
|
||||
#include "m-tree.h"
|
||||
|
||||
M_TREE_DEF(tree, int)
|
||||
M_TREE_DEF(tree_str, string_t)
|
||||
|
||||
#include "m-tuple.h"
|
||||
|
||||
M_TUPLE_DEF2(tuple1, (num, int) )
|
||||
M_TUPLE_DEF2(tuple2, (num, int), (name, string_t) )
|
||||
|
||||
#include "m-variant.h"
|
||||
|
||||
M_VARIANT_DEF2(variant1, (num, int) )
|
||||
M_VARIANT_DEF2(variant2, (num, int), (name, string_t) )
|
||||
|
||||
#include "m-worker.h"
|
||||
|
||||
M_WORKER_SPAWN_DEF2(worker_int, (in, int) )
|
||||
M_WORKER_SPAWN_DEF2(worker_str, (str, string_t) )
|
||||
|
||||
#include "m-shared-ptr.h"
|
||||
|
||||
M_SHARED_PTR_DEF(shared_int, int)
|
||||
M_SHARED_PTR_DEF(shared_str, string_t)
|
||||
|
||||
#include "m-queue.h"
|
||||
|
||||
M_QUEUE_DEF(queue_10, int, 10 )
|
||||
M_STACK_DEF(stack_20, string_t, 20)
|
||||
|
||||
#include "m-serial-json.h"
|
||||
|
||||
#include "m-serial-bin.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#include <stdio.h>
|
||||
#include "m-variant.h"
|
||||
#include "m-list.h"
|
||||
|
||||
// Define the variant with the given basic types
|
||||
// A variant is an union of all its type, with the information needed to identify which type it holds
|
||||
VARIANT_DEF2(obj,
|
||||
(boolean, bool, M_BOOL_OPLIST),
|
||||
(integer, int, M_BASIC_OPLIST),
|
||||
(real, double, M_BASIC_OPLIST),
|
||||
(cstr, const char*, M_CSTR_OPLIST))
|
||||
// Associate obj_t to its oplist, so that we can only use 'obj_t'
|
||||
// and M*LIB knows how to handle this type properly
|
||||
// We need to extend the oplist with FIELD so that M*LIB is able to generate 'emplace' functions
|
||||
#define M_OPL_obj_t() \
|
||||
VARIANT_OPLIST(obj, M_OPEXTEND(M_BOOL_OPLIST, FIELD(boolean)), \
|
||||
M_OPEXTEND(M_BASIC_OPLIST, FIELD(integer)), \
|
||||
M_OPEXTEND(M_BASIC_OPLIST, FIELD(real)), \
|
||||
M_OPEXTEND(M_CSTR_OPLIST, FIELD(cstr)))
|
||||
|
||||
// Define a list over the variant
|
||||
LIST_DEF(list, obj_t)
|
||||
// Associate list_t to its oplist, so that we can only use 'list_t'
|
||||
#define M_OPL_list_t() LIST_OPLIST(list, M_OPL_obj_t())
|
||||
|
||||
/* Print the type of the object */
|
||||
static void print_obj(obj_t o, const char name[])
|
||||
{
|
||||
if (obj_empty_p(o)) {
|
||||
printf("Object %s is empty\n", name);
|
||||
}
|
||||
if (obj_boolean_p(o)) {
|
||||
printf("Object %s is a boolean (%d)\n", name, *obj_get_boolean(o));
|
||||
}
|
||||
if (obj_integer_p(o)) {
|
||||
printf("Object %s is an integer (%d)\n", name, *obj_get_integer(o));
|
||||
}
|
||||
if (obj_real_p(o)) {
|
||||
printf("Object %s is a float (%f)\n", name, *obj_get_real(o));
|
||||
}
|
||||
if (obj_cstr_p(o)) {
|
||||
printf("Object %s is C string (%s)\n", name, *obj_get_cstr(o));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void f(void)
|
||||
{
|
||||
// Initialize & afterward clear the object
|
||||
M_LET(o, obj_t) {
|
||||
print_obj(o, "1st init");
|
||||
obj_set_real(o, 23.4);
|
||||
print_obj(o, "1st+set");
|
||||
} // Now the object is out of scope and is cleared.
|
||||
|
||||
// Initialize, set as an integer of value '12' & afterward clear the object
|
||||
// () around 12 means that we want to 'emplace' object using the input '12' to construct the object in-place,
|
||||
// rather than setting it from an already existing 'obj_t'.
|
||||
// It checks for the type of 12, find it is an integer, and initialize o with this value
|
||||
// It won't work if there is multiple compatible type in the variant
|
||||
M_LET( (o, (12)), obj_t) {
|
||||
print_obj(o, "2nd init");
|
||||
obj_set_cstr(o, "Hello");
|
||||
print_obj(o, "2nd+set");
|
||||
}
|
||||
|
||||
M_LET( (o, (true)), obj_t) {
|
||||
// In C11, a 'true' value is an 'int' type, not a bool!
|
||||
print_obj(o, "3rd init");
|
||||
obj_set_boolean(o, true);
|
||||
print_obj(o, "3rd+set");
|
||||
// Without "()" around the source, it will set the value using the object builtin copy.
|
||||
M_LET( (p, o), obj_t) {
|
||||
print_obj(o, "3rd+rec");
|
||||
// We can also print the object directly:
|
||||
printf("OBJ=");
|
||||
obj_out_str(stdout, o);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void g(void)
|
||||
{
|
||||
// Initialize a list of obj_t and clear it afterwards
|
||||
M_LET(l, list_t){
|
||||
// Let's emplace some type in the list
|
||||
list_emplace_back_boolean(l, true);
|
||||
list_emplace_back_integer(l, 12);
|
||||
list_emplace_back_cstr(l, "World");
|
||||
list_emplace_back_real(l, 17.42);
|
||||
// We can iterate on the list
|
||||
for M_EACH(item, l, list_t) {
|
||||
print_obj(*item, "loop");
|
||||
}
|
||||
// We can also print the object directly:
|
||||
printf("LIST=");
|
||||
list_out_str(stdout, l);
|
||||
printf("\n");
|
||||
} // Clear & free the list
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
f();
|
||||
g();
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - ALGO module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -170,19 +170,20 @@
|
||||
typedef bool (*M_F(name, _test_cb_ct))(type_t const); \
|
||||
typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \
|
||||
typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \
|
||||
typedef void (*M_F(name, _transform_cb_ct))(type_t *, type_t const); \
|
||||
typedef void (*M_F(name, _apply_cb_ct))(type_t); \
|
||||
typedef bool (*M_F(name, _transform_cb_ct))(type_t *, type_t const, void*); \
|
||||
typedef void (*M_F(name, _reduce_cb_ct))(type_t *, type_t const); \
|
||||
typedef void (*M_F(name, _apply_cb_ct))(type_t M_IF_METHOD(IT_REF, cont_oplist)( , const) );
|
||||
|
||||
|
||||
/* Define the function objects associated to the algorithms.
|
||||
* Created Function objects are part of the public interface */
|
||||
#define M_ALG0_DEF_FUNCOBJ(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
|
||||
\
|
||||
FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \
|
||||
FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \
|
||||
FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \
|
||||
FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), void, type_t *, type_t const ) \
|
||||
FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \
|
||||
M_FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \
|
||||
M_FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \
|
||||
M_FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \
|
||||
M_FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), bool, type_t *, type_t const )\
|
||||
M_FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \
|
||||
|
||||
|
||||
/* Define the sort functions with the CMP operator using the order selected */
|
||||
@@ -276,14 +277,14 @@
|
||||
M_CALL_SPLICE_BACK(cont_oplist, (b ? l1 : l2), l, it); \
|
||||
b = !b; \
|
||||
} \
|
||||
/* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \
|
||||
M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); \
|
||||
} \
|
||||
\
|
||||
/* Merge in empty 'l' the sorted container 'l1' and 'l2' */ \
|
||||
M_INLINE void \
|
||||
M_C3(name,sort_name,_merge)(container_t l, container_t l1, container_t l2 cmp_param(name)) \
|
||||
{ \
|
||||
/* M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); */ \
|
||||
M_ASSERT(M_CALL_EMPTY_P (cont_oplist, l)); \
|
||||
it_t it; \
|
||||
it_t it1; \
|
||||
it_t it2; \
|
||||
@@ -350,6 +351,7 @@
|
||||
return; \
|
||||
} \
|
||||
/* Container length is greater than 2: split, sort & merge */ \
|
||||
M_GLOBAL_CONTEXT(); \
|
||||
M_CALL_INIT(cont_oplist, l1); \
|
||||
M_CALL_INIT(cont_oplist, l2); \
|
||||
M_C3(name,sort_name,_split)(l1, l2, l); \
|
||||
@@ -357,6 +359,9 @@
|
||||
M_F(name,sort_name)(l2 cmp_arg); \
|
||||
M_C3(name,sort_name,_merge)(l, l1, l2 cmp_arg); \
|
||||
/* l1 & l2 shall be empty now */ \
|
||||
M_ASSERT( M_CALL_EMPTY_P(cont_oplist, l1)); \
|
||||
M_ASSERT( M_CALL_EMPTY_P(cont_oplist, l2)); \
|
||||
/* Normally nothing is done here */ \
|
||||
M_CALL_CLEAR(cont_oplist, l2); \
|
||||
M_CALL_CLEAR(cont_oplist, l1); \
|
||||
} \
|
||||
@@ -434,7 +439,7 @@
|
||||
/* Compute the union of two ***sorted*** containers */ \
|
||||
M_IF_METHOD(IT_INSERT, cont_oplist)( \
|
||||
M_INLINE void \
|
||||
M_C3(name,sort_name,_union)(container_t dst, const container_t src cmp_param(name)) \
|
||||
M_C3(name,sort_name,_union)(M_P_EXPAND container_t dst, const container_t src cmp_param(name)) \
|
||||
{ \
|
||||
it_t itSrc; \
|
||||
it_t itDst; \
|
||||
@@ -479,7 +484,7 @@
|
||||
/* Compute the intersection of two ***sorted*** containers */ \
|
||||
M_IF_METHOD(IT_REMOVE, cont_oplist)( \
|
||||
M_INLINE void \
|
||||
M_C3(name,sort_name,_intersect)(container_t dst, const container_t src cmp_param(name)) \
|
||||
M_C3(name,sort_name,_intersect)(M_P_EXPAND container_t dst, const container_t src cmp_param(name)) \
|
||||
{ \
|
||||
it_t itSrc; \
|
||||
it_t itDst; \
|
||||
@@ -688,9 +693,9 @@
|
||||
#define M_ALG0_DEF_FILL(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
|
||||
\
|
||||
/* Fill all the container with value (overwritten) */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _fill) (container_t l, type_t const value) \
|
||||
M_P(void, name, _fill, container_t l, type_t const value) \
|
||||
{ \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
M_CALL_SET(type_oplist, *item, value); \
|
||||
} \
|
||||
@@ -698,8 +703,7 @@
|
||||
\
|
||||
/* Fill the container with exactly 'n' occurrence of 'value' */ \
|
||||
M_IF_METHOD(PUSH, cont_oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _fill_n) (container_t l, size_t n, type_t const value) \
|
||||
M_P(void, name, _fill_n, container_t l, size_t n, type_t const value) \
|
||||
{ \
|
||||
M_CALL_RESET(cont_oplist, l); \
|
||||
for(size_t i = 0; i < n; i++) { \
|
||||
@@ -710,9 +714,9 @@
|
||||
\
|
||||
/* Fill the container with FOR('value'; 'value'+'inc') */ \
|
||||
M_IF_METHOD(ADD, type_oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _fill_a) (container_t l, type_t const value, type_t const inc) \
|
||||
M_P(void, name, _fill_a, container_t l, type_t const value, type_t const inc) \
|
||||
{ \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
type_t tmp; \
|
||||
M_CALL_INIT_SET(type_oplist, tmp, value); \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
@@ -724,9 +728,9 @@
|
||||
\
|
||||
/* Fill the container with n occurences of FOR('value'; 'value'+'inc') */ \
|
||||
M_IF_METHOD(PUSH, cont_oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _fill_an) (container_t l, size_t n, type_t const value, type_t const inc) \
|
||||
M_P(void, name, _fill_an, container_t l, size_t n, type_t const value, type_t const inc) \
|
||||
{ \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
type_t tmp; \
|
||||
M_CALL_INIT_SET(type_oplist, tmp, value); \
|
||||
M_CALL_RESET(cont_oplist, l); \
|
||||
@@ -753,32 +757,30 @@
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, type_oplist)( \
|
||||
M_IF_METHOD(PUSH_MOVE, cont_oplist)( \
|
||||
M_IF_METHOD(PUSH, cont_oplist)( \
|
||||
\
|
||||
/* Apply func for all elements of the container src and push the result in dst */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _transform) (container_t dst, \
|
||||
container_t src, \
|
||||
M_F(name, _transform_cb_ct) func) \
|
||||
M_P(void, name, _transform, container_t dst, container_t src, M_F(name, _transform_cb_ct) func, void *data) \
|
||||
{ \
|
||||
M_ASSERT(dst != src); \
|
||||
M_CALL_RESET(cont_oplist, dst); \
|
||||
type_t tmp; \
|
||||
M_CALL_INIT(type_oplist, tmp); \
|
||||
for M_EACH(item, src, cont_oplist) { \
|
||||
type_t tmp; \
|
||||
M_CALL_INIT(type_oplist, tmp); \
|
||||
func(&tmp, *item); \
|
||||
M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \
|
||||
if (func(&tmp, *item, data)) { \
|
||||
M_CALL_PUSH(cont_oplist, dst, tmp); \
|
||||
} \
|
||||
} \
|
||||
M_CALL_CLEAR(type_oplist, tmp); \
|
||||
M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \
|
||||
} \
|
||||
, /* END PUSH_MOVE */), /* END INIT */ ) \
|
||||
\
|
||||
/* Reduce all elements of the container in dst in function of func */ \
|
||||
M_IF_METHOD(SET, type_oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _reduce) (type_t *dest, container_t const l, \
|
||||
M_F(name, _transform_cb_ct) func) \
|
||||
M_P(bool, name, _reduce, type_t *dest, container_t const l, M_F(name, _reduce_cb_ct) func) \
|
||||
{ \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
bool initDone = false; \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
if (initDone) { \
|
||||
@@ -788,30 +790,31 @@
|
||||
initDone = true; \
|
||||
} \
|
||||
} \
|
||||
return initDone; \
|
||||
} \
|
||||
, /* END SET */) \
|
||||
\
|
||||
/* Reduce all transformed elements of the container in dst in function of func */ \
|
||||
M_IF_METHOD(INIT, type_oplist)( \
|
||||
M_INLINE \
|
||||
void M_F(name, _map_reduce) (type_t *dest, \
|
||||
const container_t l, \
|
||||
M_F(name, _transform_cb_ct) redFunc, \
|
||||
M_F(name, _transform_cb_ct) mapFunc) \
|
||||
M_P(bool, name, _transform_reduce, type_t *dest, const container_t l, \
|
||||
M_F(name, _reduce_cb_ct) redFunc, \
|
||||
M_F(name, _transform_cb_ct) mapFunc, void *data) \
|
||||
{ \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
bool initDone = false; \
|
||||
type_t tmp; \
|
||||
M_CALL_INIT(type_oplist, tmp); \
|
||||
for M_EACH(item, l, cont_oplist) { \
|
||||
if (initDone) { \
|
||||
mapFunc(&tmp, *item); \
|
||||
redFunc(dest, tmp); \
|
||||
if (mapFunc(&tmp, *item, data) ) { \
|
||||
redFunc(dest, tmp); \
|
||||
} \
|
||||
} else { \
|
||||
mapFunc(dest, *item); \
|
||||
initDone = true; \
|
||||
initDone = mapFunc(dest, *item, data); \
|
||||
} \
|
||||
} \
|
||||
M_CALL_CLEAR(type_oplist, tmp); \
|
||||
return initDone; \
|
||||
} \
|
||||
, ) \
|
||||
|
||||
@@ -905,8 +908,7 @@
|
||||
/* Define functions based on remove method */
|
||||
#define M_ALG0_DEF_REMOVE(name, container_t, cont_oplist, type_t, type_oplist, it_t) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _uniq)(container_t l) \
|
||||
M_P(void, name, _uniq, container_t l) \
|
||||
{ \
|
||||
it_t it1; \
|
||||
it_t it2; \
|
||||
@@ -927,8 +929,7 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _remove_val)(container_t l, type_t const val) \
|
||||
M_P(void, name, _remove_val, container_t l, type_t const val) \
|
||||
{ \
|
||||
it_t it1; \
|
||||
M_CALL_IT_FIRST(cont_oplist, it1, l); \
|
||||
@@ -942,8 +943,7 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _remove_if)(container_t l, M_F(name, _test_cb_ct) func) \
|
||||
M_P(void, name, _remove_if, container_t l, M_F(name, _test_cb_ct) func) \
|
||||
{ \
|
||||
it_t it1; \
|
||||
M_CALL_IT_FIRST(cont_oplist, it1, l); \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - dynamic ARRAY module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -80,6 +80,7 @@
|
||||
/* OPLIST definition of a dynamic array */
|
||||
/* FIXME: Do we want to export some methods as they are slow and
|
||||
are not fit to be used for building other methods (like _it_remove)? */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_ARRA4_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)) \
|
||||
,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(M_F(name, _init_set)),) \
|
||||
@@ -106,7 +107,7 @@
|
||||
,IT_REF(M_F(name,_ref)) \
|
||||
,IT_CREF(M_F(name,_cref)) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(M_F(name,_insert)) ,) \
|
||||
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(IT_REMOVE(M_F(name,_remove)),) \
|
||||
,IT_REMOVE(M_F(name,_remove)) \
|
||||
,RESET(M_F(name,_reset)) \
|
||||
,KEY_TYPE(size_t) \
|
||||
,VALUE_TYPE(M_F(name, _subtype_ct)) \
|
||||
@@ -115,12 +116,12 @@
|
||||
,M_IF_METHOD(SET, oplist)(SET_KEY(M_F(name, _set_at)) ,) \
|
||||
,GET_KEY(M_F(name, _get)) \
|
||||
,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(M_F(name, _safe_get)) ,) \
|
||||
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(ERASE_KEY(M_F(name, _erase)),) \
|
||||
,ERASE_KEY(M_F(name, _erase)) \
|
||||
,GET_SIZE(M_F(name, _size)) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(PUSH(M_F(name,_push_back)) ,) \
|
||||
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(POP(M_F(name,_pop_back)) ,) \
|
||||
,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(PUSH_MOVE(M_F(name,_push_move)) ,) \
|
||||
,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(POP_MOVE(M_F(name,_pop_move)) ,) \
|
||||
,POP(M_F(name,_pop_back)) \
|
||||
,PUSH_MOVE(M_F(name,_push_move)) \
|
||||
,POP_MOVE(M_F(name,_pop_move)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \
|
||||
@@ -132,11 +133,65 @@
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
|
||||
#else
|
||||
#define M_ARRA4_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)) \
|
||||
,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(API_0P(M_F(name, _init_set))),) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(INIT_WITH(API_1(M_INIT_WITH_VAI)),) \
|
||||
,M_IF_METHOD2(INIT_SET,SET, oplist)(SET(API_0P(M_F(name, _set))), ) \
|
||||
,CLEAR(API_0P(M_F(name, _clear))) \
|
||||
,INIT_MOVE(M_F(name, _init_move)) \
|
||||
,MOVE(API_0P(M_F(name, _move))) \
|
||||
,SWAP(M_F(name, _swap)) \
|
||||
,TYPE(M_F(name,_ct)) , GENTYPE(struct M_F(name,_s)*) \
|
||||
,NAME(name) \
|
||||
,SUBTYPE(M_F(name, _subtype_ct)) \
|
||||
,EMPTY_P(M_F(name,_empty_p)) \
|
||||
,IT_TYPE(M_F(name,_it_ct)), \
|
||||
,IT_FIRST(M_F(name,_it)) \
|
||||
,IT_LAST(M_F(name,_it_last)) \
|
||||
,IT_END(M_F(name,_it_end)) \
|
||||
,IT_SET(M_F(name,_it_set)) \
|
||||
,IT_END_P(M_F(name,_end_p)) \
|
||||
,IT_LAST_P(M_F(name,_last_p)) \
|
||||
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
|
||||
,IT_NEXT(M_F(name,_next)) \
|
||||
,IT_PREVIOUS(M_F(name,_previous)) \
|
||||
,IT_REF(M_F(name,_ref)) \
|
||||
,IT_CREF(M_F(name,_cref)) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(API_0P(M_F(name,_insert))) ,) \
|
||||
,IT_REMOVE(API_0P(M_F(name,_remove))) \
|
||||
,RESET(API_0P(M_F(name,_reset))) \
|
||||
,KEY_TYPE(size_t) \
|
||||
,VALUE_TYPE(M_F(name, _subtype_ct)) \
|
||||
,KEY_OPLIST(M_BASIC_OPLIST) \
|
||||
,VALUE_OPLIST(oplist) \
|
||||
,M_IF_METHOD(SET, oplist)(SET_KEY(API_0P(M_F(name, _set_at))) ,) \
|
||||
,GET_KEY(M_F(name, _get)) \
|
||||
,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(API_0P(M_F(name, _safe_get))) ,) \
|
||||
,ERASE_KEY(API_0P(M_F(name, _erase))) \
|
||||
,GET_SIZE(M_F(name, _size)) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(PUSH(API_0P(M_F(name,_push_back))) ,) \
|
||||
,POP(API_0P(M_F(name,_pop_back))) \
|
||||
,PUSH_MOVE(API_0P(M_F(name,_push_move))) \
|
||||
,POP_MOVE(M_F(name,_pop_move)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(API_0P(M_F(name, _get_str))),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(API_0P(M_F(name, _in_str))),) \
|
||||
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),) \
|
||||
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/********************************** INTERNAL *********************************/
|
||||
|
||||
/* Define the internal contract of an array */
|
||||
//TODO: Can be simplified in release build to speed up compilation speed.
|
||||
#define M_ARRA4_CONTRACT(a) do { \
|
||||
M_ASSERT (a != NULL); \
|
||||
M_ASSERT (a->size <= a->alloc); \
|
||||
@@ -173,7 +228,7 @@
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \
|
||||
M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \
|
||||
M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF)
|
||||
M_EMPLACE_QUEUE_DEF(name, array_t, _emplace_back, oplist, M_ARRA4_EMPLACE_DEF)
|
||||
|
||||
/* Define the types */
|
||||
#define M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \
|
||||
@@ -212,40 +267,37 @@
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(array_t v) \
|
||||
M_P(void, name, _reset, array_t v) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
for(size_t i = 0; i < v->size; i++) \
|
||||
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
||||
v->size = 0; \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(array_t v) \
|
||||
M_P(void, name, _clear, array_t v) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_F(name, _reset)(v); \
|
||||
M_CALL_FREE(oplist, v->ptr); \
|
||||
M_F(name, _reset) M_R(v); \
|
||||
M_CALL_FREE(oplist, type, v->ptr, v->alloc); \
|
||||
/* This is so reusing the object implies an assertion failure */ \
|
||||
v->alloc = 1; \
|
||||
v->ptr = NULL; \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD2(INIT_SET, SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _set)(array_t d, const array_t s) \
|
||||
M_P(void, name, _set, array_t d, const array_t s) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(d); \
|
||||
M_ARRA4_CONTRACT(s); \
|
||||
if (M_UNLIKELY (d == s)) return; \
|
||||
if (s->size > d->alloc) { \
|
||||
const size_t alloc = s->size; \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, d->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
d->ptr = ptr; \
|
||||
d->alloc = alloc; \
|
||||
@@ -254,20 +306,23 @@
|
||||
size_t step1 = M_MIN(s->size, d->size); \
|
||||
for(i = 0; i < step1; i++) \
|
||||
M_CALL_SET(oplist, d->ptr[i], s->ptr[i]); \
|
||||
for( ; i < s->size; i++) \
|
||||
M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]); \
|
||||
for( ; i < d->size; i++) \
|
||||
M_CALL_CLEAR(oplist, d->ptr[i]); \
|
||||
for( ; i < s->size; i++) { \
|
||||
M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]); \
|
||||
M_IF_EXCEPTION( d->size = i + 1 ); \
|
||||
} \
|
||||
d->size = s->size; \
|
||||
M_ARRA4_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _init_set)(array_t d, const array_t s) \
|
||||
M_P(void, name, _init_set, array_t d, const array_t s) \
|
||||
{ \
|
||||
M_ASSERT (d != s); \
|
||||
M_F(name, _init)(d); \
|
||||
M_F(name, _set)(d, s); \
|
||||
M_ON_EXCEPTION(M_F(name, _clear) M_R(d) ) { \
|
||||
M_F(name, _init)(d); \
|
||||
M_F(name, _set) M_R(d, s); \
|
||||
} \
|
||||
} \
|
||||
, /* No SET & INIT_SET */) \
|
||||
\
|
||||
@@ -285,19 +340,18 @@
|
||||
M_ARRA4_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(array_t d, array_t s) \
|
||||
M_P(void, name, _move, array_t d, array_t s) \
|
||||
{ \
|
||||
M_ASSERT (d != s); \
|
||||
M_F(name, _clear)(d); \
|
||||
M_F(name, _clear) M_R(d); \
|
||||
M_F(name, _init_move)(d, s); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _set_at)(array_t v, size_t i, type const x) \
|
||||
M_P(void, name, _set_at, array_t v, size_t i, type const x) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_ASSERT(v->size > 0 && v->ptr != NULL); \
|
||||
M_ASSERT_INDEX(i, v->size); \
|
||||
M_CALL_SET(oplist, v->ptr[i], x); \
|
||||
@@ -313,22 +367,19 @@
|
||||
return &v->ptr[v->size-1]; \
|
||||
} \
|
||||
\
|
||||
M_INLINE type * \
|
||||
M_F(name, _push_raw)(array_t v) \
|
||||
M_P(type *, name, _push_back_raw, array_t v) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
if (M_UNLIKELY (v->size >= v->alloc)) { \
|
||||
M_ASSERT(v->size == v->alloc); \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
|
||||
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL(type, -(size_t)1); \
|
||||
} \
|
||||
M_ASSERT (alloc > v->size); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
@@ -342,43 +393,41 @@
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT_SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _push_back)(array_t v, type const x) \
|
||||
M_P(void, name, _push_back, array_t v, type const x) \
|
||||
{ \
|
||||
type *data = M_F(name, _push_raw)(v); \
|
||||
type *data = M_F(name, _push_back_raw) M_R(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_CALL_INIT_SET(oplist, *data, x); \
|
||||
M_IF_EXCEPTION( v->size --); \
|
||||
M_CALL_INIT_SET(oplist, *data, x); \
|
||||
M_IF_EXCEPTION( v->size ++); \
|
||||
} \
|
||||
, /* No INIT_SET */ ) \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE type * \
|
||||
M_F(name, _push_new)(array_t v) \
|
||||
M_P(type *, name, _push_new, array_t v) \
|
||||
{ \
|
||||
type *data = M_F(name, _push_raw)(v); \
|
||||
type *data = M_F(name, _push_back_raw) M_R(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return NULL; \
|
||||
M_IF_EXCEPTION( v->size --); \
|
||||
M_CALL_INIT(oplist, *data); \
|
||||
M_IF_EXCEPTION( v->size ++); \
|
||||
return data; \
|
||||
} \
|
||||
, /* No INIT */ ) \
|
||||
\
|
||||
M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _push_move)(array_t v, type *x) \
|
||||
M_P(void, name, _push_move, array_t v, type *x) \
|
||||
{ \
|
||||
M_ASSERT (x != NULL); \
|
||||
type *data = M_F(name, _push_raw)(v); \
|
||||
type *data = M_F(name, _push_back_raw) M_R(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_DO_INIT_MOVE (oplist, *data, *x); \
|
||||
M_CALL_INIT_MOVE (oplist, *data, *x); \
|
||||
} \
|
||||
, /* INIT_SET | INIT_MOVE */ ) \
|
||||
\
|
||||
M_IF_METHOD(INIT_SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _push_at)(array_t v, size_t key, type const x) \
|
||||
M_P(void, name, _push_at, array_t v, size_t key, type const x) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_ASSERT_INDEX(key, v->size+1); \
|
||||
@@ -386,29 +435,28 @@
|
||||
M_ASSERT(v->size == v->alloc); \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
|
||||
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
M_MEMORY_FULL(type, -(size_t)1); \
|
||||
} \
|
||||
M_ASSERT (alloc > v->size); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
} \
|
||||
M_ASSERT(v->ptr != NULL); \
|
||||
memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \
|
||||
M_ON_EXCEPTION( memmove(&v->ptr[key], &v->ptr[v->size], sizeof(type))) { \
|
||||
M_CALL_INIT_SET(oplist, v->ptr[key], x); \
|
||||
} \
|
||||
v->size++; \
|
||||
M_CALL_INIT_SET(oplist, v->ptr[key], x); \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
, /* No INIT_SET */ ) \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _resize)(array_t v, size_t size) \
|
||||
M_P(void, name, _resize, array_t v, size_t size) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
if (v->size > size) { \
|
||||
@@ -420,24 +468,24 @@
|
||||
/* Increase size of array */ \
|
||||
if (size > v->alloc) { \
|
||||
size_t alloc = size ; \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
} \
|
||||
for(size_t i = v->size ; i < size; i++) \
|
||||
for(size_t i = v->size ; i < size; i++) { \
|
||||
M_CALL_INIT(oplist, v->ptr[i]); \
|
||||
M_IF_EXCEPTION( v->size = i+1); \
|
||||
} \
|
||||
v->size = size; \
|
||||
} \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
, /* No INIT */ ) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reserve)(array_t v, size_t alloc) \
|
||||
M_P(void, name, _reserve, array_t v, size_t alloc) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
/* NOTE: Reserve below needed size to perform a shrink to fit */ \
|
||||
@@ -445,14 +493,13 @@
|
||||
alloc = v->size; \
|
||||
} \
|
||||
if (M_UNLIKELY (alloc == 0)) { \
|
||||
M_CALL_FREE(oplist, v->ptr); \
|
||||
M_CALL_FREE(oplist, type, v->ptr, v->alloc); \
|
||||
v->size = v->alloc = 0; \
|
||||
v->ptr = NULL; \
|
||||
} else { \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
@@ -461,8 +508,7 @@
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE type * \
|
||||
M_F(name, _safe_get)(array_t v, size_t idx) \
|
||||
M_P(type *, name, _safe_get, array_t v, size_t idx) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
const size_t size = idx + 1; \
|
||||
@@ -473,19 +519,19 @@
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
||||
/* In case of overflow of size_t */ \
|
||||
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL(type, -(size_t)1); \
|
||||
} \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
} \
|
||||
for(size_t i = v->size ; i < size; i++) \
|
||||
for(size_t i = v->size ; i < size; i++) { \
|
||||
M_CALL_INIT(oplist, v->ptr[i]); \
|
||||
M_IF_EXCEPTION( v->size = i+1); \
|
||||
} \
|
||||
v->size = size; \
|
||||
} \
|
||||
M_ASSERT (idx < v->size); \
|
||||
@@ -494,11 +540,10 @@
|
||||
} \
|
||||
, /* No INIT */) \
|
||||
\
|
||||
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_back)(type *dest, array_t v) \
|
||||
M_P(void, name, _pop_back, type *dest, array_t v) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_ASSERT (v->ptr != NULL); \
|
||||
M_ASSERT_INDEX(0, v->size); \
|
||||
v->size--; \
|
||||
@@ -509,9 +554,7 @@
|
||||
} \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
, /* SET | INIT_MOVE */ ) \
|
||||
\
|
||||
M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_move)(type *dest, array_t v) \
|
||||
{ \
|
||||
@@ -520,19 +563,18 @@
|
||||
M_ASSERT_INDEX(0, v->size); \
|
||||
M_ASSERT (dest != NULL); \
|
||||
v->size--; \
|
||||
M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
|
||||
M_CALL_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
, /* INIT_SET | INIT_MOVE */ ) \
|
||||
\
|
||||
/* TODO: Factorize all INIT dependent methods within the same IF*/ \
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_until)(array_t v, it_t pos) \
|
||||
M_P(void, name, _pop_until, array_t v, it_t pos) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_ASSERT (v == pos->array); \
|
||||
M_ASSERT_INDEX(pos->index, v->size+1); \
|
||||
M_F(name, _resize)(v, pos->index); \
|
||||
M_F(name, _resize) M_R(v, pos->index); \
|
||||
} \
|
||||
, /* No INIT */ ) \
|
||||
\
|
||||
@@ -557,11 +599,10 @@
|
||||
return v->alloc; \
|
||||
} \
|
||||
\
|
||||
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_at)(type *dest, array_t v, size_t i) \
|
||||
M_P(void, name, _pop_at, type *dest, array_t v, size_t i) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_ASSERT (v->size > 0 && v->ptr != NULL); \
|
||||
M_ASSERT_INDEX(i, v->size); \
|
||||
if (dest) \
|
||||
@@ -573,19 +614,16 @@
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _erase)(array_t a, size_t i) \
|
||||
M_P(bool, name, _erase, array_t a, size_t i) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(a); \
|
||||
if (i >= a->size) return false; \
|
||||
M_F(name, _pop_at)(NULL, a, i); \
|
||||
M_F(name, _pop_at) M_R(NULL, a, i); \
|
||||
return true; \
|
||||
} \
|
||||
, /* SET | INIT_MOVE */ ) \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _insert_v)(array_t v, size_t i, size_t num) \
|
||||
M_P(void, name, _insert_v, array_t v, size_t i, size_t num) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_ASSERT_INDEX(i, v->size+1); \
|
||||
@@ -594,36 +632,36 @@
|
||||
if (M_UNLIKELY_NOMEM (size <= v->size)) { \
|
||||
/* Unlikely case of nothing to do */ \
|
||||
if (num == 0) return; \
|
||||
M_MEMORY_FULL(sizeof (type) * v->size); \
|
||||
return ; \
|
||||
M_MEMORY_FULL(type, v->size); \
|
||||
} \
|
||||
/* Test if alloc array is sufficient */ \
|
||||
if (size > v->alloc) { \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
||||
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
M_MEMORY_FULL(type, -(size_t)1); \
|
||||
} \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, v->alloc, alloc); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
M_MEMORY_FULL(type, alloc); \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
} \
|
||||
memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) ); \
|
||||
for(size_t k = i ; k < i+num; k++) \
|
||||
M_CALL_INIT(oplist, v->ptr[k]); \
|
||||
m_volatile size_t k; \
|
||||
M_ON_EXCEPTION(memmove(&v->ptr[k], &v->ptr[i+num], sizeof(type)*(v->size - i) ), v->size += (k-i) ) { \
|
||||
for(k = i ; k < i+num; k++) \
|
||||
M_CALL_INIT(oplist, v->ptr[k]); \
|
||||
} \
|
||||
v->size = size; \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
, /* No INIT */) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _remove_v)(array_t v, size_t i, size_t j) \
|
||||
M_P(void, name, _remove_v, array_t v, size_t i, size_t j) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_ASSERT(i < j && v->ptr != NULL); \
|
||||
M_ASSERT_INDEX(i, v->size); \
|
||||
M_ASSERT_INDEX(j, v->size+1); \
|
||||
@@ -646,7 +684,6 @@
|
||||
M_ARRA4_CONTRACT(v2); \
|
||||
} \
|
||||
\
|
||||
M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist) ( \
|
||||
M_INLINE void \
|
||||
M_F(name, _swap_at)(array_t v, size_t i, size_t j) \
|
||||
{ \
|
||||
@@ -655,12 +692,11 @@
|
||||
M_ASSERT_INDEX(i, v->size); \
|
||||
M_ASSERT_INDEX(j, v->size); \
|
||||
type tmp; \
|
||||
M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]); \
|
||||
M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_DO_INIT_MOVE(oplist, v->ptr[j], tmp); \
|
||||
M_CALL_INIT_MOVE(oplist, tmp, v->ptr[i]); \
|
||||
M_CALL_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_CALL_INIT_MOVE(oplist, v->ptr[j], tmp); \
|
||||
M_ARRA4_CONTRACT(v); \
|
||||
} \
|
||||
, /* INIT_SET | INIT_MOVE */ ) \
|
||||
\
|
||||
M_INLINE type * \
|
||||
M_F(name, _get)(const array_t v, size_t i) \
|
||||
@@ -780,23 +816,21 @@
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT_SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _insert)(array_t a, it_t it, type const x) \
|
||||
M_P(void, name, _insert, array_t a, it_t it, type const x) \
|
||||
{ \
|
||||
M_ASSERT (it != NULL && a == it->array); \
|
||||
size_t index = M_F(name, _end_p)(it) ? 0 : it->index+1; \
|
||||
M_F(name, _push_at)(a, index, x); \
|
||||
M_F(name, _push_at)M_R(a, index, x); \
|
||||
it->index = index; \
|
||||
} \
|
||||
, /* End of INIT_SET */ ) \
|
||||
\
|
||||
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _remove)(array_t a, it_t it) \
|
||||
M_P(void, name, _remove, array_t a, it_t it) \
|
||||
{ \
|
||||
M_ASSERT (it != NULL && a == it->array); \
|
||||
M_F(name, _pop_at)(NULL, a, it->index); \
|
||||
/* NOTE: it->index will naturaly point to the next element */ \
|
||||
M_F(name, _pop_at) M_R(NULL, a, it->index); \
|
||||
/* NOTE: it->index will naturally point to the next element */ \
|
||||
} \
|
||||
, /* End of SET | INIT_SET */ ) \
|
||||
\
|
||||
@@ -853,25 +887,23 @@
|
||||
k += n1+n2; \
|
||||
for (;;) { \
|
||||
if (M_CALL_CMP(oplist, *el1, *el2) <= 0) { \
|
||||
M_DO_INIT_MOVE(oplist, *dest, *el1); \
|
||||
M_CALL_INIT_MOVE(oplist, *dest, *el1); \
|
||||
dest++; \
|
||||
el1++; \
|
||||
if (M_UNLIKELY(-- n1 == 0)) { \
|
||||
if (n2 > 0) { \
|
||||
memcpy (dest, el2, n2 * sizeof (type)); \
|
||||
dest += n2; \
|
||||
} \
|
||||
M_ASSERT (n2 > 0); \
|
||||
memcpy (dest, el2, n2 * sizeof (type)); \
|
||||
dest += n2; \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
M_DO_INIT_MOVE(oplist, *dest, *el2); \
|
||||
M_CALL_INIT_MOVE(oplist, *dest, *el2); \
|
||||
dest++; \
|
||||
el2++; \
|
||||
if (M_UNLIKELY(-- n2 == 0)) { \
|
||||
if (n1 > 0) { \
|
||||
memcpy (dest, el1, n1 * sizeof (type)); \
|
||||
dest += n1; \
|
||||
} \
|
||||
M_ASSERT (n1 > 0); \
|
||||
memcpy (dest, el1, n1 * sizeof (type)); \
|
||||
dest += n1; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
@@ -885,19 +917,17 @@
|
||||
M_ASSERT (org_tab == tab); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _special_stable_sort)(array_t l) \
|
||||
M_P(void, name, _special_stable_sort, array_t l) \
|
||||
{ \
|
||||
if (M_UNLIKELY (l->size < 2)) \
|
||||
return; \
|
||||
/* NOTE: if size is <= 4, no need to perform an allocation */ \
|
||||
type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \
|
||||
type *temp = M_CALL_REALLOC(oplist, type, NULL, 0, l->size); \
|
||||
if (M_UNLIKELY_NOMEM (temp == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * l->size); \
|
||||
return ; \
|
||||
M_MEMORY_FULL(type, l->size); \
|
||||
} \
|
||||
M_C3(m_arra4_,name,_stable_sort_noalloc)(l->ptr, l->size, temp); \
|
||||
M_CALL_FREE(oplist, temp); \
|
||||
M_CALL_FREE(oplist, type, temp, l->size); \
|
||||
} \
|
||||
,) /* IF SWAP & SET methods */ \
|
||||
\
|
||||
@@ -918,7 +948,7 @@
|
||||
bool b = M_CALL_EQUAL(oplist, *item1, *item2); \
|
||||
if (!b) return false; \
|
||||
} \
|
||||
return i == array1->size; \
|
||||
return true; \
|
||||
} \
|
||||
, /* no EQUAL */ ) \
|
||||
\
|
||||
@@ -936,21 +966,21 @@
|
||||
} \
|
||||
, /* no HASH */ ) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _splice)(array_t a1, array_t a2) \
|
||||
M_P(void, name, _splice, array_t a1, array_t a2) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(a1); \
|
||||
M_ARRA4_CONTRACT(a2); \
|
||||
M_ASSERT(a1 != a2); \
|
||||
if (M_LIKELY (a2->size > 0)) { \
|
||||
size_t newSize = a1->size + a2->size; \
|
||||
/* To overflow newSize, we need to a1 and a2 a little bit above \
|
||||
SIZE_MAX/2, which is not possible in the classic memory model as we \
|
||||
should have exhausted all memory before reaching such sizes. */ \
|
||||
M_ASSERT(newSize > a1->size); \
|
||||
M_ASSERT_INDEX(a1->size, newSize); \
|
||||
if (newSize > a1->alloc) { \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, a1->alloc, newSize); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * newSize); \
|
||||
M_MEMORY_FULL(type, newSize); \
|
||||
} \
|
||||
a1->ptr = ptr; \
|
||||
a1->alloc = newSize; \
|
||||
@@ -969,12 +999,10 @@
|
||||
#define M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _get_str)(m_string_t str, array_t const array, \
|
||||
bool append) \
|
||||
M_P(void, name, _get_str, m_string_t str, array_t const array, bool append) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(array); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
|
||||
it_t it; \
|
||||
for (M_F(name, _it)(it, array) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
@@ -982,9 +1010,9 @@
|
||||
type const *item = M_F(name, _cref)(it); \
|
||||
M_CALL_GET_STR(oplist, str, *item, true); \
|
||||
if (!M_F(name, _last_p)(it)) \
|
||||
m_string_push_back (str, M_GET_SEPARATOR oplist); \
|
||||
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
m_string_push_back (str, ']'); \
|
||||
m_string_push_back M_R(str, ']'); \
|
||||
} \
|
||||
, /* no GET_STR */ ) \
|
||||
\
|
||||
@@ -1006,24 +1034,23 @@
|
||||
, /* no OUT_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _parse_str)(array_t array, const char str[], const char**endp) \
|
||||
M_P(bool, name, _parse_str, array_t array, const char str[], const char**endp) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(array); \
|
||||
M_ASSERT (str != NULL); \
|
||||
M_F(name,_reset)(array); \
|
||||
M_F(name,_reset)M_R(array); \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (M_UNLIKELY (c == ']')) { goto exit; } \
|
||||
if (M_UNLIKELY (c == 0)) { goto exit; } \
|
||||
str--; \
|
||||
M_QLET(item, type, oplist) { \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (b == false || c == 0) { c = 0; break; } \
|
||||
M_F(name, _push_back)(array, item); \
|
||||
M_F(name, _push_back) M_R(array, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
exit: \
|
||||
@@ -1034,24 +1061,23 @@
|
||||
, /* no PARSE_STR & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_STR, INIT, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _in_str)(array_t array, FILE *file) \
|
||||
M_P(bool, name, _in_str, array_t array, FILE *file) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(array); \
|
||||
M_ASSERT (file != NULL); \
|
||||
M_F(name,_reset)(array); \
|
||||
M_F(name,_reset)M_R(array); \
|
||||
int c = fgetc(file); \
|
||||
if (M_UNLIKELY (c != '[')) return false; \
|
||||
c = fgetc(file); \
|
||||
if (M_UNLIKELY (c == ']')) return true; \
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
M_QLET(item, type, oplist) { \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
c = m_core_fgetc_nospace(file); \
|
||||
if (b == false || c == EOF) { c = 0; break; } \
|
||||
M_F(name, _push_back)(array, item); \
|
||||
M_F(name, _push_back) M_R(array, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
M_ARRA4_CONTRACT(array); \
|
||||
@@ -1060,46 +1086,44 @@
|
||||
, /* no IN_STR & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _out_serial)(m_serial_write_t f, const array_t array) \
|
||||
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const array_t array) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(array); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
ret = f->m_interface->write_array_start(local, f, array->size); \
|
||||
ret = f->m_interface->write_array_start M_R(local, f, array->size); \
|
||||
for (size_t i = 0; i < array->size; i++) { \
|
||||
type const *item = M_F(name, _cget)(array, i); \
|
||||
if (i != 0) { \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= f->m_interface->write_array_next M_R(local, f); \
|
||||
} \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
ret |= f->m_interface->write_array_end M_R(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _in_serial)(array_t array, m_serial_read_t f) \
|
||||
M_P(m_serial_return_code_t, name, _in_serial, array_t array, m_serial_read_t f) \
|
||||
{ \
|
||||
M_ARRA4_CONTRACT(array); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_return_code_t ret; \
|
||||
m_serial_local_t local; \
|
||||
size_t estimated_size = 0; \
|
||||
M_F(name,_reset)(array); \
|
||||
M_F(name,_reset)M_R(array); \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) { \
|
||||
return ret; \
|
||||
} \
|
||||
M_F(name, _reserve)(array, estimated_size); \
|
||||
M_QLET(item, type, oplist) { \
|
||||
M_F(name, _reserve) M_R(array, estimated_size); \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_F(name, _push_back)(array, item); \
|
||||
M_F(name, _push_back)M_R(array, item); \
|
||||
ret = f->m_interface->read_array_next(local, f); \
|
||||
} while (ret == M_SERIAL_OK_CONTINUE); \
|
||||
} \
|
||||
@@ -1110,14 +1134,14 @@
|
||||
|
||||
/* Definition of the emplace_back function for arrays */
|
||||
#define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
|
||||
M_INLINE void \
|
||||
function_name(name_t v \
|
||||
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
|
||||
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
|
||||
{ \
|
||||
M_F(name, _subtype_ct) *data = M_F(name, _push_raw)(v); \
|
||||
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw) M_R(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_IF_EXCEPTION( v->size --); \
|
||||
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
|
||||
M_IF_EXCEPTION( v->size ++); \
|
||||
} \
|
||||
|
||||
/********************************** INTERNAL *********************************/
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Thin stdatomic wrapper for C++ compatibility
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef MSTARLIB_ATOMIC_H
|
||||
#define MSTARLIB_ATOMIC_H
|
||||
|
||||
/* NOTE: Due to the C++ not having recognized stdatomic.h officialy,
|
||||
/* NOTE: Due to the C++ not having recognized stdatomic.h officially,
|
||||
it is hard to use this header directly with a C++ compiler.
|
||||
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932
|
||||
clang++ has no issue with this header but if someone includes
|
||||
@@ -146,7 +146,7 @@ using std::memory_order_seq_cst;
|
||||
#endif
|
||||
|
||||
/* Non working C++ atomic header, nor working stdatomic.h found.
|
||||
Write a compatible layer using mutex as slin as possible.
|
||||
Write a compatible layer using mutex as slim as possible.
|
||||
Supports only up to 64-bits atomic (sizeof long long to be more precise).
|
||||
The locks are never properly cleared and remain active until
|
||||
the end of the program.
|
||||
|
||||
+80
-58
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - BITSET module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -101,11 +101,10 @@ m_bitset_reset(m_bitset_t t)
|
||||
}
|
||||
|
||||
/* Clear a bitset (DESTRUCTOR) */
|
||||
M_INLINE void
|
||||
m_bitset_clear(m_bitset_t t)
|
||||
M_P(void, m_bitset, _clear, m_bitset_t t)
|
||||
{
|
||||
m_bitset_reset(t);
|
||||
M_MEMORY_FREE(t->ptr);
|
||||
M_MEMORY_FREE(m_context, m_b1tset_limb_ct, t->ptr, t->alloc);
|
||||
// This is not really needed, but is safer
|
||||
// This representation is invalid and will be detected by the contract.
|
||||
// A C compiler should be able to optimize out theses initializations.
|
||||
@@ -114,8 +113,7 @@ m_bitset_clear(m_bitset_t t)
|
||||
}
|
||||
|
||||
/* Set a bitset to another one */
|
||||
M_INLINE void
|
||||
m_bitset_set(m_bitset_t d, const m_bitset_t s)
|
||||
M_P(void, m_bitset, _set, m_bitset_t d, const m_bitset_t s)
|
||||
{
|
||||
M_B1TSET_CONTRACT(d);
|
||||
M_B1TSET_CONTRACT(s);
|
||||
@@ -124,10 +122,9 @@ m_bitset_set(m_bitset_t d, const m_bitset_t s)
|
||||
if (M_LIKELY (s->size > 0)) {
|
||||
// Test if enough space in target
|
||||
if (s->size > M_B1TSET_FROM_ALLOC (d->alloc)) {
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, d->ptr, needAlloc);
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, d->ptr, d->alloc, needAlloc);
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
|
||||
M_MEMORY_FULL(needAlloc);
|
||||
return ;
|
||||
M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc);
|
||||
}
|
||||
d->ptr = ptr;
|
||||
d->alloc = needAlloc;
|
||||
@@ -141,12 +138,14 @@ m_bitset_set(m_bitset_t d, const m_bitset_t s)
|
||||
}
|
||||
|
||||
/* Initialize & set a bitset to another one (CONSTRUCTOR) */
|
||||
M_INLINE void
|
||||
m_bitset_init_set(m_bitset_t d, const m_bitset_t s)
|
||||
M_P(void, m_bitset, _init_set, m_bitset_t d, const m_bitset_t s)
|
||||
{
|
||||
M_ASSERT (d != s);
|
||||
// In case of exception, d remains initialized,
|
||||
// but without any allocation done.
|
||||
// Therefore it is safe, not to clear d on exception.
|
||||
m_bitset_init(d);
|
||||
m_bitset_set(d, s);
|
||||
m_bitset_set M_R(d, s);
|
||||
}
|
||||
|
||||
/* Initialize & move a bitset (CONSTRUCTOR) from another one (DESTRUCTOR) */
|
||||
@@ -164,10 +163,9 @@ m_bitset_init_move(m_bitset_t d, m_bitset_t s)
|
||||
}
|
||||
|
||||
/* Move a bitset from another one (DESTRUCTOR) */
|
||||
M_INLINE void
|
||||
m_bitset_move(m_bitset_t d, m_bitset_t s)
|
||||
M_P(void, m_bitset, _move, m_bitset_t d, m_bitset_t s)
|
||||
{
|
||||
m_bitset_clear(d);
|
||||
m_bitset_clear M_R(d);
|
||||
m_bitset_init_move (d, s);
|
||||
}
|
||||
|
||||
@@ -200,8 +198,7 @@ m_bitset_flip_at(m_bitset_t v, size_t i)
|
||||
}
|
||||
|
||||
/* Push back the boolean 'x' in the bitset (increasing the bitset) */
|
||||
M_INLINE void
|
||||
m_bitset_push_back (m_bitset_t v, bool x)
|
||||
M_P(void, m_bitset, _push_back, m_bitset_t v, bool x)
|
||||
{
|
||||
M_B1TSET_CONTRACT (v);
|
||||
if (M_UNLIKELY (v->size >= M_B1TSET_FROM_ALLOC (v->alloc))) {
|
||||
@@ -209,15 +206,13 @@ m_bitset_push_back (m_bitset_t v, bool x)
|
||||
const size_t needAlloc = M_B1TSET_INC_ALLOC_SIZE(v->alloc);
|
||||
// Check for integer overflow
|
||||
if (M_UNLIKELY_NOMEM (needAlloc <= v->alloc)) {
|
||||
M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct));
|
||||
return;
|
||||
M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc);
|
||||
}
|
||||
// Alloc memory
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, needAlloc);
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, needAlloc);
|
||||
// Check if success
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
|
||||
M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct));
|
||||
return;
|
||||
M_MEMORY_FULL(m_b1tset_limb_ct, needAlloc);
|
||||
}
|
||||
v->ptr = ptr;
|
||||
v->alloc = needAlloc;
|
||||
@@ -239,23 +234,20 @@ m_bitset_push_back (m_bitset_t v, bool x)
|
||||
}
|
||||
|
||||
/* Resize the bitset to have exactly 'size' bits */
|
||||
M_INLINE void
|
||||
m_bitset_resize (m_bitset_t v, size_t size)
|
||||
M_P(void, m_bitset, _resize, m_bitset_t v, size_t size)
|
||||
{
|
||||
M_B1TSET_CONTRACT (v);
|
||||
// Check for overflow
|
||||
if (M_UNLIKELY_NOMEM (size >= ((size_t)-1) - M_B1TSET_LIMB_BIT)) {
|
||||
M_MEMORY_FULL((size_t) -1);
|
||||
return;
|
||||
M_MEMORY_FULL(char, -(size_t)1);
|
||||
}
|
||||
// Compute the needed allocation.
|
||||
size_t newAlloc = M_B1TSET_TO_ALLOC (size);
|
||||
if (newAlloc > v->alloc) {
|
||||
// Allocate more limbs to store the bitset.
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc);
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc);
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
|
||||
M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct));
|
||||
return;
|
||||
M_MEMORY_FULL(m_b1tset_limb_ct, newAlloc);
|
||||
}
|
||||
v->ptr = ptr;
|
||||
v->alloc = newAlloc;
|
||||
@@ -286,9 +278,8 @@ m_bitset_resize (m_bitset_t v, size_t size)
|
||||
M_B1TSET_CONTRACT (v);
|
||||
}
|
||||
|
||||
/* Reserve allocation in the bitset to accomodate at least 'size' bits without reallocation */
|
||||
M_INLINE void
|
||||
m_bitset_reserve (m_bitset_t v, size_t alloc)
|
||||
/* Reserve allocation in the bitset to accommodate at least 'size' bits without reallocation */
|
||||
M_P(void, m_bitset, _reserve, m_bitset_t v, size_t alloc)
|
||||
{
|
||||
M_B1TSET_CONTRACT (v);
|
||||
size_t oldAlloc = M_B1TSET_TO_ALLOC (v->size);
|
||||
@@ -299,15 +290,14 @@ m_bitset_reserve (m_bitset_t v, size_t alloc)
|
||||
}
|
||||
if (M_UNLIKELY (newAlloc == 0)) {
|
||||
// Free all memory used by the bitsets
|
||||
M_MEMORY_FREE (v->ptr);
|
||||
M_MEMORY_FREE (m_context, m_b1tset_limb_ct, v->ptr, v->alloc);
|
||||
v->size = v->alloc = 0;
|
||||
v->ptr = NULL;
|
||||
} else {
|
||||
// Allocate more memory or reduce memory usage
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc);
|
||||
m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_context, m_b1tset_limb_ct, v->ptr, v->alloc, newAlloc);
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL) ) {
|
||||
M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct));
|
||||
return;
|
||||
M_MEMORY_FULL(m_b1tset_limb_ct, newAlloc);
|
||||
}
|
||||
v->ptr = ptr;
|
||||
v->alloc = newAlloc;
|
||||
@@ -387,7 +377,7 @@ m_bitset_size(m_bitset_t v)
|
||||
return v->size;
|
||||
}
|
||||
|
||||
/* Return the capacity in limbs of the bitset */
|
||||
/* Return the capacity in bits of the bitset */
|
||||
M_INLINE size_t
|
||||
m_bitset_capacity(m_bitset_t v)
|
||||
{
|
||||
@@ -453,12 +443,11 @@ m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry)
|
||||
|
||||
/* Insert a new bit at position 'key' of value 'value' in the bitset 'set'
|
||||
shifting the set accordingly */
|
||||
M_INLINE void
|
||||
m_bitset_push_at(m_bitset_t set, size_t key, bool value)
|
||||
M_P(void, m_bitset, _push_at, m_bitset_t set, size_t key, bool value)
|
||||
{
|
||||
M_B1TSET_CONTRACT (set);
|
||||
// First push another value to extend the array to the right size
|
||||
m_bitset_push_back(set, false);
|
||||
m_bitset_push_back M_R(set, false);
|
||||
M_ASSERT (set->ptr != NULL);
|
||||
M_ASSERT_INDEX(key, set->size);
|
||||
|
||||
@@ -514,7 +503,7 @@ m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
|
||||
M_B1TSET_CONTRACT (set2);
|
||||
if (set1->size != set2->size)
|
||||
return false;
|
||||
/* We won't compare each bit individualy,
|
||||
/* We won't compare each bit individually,
|
||||
but instead compare them per limb */
|
||||
const size_t limbSize = (set1->size + M_B1TSET_LIMB_BIT -1) / M_B1TSET_LIMB_BIT;
|
||||
for(size_t i = 0 ; i < limbSize;i++)
|
||||
@@ -523,7 +512,7 @@ m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Initialize an iterator to the first bit of the biset */
|
||||
/* Initialize an iterator to the first bit of the bitset */
|
||||
M_INLINE void
|
||||
m_bitset_it(m_bitset_it_t it, m_bitset_t set)
|
||||
{
|
||||
@@ -629,8 +618,7 @@ m_bitset_out_str(FILE *file, const m_bitset_t set)
|
||||
}
|
||||
|
||||
/* Input the bitset from a formatted text in a FILE */
|
||||
M_INLINE bool
|
||||
m_bitset_in_str(m_bitset_t set, FILE *file)
|
||||
M_P(bool, m_bitset, _in_str, m_bitset_t set, FILE *file)
|
||||
{
|
||||
M_B1TSET_CONTRACT (set);
|
||||
M_ASSERT(file != NULL);
|
||||
@@ -640,7 +628,7 @@ m_bitset_in_str(m_bitset_t set, FILE *file)
|
||||
c = fgetc(file);
|
||||
while (c == '0' || c == '1') {
|
||||
const bool b = (c == '1');
|
||||
m_bitset_push_back (set, b);
|
||||
m_bitset_push_back M_R(set, b);
|
||||
c = fgetc(file);
|
||||
}
|
||||
M_B1TSET_CONTRACT (set);
|
||||
@@ -648,8 +636,7 @@ m_bitset_in_str(m_bitset_t set, FILE *file)
|
||||
}
|
||||
|
||||
/* Parse the bitset from a formatted text in a C string */
|
||||
M_INLINE bool
|
||||
m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
|
||||
M_P(bool, m_bitset, _parse_str, m_bitset_t set, const char str[], const char **endptr)
|
||||
{
|
||||
M_B1TSET_CONTRACT (set);
|
||||
M_ASSERT(str != NULL);
|
||||
@@ -661,7 +648,7 @@ m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
|
||||
do {
|
||||
if (M_UNLIKELY(c != '0' && c != '1')) goto exit;
|
||||
const bool b = (c == '1');
|
||||
m_bitset_push_back (set, b);
|
||||
m_bitset_push_back M_R(set, b);
|
||||
c = *str++;
|
||||
} while (c != ']' && c != 0);
|
||||
M_B1TSET_CONTRACT (set);
|
||||
@@ -672,10 +659,9 @@ m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr)
|
||||
}
|
||||
|
||||
/* Set the bitset from a formatted text in a C string */
|
||||
M_INLINE bool
|
||||
m_bitset_set_str(m_bitset_t dest, const char str[])
|
||||
M_P(bool, m_bitset, _set_str, m_bitset_t dest, const char str[])
|
||||
{
|
||||
return m_bitset_parse_str(dest, str, NULL);
|
||||
return m_bitset_parse_str M_R(dest, str, NULL);
|
||||
}
|
||||
|
||||
/* Perform an AND operation between the bitsets,
|
||||
@@ -750,7 +736,7 @@ m_bitset_not(m_bitset_t dest)
|
||||
M_B1TSET_CONTRACT(dest);
|
||||
}
|
||||
|
||||
/* Copute a hash of the bitset */
|
||||
/* Compute a hash of the bitset */
|
||||
M_INLINE size_t
|
||||
m_bitset_hash(const m_bitset_t set)
|
||||
{
|
||||
@@ -848,6 +834,7 @@ m_bitset_popcount(const m_bitset_t set)
|
||||
}
|
||||
|
||||
/* Oplist for a bitset */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_BITSET_OPLIST \
|
||||
(INIT(m_bitset_init) \
|
||||
,INIT_SET(m_bitset_init_set) \
|
||||
@@ -882,6 +869,42 @@ m_bitset_popcount(const m_bitset_t set)
|
||||
,IN_STR(m_bitset_in_str) \
|
||||
,EQUAL(m_bitset_equal_p) \
|
||||
)
|
||||
#else
|
||||
#define M_BITSET_OPLIST \
|
||||
(INIT(m_bitset_init) \
|
||||
,INIT_SET(API_0P(m_bitset_init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
,SET(API_0P(m_bitset_set)) \
|
||||
,CLEAR(API_0P(m_bitset_clear)) \
|
||||
,INIT_MOVE(m_bitset_init_move) \
|
||||
,MOVE(API_0P(m_bitset_move)) \
|
||||
,SWAP(m_bitset_swap) \
|
||||
,TYPE(m_bitset_t) \
|
||||
,SUBTYPE(bool) \
|
||||
,EMPTY_P(m_bitset_empty_p), \
|
||||
,GET_SIZE(m_bitset_size) \
|
||||
,IT_TYPE(m_bitset_it_t) \
|
||||
,IT_FIRST(m_bitset_it) \
|
||||
,IT_SET(m_bitset_it_set) \
|
||||
,IT_LAST(m_bitset_it_last) \
|
||||
,IT_END(m_bitset_it_end) \
|
||||
,IT_END_P(m_bitset_end_p) \
|
||||
,IT_LAST_P(m_bitset_last_p) \
|
||||
,IT_EQUAL_P(m_bitset_it_equal_p) \
|
||||
,IT_NEXT(m_bitset_next) \
|
||||
,IT_PREVIOUS(m_bitset_previous) \
|
||||
,IT_CREF(m_bitset_cref) \
|
||||
,RESET(m_bitset_reset) \
|
||||
,PUSH(API_0P(m_bitset_push_back)) \
|
||||
,POP(m_bitset_pop_back) \
|
||||
,HASH(m_bitset_hash) \
|
||||
,GET_STR(API_0P(m_bitset_get_str)) \
|
||||
,OUT_STR(m_bitset_out_str) \
|
||||
,PARSE_STR(API_0P(m_bitset_parse_str)) \
|
||||
,IN_STR(API_0P(m_bitset_in_str)) \
|
||||
,EQUAL(m_bitset_equal_p) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/* Register the OPLIST as a global one */
|
||||
#define M_OPL_m_bitset_t() M_BITSET_OPLIST
|
||||
@@ -961,19 +984,18 @@ M_END_PROTECTED_CODE
|
||||
M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* Output to a m_string_t 'str' the formatted text representation of the bitset 'set'
|
||||
or append it to the strinf (append=true) */
|
||||
M_INLINE void
|
||||
m_bitset_get_str(m_string_t str, const m_bitset_t set, bool append)
|
||||
or append it to the string (append=true) */
|
||||
M_P(void, m_bitset, _get_str, m_string_t str, const m_bitset_t set, bool append)
|
||||
{
|
||||
M_B1TSET_CONTRACT (set);
|
||||
M_ASSERT(str != NULL);
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "[");
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "[");
|
||||
for(size_t i = 0; i < set->size; i++) {
|
||||
const bool b = m_bitset_get (set, i);
|
||||
const char c = b ? '1' : '0';
|
||||
m_string_push_back (str, c);
|
||||
m_string_push_back M_R(str, c);
|
||||
}
|
||||
m_string_push_back (str, ']');
|
||||
m_string_push_back M_R(str, ']');
|
||||
}
|
||||
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
+210
-152
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - B+TREE module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -147,7 +147,7 @@
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_BPTR33_KEY_OPLIST_P1(arg) M_BPTR33_KEY_OPLIST_P2 arg
|
||||
|
||||
/* Validation of the given oplists */
|
||||
/* Validation of the given oplist */
|
||||
#define M_BPTR33_KEY_OPLIST_P2(name, oplist) \
|
||||
M_IF_OPLIST(oplist)(M_BPTR33_KEY_OPLIST_P3, M_BPTR33_KEY_OPLIST_FAILURE)(name, oplist)
|
||||
|
||||
@@ -156,6 +156,7 @@
|
||||
((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
/* OPLIST definition of a b+tree (global tree) */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_BPTR33_KEY_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)), \
|
||||
INIT_SET(M_F(name, _init_set)), \
|
||||
@@ -189,7 +190,40 @@
|
||||
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
|
||||
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
|
||||
#else
|
||||
#define M_BPTR33_KEY_OPLIST_P3(name, oplist) \
|
||||
(INIT(API_0P(M_F(name, _init))), \
|
||||
INIT_SET(API_0P(M_F(name, _init_set))), \
|
||||
INIT_WITH(API_1(M_INIT_VAI)), \
|
||||
SET(API_0P(M_F(name, _set))), \
|
||||
CLEAR(API_0P(M_F(name, _clear))), \
|
||||
INIT_MOVE(M_F(name, _init_move)), \
|
||||
MOVE(API_0P(M_F(name, _move))), \
|
||||
SWAP(M_F(name, _swap)), \
|
||||
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
|
||||
SUBTYPE(M_F(name, _subtype_ct)), \
|
||||
IT_TYPE(M_F(name, _it_ct)), \
|
||||
IT_FIRST(M_F(name,_it)), \
|
||||
IT_SET(M_F(name,_it_set)), \
|
||||
IT_END(M_F(name,_it_end)), \
|
||||
IT_END_P(M_F(name,_end_p)), \
|
||||
IT_EQUAL_P(M_F(name,_it_equal_p)), \
|
||||
IT_NEXT(M_F(name,_next)), \
|
||||
IT_CREF(M_F(name,_cref)), \
|
||||
RESET(API_0P(M_F(name,_reset))), \
|
||||
PUSH(API_0P(M_F(name,_push))), \
|
||||
GET_MIN(M_F(name,_min)), \
|
||||
GET_MAX(M_F(name,_max)), \
|
||||
M_IF_METHOD(GET_STR, oplist)(API_0P(GET_STR(M_F(name, _get_str))),), \
|
||||
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
|
||||
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),), \
|
||||
M_IF_METHOD(IN_STR, oplist)(IN_STR(API_0P(M_F(name, _in_str))),), \
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
|
||||
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
|
||||
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
|
||||
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/* Deferred evaluation */
|
||||
#define M_BPTR33_OPLIST2_P1(arg) M_BPTR33_OPLIST2_P2 arg
|
||||
@@ -205,6 +239,7 @@
|
||||
((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, key_oplist, value_oplist)))
|
||||
|
||||
/* Final definition of the oplist (associative array) */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_BPTR33_OPLIST2_P4(name, key_oplist, value_oplist) \
|
||||
(INIT(M_F(name, _init)), \
|
||||
INIT_SET(M_F(name, _init_set)), \
|
||||
@@ -233,7 +268,7 @@
|
||||
VALUE_TYPE(M_F(name, _value_ct)), \
|
||||
SET_KEY(M_F(name, _set_at)), \
|
||||
GET_KEY(M_F(name, _get)), \
|
||||
SAFE_GET_KEY(M_F(name, _safe_get)) \
|
||||
SAFE_GET_KEY(M_F(name, _safe_get)), \
|
||||
ERASE_KEY(M_F(name, _erase)), \
|
||||
KEY_OPLIST(key_oplist), \
|
||||
VALUE_OPLIST(value_oplist), \
|
||||
@@ -245,7 +280,47 @@
|
||||
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_F(name, _equal_p)),), \
|
||||
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
|
||||
#else
|
||||
#define M_BPTR33_OPLIST2_P4(name, key_oplist, value_oplist) \
|
||||
(INIT(API_0P(M_F(name, _init))), \
|
||||
INIT_SET(API_0P(M_F(name, _init_set))), \
|
||||
INIT_WITH(API_1(M_INIT_KEY_VAI)), \
|
||||
SET(API_0P(M_F(name, _set))), \
|
||||
CLEAR(API_0P(M_F(name, _clear))), \
|
||||
INIT_MOVE(M_F(name, _init_move)), \
|
||||
MOVE(API_0P(M_F(name, _move))), \
|
||||
SWAP(M_F(name, _swap)), \
|
||||
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
|
||||
SUBTYPE(M_F(name, _subtype_ct)), \
|
||||
EMPTY_P(M_F(name,_empty_p)), \
|
||||
IT_TYPE(M_F(name, _it_ct)), \
|
||||
IT_FIRST(M_F(name,_it)), \
|
||||
IT_SET(M_F(name,_it_set)), \
|
||||
IT_END(M_F(name,_it_end)), \
|
||||
IT_END_P(M_F(name,_end_p)), \
|
||||
IT_EQUAL_P(M_F(name,_it_equal_p)), \
|
||||
IT_NEXT(M_F(name,_next)), \
|
||||
IT_CREF(M_F(name,_cref)), \
|
||||
RESET(API_0P(M_F(name,_reset))), \
|
||||
GET_MIN(M_F(name,_min)), \
|
||||
GET_MAX(M_F(name,_max)), \
|
||||
KEY_TYPE(M_F(name, _key_ct)), \
|
||||
VALUE_TYPE(M_F(name, _value_ct)), \
|
||||
SET_KEY(API_0P(M_F(name, _set_at))), \
|
||||
GET_KEY(M_F(name, _get)), \
|
||||
SAFE_GET_KEY(API_0P(M_F(name, _safe_get))), \
|
||||
ERASE_KEY(API_0P(M_F(name, _erase))), \
|
||||
KEY_OPLIST(key_oplist), \
|
||||
VALUE_OPLIST(value_oplist), \
|
||||
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
|
||||
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_F(name, _out_str)),), \
|
||||
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(API_0P(M_F(name, _in_str))),), \
|
||||
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
|
||||
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
|
||||
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_F(name, _equal_p)),), \
|
||||
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/******************************** INTERNAL ***********************************/
|
||||
|
||||
@@ -343,6 +418,7 @@
|
||||
M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
||||
M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
||||
M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(M_INV(isMap), name, tree_t, key_oplist, value_oplist)
|
||||
/* TODO: Check if key type has not disabled INIT_MOVE */
|
||||
|
||||
/* Define the types of a B+Tree */
|
||||
#define M_BPTR33_DEF_TYPE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
||||
@@ -406,23 +482,22 @@
|
||||
\
|
||||
/* Allocate a new node */ \
|
||||
/* TODO: Can be specialized to alloc for leaf or for non leaf */ \
|
||||
M_INLINE node_t M_F(name, _new_node)(void) \
|
||||
M_INLINE node_t M_F(name, _new_node)(M_P_EXPAND_void) \
|
||||
{ \
|
||||
M_STATIC_ASSERT(N >= 2, M_LIB_ILLEGAL_PARAM, \
|
||||
"Number of items per node shall be >= 2."); \
|
||||
node_t n = M_CALL_NEW(key_oplist, struct M_F(name, _node_s)); \
|
||||
if (M_UNLIKELY_NOMEM (n == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (node_t)); \
|
||||
M_ASSERT (0); \
|
||||
M_MEMORY_FULL(node_t, 1); \
|
||||
} \
|
||||
n->next = NULL; \
|
||||
n->num = 0; \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _init)(tree_t b) \
|
||||
M_P(void, name, _init, tree_t b) \
|
||||
{ \
|
||||
b->root = M_F(name, _new_node)(); \
|
||||
b->root = M_F(name, _new_node)(M_R_EXPAND_void); \
|
||||
b->size = 0; \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
@@ -443,7 +518,7 @@
|
||||
return num; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _reset)(tree_t b) \
|
||||
M_P(void, name, _reset, tree_t b) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
node_t next, n = b->root; \
|
||||
@@ -486,19 +561,19 @@
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _clear)(tree_t b) \
|
||||
M_P(void, name, _clear, tree_t b) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
M_F(name, _reset)(b); \
|
||||
M_F(name, _reset)M_R(b); \
|
||||
/* Once the tree is clean, only the root remains */ \
|
||||
M_CALL_DEL(key_oplist, b->root); \
|
||||
b->root = NULL; \
|
||||
} \
|
||||
\
|
||||
/* Copy recursively the node 'o' of root node 'root' */ \
|
||||
M_INLINE node_t M_F(name, _copy_node)(const node_t o, const node_t root) \
|
||||
M_P(node_t, name, _copy_node, const node_t o, const node_t root) \
|
||||
{ \
|
||||
node_t n = M_F(name, _new_node)(); \
|
||||
node_t n = M_F(name, _new_node)(M_R_EXPAND_void); \
|
||||
/* Set default number of keys and type to copy */ \
|
||||
n->num = o->num; \
|
||||
/* By default it is not linked to its brother. \
|
||||
@@ -520,7 +595,7 @@
|
||||
/* Copy recursively the associated nodes if it is not a leaf */ \
|
||||
for(int i = 0; i <= num; i++) { \
|
||||
M_ASSERT(o->kind.node[i] != root); \
|
||||
n->kind.node[i] = M_F(name, _copy_node)(o->kind.node[i], root); \
|
||||
n->kind.node[i] = M_F(name, _copy_node)M_R(o->kind.node[i], root); \
|
||||
} \
|
||||
/* The copied nodes don't have their next field correct */ \
|
||||
/* Fix the next field for the copied nodes */ \
|
||||
@@ -542,22 +617,22 @@
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _init_set)(tree_t b, const tree_t o) \
|
||||
M_P(void, name, _init_set, tree_t b, const tree_t o) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, o); \
|
||||
M_ASSERT (b != NULL); \
|
||||
/* Just copy recursively the root node */ \
|
||||
b->root = M_F(name, _copy_node)(o->root, o->root); \
|
||||
b->root = M_F(name, _copy_node)M_R(o->root, o->root); \
|
||||
b->size = o->size; \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _set)(tree_t b, const tree_t o) \
|
||||
M_P(void, name, _set, tree_t b, const tree_t o) \
|
||||
{ \
|
||||
/* NOTE: We could reuse the already allocated nodes of 'b'. \
|
||||
Not sure if it worth the effort */ \
|
||||
M_F(name, _clear)(b); \
|
||||
M_F(name, _init_set)(b, o); \
|
||||
M_F(name, _clear)M_R(b); \
|
||||
M_F(name, _init_set)M_R(b, o); \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool M_F(name, _empty_p)(const tree_t b) \
|
||||
@@ -573,7 +648,7 @@
|
||||
return b->size; \
|
||||
} \
|
||||
\
|
||||
M_INLINE node_t M_F(name, _search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
|
||||
M_INLINE node_t M_F(name, _i_search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
|
||||
{ \
|
||||
node_t n = b->root; \
|
||||
int np = 0; \
|
||||
@@ -607,7 +682,7 @@
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
pit_t pit; \
|
||||
/* Get the leaf node where the key can be */ \
|
||||
node_t n = M_F(name, _search_for_leaf)(pit, b, key); \
|
||||
node_t n = M_F(name, _i_search_for_leaf)(pit, b, key); \
|
||||
int cmp = 0; \
|
||||
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
||||
/* Search in the leaf for key */ \
|
||||
@@ -627,11 +702,11 @@
|
||||
return M_CONST_CAST(value_t, M_F(name, _get)(b, key)); \
|
||||
} \
|
||||
\
|
||||
M_INLINE int \
|
||||
M_F(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
|
||||
M_P(int, name, _i_search_and_insert_in_leaf, node_t n, key_t const key \
|
||||
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
|
||||
{ \
|
||||
M_ASSERT (M_F(name, _is_leaf)(n)); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
int i, num = M_F(name, _get_num)(n); \
|
||||
M_ASSERT (num <= N); \
|
||||
/* Search for the key in the node n (a leaf) for insertion */ \
|
||||
@@ -659,10 +734,10 @@
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
M_INLINE int \
|
||||
M_F(name, _search_and_insert_in_node)(node_t n, node_t l, key_t key) \
|
||||
M_P(int, name, _i_search_and_insert_in_node, node_t n, node_t l, key_t key) \
|
||||
{ \
|
||||
M_ASSERT (!M_F(name, _is_leaf)(n)); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
int i, num = M_F(name, _get_num)(n); \
|
||||
M_ASSERT (num <= N); \
|
||||
/* Search for the key in the node n (not a leaf) for insertion */ \
|
||||
@@ -682,16 +757,16 @@
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_IF(isMap)(M_F(name, _set_at),M_F(name,_push))(tree_t b, key_t const key \
|
||||
M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
|
||||
M_IF(isMap)( \
|
||||
M_P(void, name, _set_at, tree_t b, key_t const key, value_t const value) , \
|
||||
M_P(void, name, _push, tree_t b, key_t const key) ) \
|
||||
{ \
|
||||
pit_t pit; \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \
|
||||
node_t leaf = M_F(name, _i_search_for_leaf)(pit, b, key); \
|
||||
/* Insert key into the leaf.*/ \
|
||||
/* NOTE: Even if there is N elements, we can still add one more.*/ \
|
||||
int i = M_F(name, _search_and_insert_in_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
|
||||
int i = M_F(name, _i_search_and_insert_in_leaf)M_R(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
|
||||
if (i < 0) { \
|
||||
/* Nothing to do anymore. key already exists in the tree. \
|
||||
value has been updated if needed */ \
|
||||
@@ -713,7 +788,7 @@
|
||||
/* leaf is full: need to slip the leaf in two */ \
|
||||
int nnum = (N + 1) / 2; \
|
||||
num = N + 1 - nnum; \
|
||||
node_t nleaf = M_F(name, _new_node)(); \
|
||||
node_t nleaf = M_F(name, _new_node)(M_R_EXPAND_void); \
|
||||
/* Move half objects to the new node */ \
|
||||
memmove(&nleaf->key[0], &leaf->key[num], sizeof(key_t)*(unsigned int)nnum); \
|
||||
M_IF(isMap)(memmove(&nleaf->kind.value[0], &leaf->kind.value[num], sizeof(value_t)*(unsigned int)nnum);,) \
|
||||
@@ -728,7 +803,7 @@
|
||||
while (true) { \
|
||||
if (pit->num == 0) { \
|
||||
/* We reach root ==> Need to increase the height of the tree.*/ \
|
||||
node_t parent = M_F(name, _new_node)(); \
|
||||
node_t parent = M_F(name, _new_node)(M_R_EXPAND_void); \
|
||||
parent->num = 1; \
|
||||
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
||||
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
|
||||
@@ -741,7 +816,7 @@
|
||||
/* Non root node. Get the parent node */ \
|
||||
node_t parent = pit->parent[--pit->num]; \
|
||||
/* Insert into parent (It is big enough to receive temporary one more) */ \
|
||||
i = M_F(name, _search_and_insert_in_node)(parent, leaf, *key_ptr); \
|
||||
i = M_F(name, _i_search_and_insert_in_node)M_R(parent, leaf, *key_ptr); \
|
||||
parent->kind.node[i] = leaf; \
|
||||
parent->kind.node[i+1] = nleaf; \
|
||||
/* Test if parent node is full? */ \
|
||||
@@ -754,7 +829,7 @@
|
||||
int nnp = N / 2; \
|
||||
int np = N - nnp; \
|
||||
M_ASSERT (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
|
||||
node_t nparent = M_F(name, _new_node)(); \
|
||||
node_t nparent = M_F(name, _new_node)(M_R_EXPAND_void); \
|
||||
/* Move half items to new node (Like a classic B-TREE) \
|
||||
and the median key to the grand-parent*/ \
|
||||
memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*(unsigned int)nnp); \
|
||||
@@ -772,7 +847,7 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE value_t *M_F(name, _safe_get)(tree_t b, key_t const key) \
|
||||
M_P(value_t *, name, _safe_get, tree_t b, key_t const key) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
/* Not optimized implementation */ \
|
||||
@@ -781,7 +856,7 @@
|
||||
M_IF(isMap)( \
|
||||
value_t v; \
|
||||
M_CALL_INIT(value_oplist, v); \
|
||||
M_F(name, _set_at)(b, key, v); \
|
||||
M_F(name, _set_at)M_R(b, key, v); \
|
||||
M_CALL_CLEAR(value_oplist, v); \
|
||||
, \
|
||||
M_F(name, _push)(b, key); \
|
||||
@@ -791,10 +866,10 @@
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
M_INLINE int \
|
||||
M_F(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \
|
||||
M_P(int, name, _i_search_and_remove_in_leaf, node_t n, key_t const key) \
|
||||
{ \
|
||||
M_ASSERT(M_F(name, _is_leaf)(n)); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
const int num = M_F(name, _get_num)(n); \
|
||||
for(int i = 0; i < num; i++) { \
|
||||
const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||
@@ -811,10 +886,11 @@
|
||||
return -1; /* Not found */ \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _left_shift)(node_t parent, int k) \
|
||||
M_P(void, name, _i_left_shift, node_t parent, int k) \
|
||||
{ \
|
||||
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
|
||||
M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
node_t left = parent->kind.node[k]; \
|
||||
node_t right = parent->kind.node[k+1]; \
|
||||
M_ASSERT (left != NULL && right != NULL); \
|
||||
@@ -846,10 +922,11 @@
|
||||
M_ASSERT (left->num != 0); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _right_shift)(node_t parent, int k) \
|
||||
M_P(void, name, _i_right_shift, node_t parent, int k) \
|
||||
{ \
|
||||
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
|
||||
M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
node_t left = parent->kind.node[k]; \
|
||||
node_t right = parent->kind.node[k+1]; \
|
||||
M_ASSERT (left != NULL && right != NULL); \
|
||||
@@ -880,7 +957,7 @@
|
||||
M_ASSERT (left->num != 0); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name, _merge_node)(node_t parent, int k, bool leaf) \
|
||||
M_P(void, name, _i_merge_node, node_t parent, int k, bool leaf) \
|
||||
{ \
|
||||
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
|
||||
M_ASSERT (0 <= k && k < M_F(name, _get_num(parent))); \
|
||||
@@ -916,7 +993,7 @@
|
||||
/* We can also cache the index when we descend the tree. \
|
||||
TODO: Bench if this is worth the effort.*/ \
|
||||
M_INLINE int \
|
||||
M_F(name, _search_for_node)(node_t parent, node_t child) \
|
||||
M_F(name, _i_search_for_node)(node_t parent, node_t child) \
|
||||
{ \
|
||||
M_ASSERT (!M_F(name, _is_leaf)(parent)); \
|
||||
int i = 0; \
|
||||
@@ -928,12 +1005,12 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool M_F(name, _erase)(tree_t b, key_t const key) \
|
||||
M_P(bool, name, _erase, tree_t b, key_t const key) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
pit_t pit; \
|
||||
node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \
|
||||
int k = M_F(name, _search_and_remove_in_leaf)(leaf, key); \
|
||||
node_t leaf = M_F(name, _i_search_for_leaf)(pit, b, key); \
|
||||
int k = M_F(name, _i_search_and_remove_in_leaf)M_R(leaf, key); \
|
||||
/* If key is not found ==> erase failed */ \
|
||||
if (k < 0) return false; \
|
||||
/* Remove one item from the B+TREE */ \
|
||||
@@ -949,15 +1026,15 @@
|
||||
/* Search for node 'leaf' in parent */ \
|
||||
node_t parent = pit->parent[--pit->num]; \
|
||||
M_ASSERT (parent != NULL); \
|
||||
k = M_F(name, _search_for_node)(parent, leaf); \
|
||||
/* Look for the neighboor of the removed key. */ \
|
||||
k = M_F(name, _i_search_for_node)(parent, leaf); \
|
||||
/* Look for the neighbour of the removed key. */ \
|
||||
/* if we can steal one key from them to keep our node balanced */ \
|
||||
if (k > 0 && M_F(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
|
||||
M_F(name, _left_shift)(parent, k-1); \
|
||||
M_F(name, _i_left_shift)M_R(parent, k-1); \
|
||||
return true; \
|
||||
} else if (k < M_F(name, _get_num)(parent) \
|
||||
&& M_F(name, _get_num)(parent->kind.node[k+1]) > N/2) { \
|
||||
M_F(name, _right_shift)(parent, k); \
|
||||
M_F(name, _i_right_shift)M_R(parent, k); \
|
||||
return true; \
|
||||
} \
|
||||
/* Merge both nodes, removing 'k' from parent */ \
|
||||
@@ -965,7 +1042,7 @@
|
||||
k--; \
|
||||
M_ASSERT(k >= 0 && k < M_F(name, _get_num)(parent)); \
|
||||
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
|
||||
M_F(name, _merge_node)(parent, k, pass1); \
|
||||
M_F(name, _i_merge_node)M_R(parent, k, pass1); \
|
||||
/* Check if we need to continue */ \
|
||||
if (M_F(name, _get_num)(parent) >= N/2) \
|
||||
return true; \
|
||||
@@ -984,16 +1061,17 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool M_F(name, _pop_at)(value_t *ptr, tree_t b, key_t const key) \
|
||||
M_P(bool, name, _pop_at, value_t *ptr, tree_t b, key_t const key) \
|
||||
{ \
|
||||
if (ptr != NULL) { \
|
||||
/* Not optimized tree parsing */ \
|
||||
value_t *ref = M_F(name, _get)(b, key); \
|
||||
if (ref == NULL) { \
|
||||
return false; \
|
||||
} \
|
||||
M_CALL_SET(value_oplist, *ptr, *ref); \
|
||||
} \
|
||||
return M_F(name, _erase)(b, key); \
|
||||
return M_F(name, _erase)M_R(b, key); \
|
||||
} \
|
||||
\
|
||||
M_INLINE value_t * \
|
||||
@@ -1042,16 +1120,16 @@
|
||||
b->size = ref->size; \
|
||||
b->root = ref->root; \
|
||||
ref->root = NULL; \
|
||||
ref->size = 1; \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(tree_t b, tree_t ref) \
|
||||
M_P(void, name, _move, tree_t b, tree_t ref) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \
|
||||
M_ASSERT (b != ref); \
|
||||
M_F(name,_clear)(b); \
|
||||
M_F(name,_clear)M_R(b); \
|
||||
M_F(name,_init_move)(b, ref); \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
} \
|
||||
@@ -1160,7 +1238,7 @@
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
||||
M_ASSERT (it != NULL); \
|
||||
pit_t pit; \
|
||||
node_t n = M_F(name, _search_for_leaf)(pit, b, key); \
|
||||
node_t n = M_F(name, _i_search_for_leaf)(pit, b, key); \
|
||||
it->node = n; \
|
||||
int i; \
|
||||
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
||||
@@ -1209,7 +1287,7 @@
|
||||
it_t it1; \
|
||||
it_t it2; \
|
||||
/* NOTE: We can't compare two trees directly as they can be \
|
||||
structuraly different but functionnaly equal (you get this by \
|
||||
structurally different but functionally equal (you get this by \
|
||||
constructing the tree in a different way). We have to \
|
||||
compare the ordered value within the tree. */ \
|
||||
M_F(name, _it)(it1, t1); \
|
||||
@@ -1258,30 +1336,29 @@
|
||||
, /* NO HASH METHOD */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
|
||||
M_INLINE void M_F(name, _get_str)(m_string_t str, \
|
||||
const tree_t t1, bool append) { \
|
||||
M_P(void, name, _get_str, m_string_t str, const tree_t t1, bool append) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
M_ASSERT(str != NULL); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
|
||||
/* NOTE: The print is really naive, and not really efficient */ \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
|
||||
bool commaToPrint = false; \
|
||||
it_t it; \
|
||||
for (M_F(name, _it)(it, t1) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
M_F(name, _next)(it)) { \
|
||||
if (commaToPrint) \
|
||||
m_string_push_back (str, M_GET_SEPARATOR key_oplist); \
|
||||
m_string_push_back M_R(str, M_GET_SEPARATOR key_oplist); \
|
||||
commaToPrint = true; \
|
||||
subtype_t const *ref1 = M_F(name, _cref)(it); \
|
||||
M_IF(isMap)( \
|
||||
M_CALL_GET_STR(key_oplist, str, *ref1->key_ptr, true); \
|
||||
m_string_cat_cstr(str, ":"); \
|
||||
m_string_cat_cstr M_R(str, ":"); \
|
||||
M_CALL_GET_STR(value_oplist,str, *ref1->value_ptr, true) \
|
||||
, \
|
||||
M_CALL_GET_STR(key_oplist, str, *ref1, true); \
|
||||
); \
|
||||
} \
|
||||
m_string_push_back (str, ']'); \
|
||||
m_string_push_back M_R(str, ']'); \
|
||||
} \
|
||||
, /* NO GET_STR */ ) \
|
||||
\
|
||||
@@ -1314,88 +1391,74 @@
|
||||
, /* no out_str */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _parse_str)(tree_t t1, const char str[], const char **endp) \
|
||||
M_P(bool, name, _parse_str, tree_t t1, const char str[], const char **endp) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
M_ASSERT (str != NULL); \
|
||||
M_F(name,_reset)(t1); \
|
||||
bool success = false; \
|
||||
M_F(name,_reset)M_R(t1); \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) goto exit; \
|
||||
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
|
||||
c = *str++; \
|
||||
if (M_UNLIKELY (c == ']')) { success = true; goto exit;} \
|
||||
if (M_UNLIKELY (c == 0)) goto exit; \
|
||||
if (M_UNLIKELY (c == ']' || c == 0)) goto exit; \
|
||||
str--; \
|
||||
key_t key; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
M_IF(isMap)(value_t value; \
|
||||
M_CALL_INIT(value_oplist, value); \
|
||||
, /* No isMap */) \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false) goto exit_clear; \
|
||||
M_IF(isMap)(if (c != ':') goto exit_clear; \
|
||||
b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false || c == 0) goto exit_clear; \
|
||||
M_F(name, _set_at)(t1, key, value); \
|
||||
, \
|
||||
M_F(name, _push)(t1, key); \
|
||||
) \
|
||||
} while (c == M_GET_SEPARATOR key_oplist); \
|
||||
success = (c == ']'); \
|
||||
exit_clear: \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); , /* No isMap */ ) \
|
||||
M_QLET(1, key, key_t, key_oplist) \
|
||||
M_IF(isMap)(M_QLET(2, value, value_t, value_oplist), /* No isMap */) { \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (b == false) { c = 0; break; } \
|
||||
M_IF(isMap)(if (c != ':') { c = 0; break; } \
|
||||
b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (b == false || c == 0) { c = 0; break; } \
|
||||
M_F(name, _set_at)M_R(t1, key, value); \
|
||||
, \
|
||||
M_F(name, _push)M_R(t1, key); \
|
||||
) \
|
||||
} while (c == M_GET_SEPARATOR key_oplist); \
|
||||
} \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
return (c == ']'); \
|
||||
} \
|
||||
, /* no parse_str */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _in_str)(tree_t t1, FILE *file) \
|
||||
M_P(bool, name, _in_str, tree_t t1, FILE *file) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
M_ASSERT (file != NULL); \
|
||||
M_F(name,_reset)(t1); \
|
||||
M_F(name,_reset)M_R(t1); \
|
||||
int c = fgetc(file); \
|
||||
if (M_UNLIKELY (c != '[')) return false; \
|
||||
c = fgetc(file); \
|
||||
if (M_UNLIKELY (c == ']')) return true; \
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
key_t key; \
|
||||
M_CALL_INIT (key_oplist, key); \
|
||||
M_IF(isMap)(value_t value; \
|
||||
M_CALL_INIT (value_oplist, value); \
|
||||
,) \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(key_oplist, key, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false) break; \
|
||||
M_IF(isMap)(if (c!=':') break; \
|
||||
b = M_CALL_IN_STR(value_oplist,value, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) break; \
|
||||
M_F(name, _set_at)(t1, key, value) \
|
||||
, \
|
||||
M_F(name, _push)(t1, key) \
|
||||
); \
|
||||
} while (c == M_GET_SEPARATOR key_oplist); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); \
|
||||
,) \
|
||||
M_QLET(1, key, key_t, key_oplist) \
|
||||
M_IF(isMap)(M_QLET(2, value, value_t, value_oplist) , /*nothing*/) { \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(key_oplist, key, file); \
|
||||
c = m_core_fgetc_nospace(file); \
|
||||
if (b == false) { c = 0; break; } \
|
||||
M_IF(isMap)(if (c!=':') { c = 0; break; } \
|
||||
b = M_CALL_IN_STR(value_oplist,value, file); \
|
||||
c = m_core_fgetc_nospace(file); \
|
||||
if (b == false || c == EOF) { c = 0; break; } \
|
||||
M_F(name, _set_at)M_R(t1, key, value) \
|
||||
, \
|
||||
M_F(name, _push)M_R(t1, key) \
|
||||
); \
|
||||
} while (c == M_GET_SEPARATOR key_oplist); \
|
||||
} \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no in_str */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
|
||||
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, tree_t const t1) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
@@ -1407,76 +1470,71 @@
|
||||
/* Format is different between associative container \
|
||||
& set container */ \
|
||||
M_IF(isMap)( \
|
||||
ret = f->m_interface->write_map_start(local, f, t1->size); \
|
||||
ret = f->m_interface->write_map_start M_R(local, f, t1->size); \
|
||||
for (M_F(name, _it)(it, t1) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
M_F(name, _next)(it)){ \
|
||||
item = M_F(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_map_next(local, f); \
|
||||
ret |= f->m_interface->write_map_next M_R(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item->key_ptr); \
|
||||
ret |= f->m_interface->write_map_value(local, f); \
|
||||
ret |= f->m_interface->write_map_value M_R(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(value_oplist, f, *item->value_ptr); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_map_end(local, f); \
|
||||
ret |= f->m_interface->write_map_end M_R(local, f); \
|
||||
, \
|
||||
ret = f->m_interface->write_array_start(local, f, t1->size); \
|
||||
ret = f->m_interface->write_array_start M_R(local, f, t1->size); \
|
||||
for (M_F(name, _it)(it, t1) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
M_F(name, _next)(it)){ \
|
||||
item = M_F(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= f->m_interface->write_array_next M_R(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
ret |= f->m_interface->write_array_end M_R(local, f); \
|
||||
) \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \
|
||||
M_P(m_serial_return_code_t, name, _in_serial, tree_t t1, m_serial_read_t f) \
|
||||
{ \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
key_t key; \
|
||||
M_F(name,_reset)(t1); \
|
||||
M_IF(isMap)( \
|
||||
value_t value; \
|
||||
M_F(name,_reset)M_R(t1); \
|
||||
M_QLET(1, key, key_t, key_oplist) \
|
||||
M_IF(isMap)(M_QLET(2, value, value_t, value_oplist) , /*nothing*/) { \
|
||||
M_IF(isMap)( \
|
||||
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
M_CALL_INIT (value_oplist, value); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) break; \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
||||
if (ret != M_SERIAL_OK_DONE) break; \
|
||||
ret = f->m_interface->read_map_value(local, f); \
|
||||
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
|
||||
if (ret != M_SERIAL_OK_CONTINUE) break; \
|
||||
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
||||
M_F(name, _set_at)(t1, key, value); \
|
||||
if (ret != M_SERIAL_OK_DONE) break; \
|
||||
M_F(name, _set_at)M_R(t1, key, value); \
|
||||
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
M_CALL_CLEAR(value_oplist, value); \
|
||||
, \
|
||||
, /* queue style */ \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(key_oplist, key); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) break; \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_F(name, _push)(t1, key); \
|
||||
if (ret != M_SERIAL_OK_DONE) break; \
|
||||
M_F(name, _push)M_R(t1, key); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(key_oplist, key); \
|
||||
) /* End of IF isMap */ \
|
||||
return ret; \
|
||||
) /* End of IF isMap */ \
|
||||
} \
|
||||
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no in_serial */ ) \
|
||||
|
||||
|
||||
+610
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
* M*LIB - Byte String module
|
||||
*
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_BSTRING_H
|
||||
#define MSTARLIB_BSTRING_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* Byte String Type definition*/
|
||||
typedef struct m_bstring_s {
|
||||
size_t size; // Number of effective BYTES
|
||||
size_t alloc; // Size of the allocated buffer
|
||||
size_t offset; // Offset where the effective BYTES are stored in the allocated buffer
|
||||
uint8_t *ptr; // Pointer to the allocated buffer
|
||||
} m_bstring_t[1];
|
||||
|
||||
// Contract of a bstring
|
||||
#define M_BSTRING_CONTRACT(o) do { \
|
||||
M_ASSERT( (o)->size + (o)->offset >= (o)->size ); \
|
||||
M_ASSERT( (o)->alloc >= (o)->size + (o)->offset ); \
|
||||
M_ASSERT( (o)->ptr != NULL || (o)->alloc == 0); \
|
||||
} while (0)
|
||||
|
||||
M_INLINE size_t
|
||||
m_bstring_size(const m_bstring_t s)
|
||||
{
|
||||
M_BSTRING_CONTRACT(s);
|
||||
return s->size;
|
||||
}
|
||||
|
||||
M_INLINE size_t
|
||||
m_bstring_capacity(const m_bstring_t s)
|
||||
{
|
||||
M_BSTRING_CONTRACT(s);
|
||||
return s->alloc;
|
||||
}
|
||||
|
||||
M_INLINE uint8_t *
|
||||
m_bstr1ng_cstr(const m_bstring_t v)
|
||||
{
|
||||
return &v->ptr[v->offset];
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_init(m_bstring_t s)
|
||||
{
|
||||
s->size = 0;
|
||||
s->alloc = 0;
|
||||
s->offset = 0;
|
||||
s->ptr = NULL;
|
||||
M_BSTRING_CONTRACT(s);
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _clear, m_bstring_t s)
|
||||
{
|
||||
M_BSTRING_CONTRACT(s);
|
||||
M_MEMORY_FREE(m_context, uint8_t, s->ptr, s->alloc);
|
||||
s->offset = (size_t) -1;
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_reset(m_bstring_t s)
|
||||
{
|
||||
M_BSTRING_CONTRACT(s);
|
||||
s->size = 0;
|
||||
s->offset = 0;
|
||||
M_BSTRING_CONTRACT(s);
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_swap(m_bstring_t s1, m_bstring_t s2)
|
||||
{
|
||||
M_BSTRING_CONTRACT(s1);
|
||||
M_BSTRING_CONTRACT(s2);
|
||||
m_bstring_t tmp;
|
||||
memcpy(tmp, s1, sizeof (m_bstring_t));
|
||||
memcpy(s1, s2, sizeof (m_bstring_t));
|
||||
memcpy(s2, tmp, sizeof (m_bstring_t));
|
||||
M_BSTRING_CONTRACT(s1);
|
||||
M_BSTRING_CONTRACT(s2);
|
||||
}
|
||||
|
||||
/* Return the selected byte-character of the string */
|
||||
M_INLINE uint8_t
|
||||
m_bstring_get_byte(const m_bstring_t v, size_t index)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX(index, v->size);
|
||||
return m_bstr1ng_cstr(v)[index];
|
||||
}
|
||||
|
||||
/* Set the selected byte-character of the string */
|
||||
M_INLINE void
|
||||
m_bstring_set_byte(m_bstring_t v, size_t index, const uint8_t c)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX(index, v->size);
|
||||
m_bstr1ng_cstr(v)[index] = c;
|
||||
}
|
||||
|
||||
/* Test if the string is empty or not */
|
||||
M_INLINE bool
|
||||
m_bstring_empty_p(const m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
return v->size == 0;
|
||||
}
|
||||
|
||||
/* Internal method to fit the Byte string to the given size
|
||||
Ensures that the Byte String capacity is greater than size_alloc+offset
|
||||
The returned Byte String 'v' no longer has a **correct** size field.
|
||||
If exact_alloc is true, it will request exactly the request size for the buffer
|
||||
on reallocation otherwise it will augment the requested size to amortize the future
|
||||
allocations.
|
||||
Return a pointer to the writable string.
|
||||
*/
|
||||
M_P(uint8_t *, m_bstr1ng, _fit2size, m_bstring_t v, size_t size_alloc, bool exact_alloc)
|
||||
{
|
||||
// Very unlikely overflow case
|
||||
if (M_UNLIKELY_NOMEM (v->offset > v->offset + size_alloc)) {
|
||||
goto allocation_error;
|
||||
}
|
||||
M_ASSERT_INDEX (v->offset, v->offset + size_alloc);
|
||||
// Note: this function may be called in context where the contract is not fulfilled.
|
||||
if (M_UNLIKELY (v->offset + size_alloc > v->alloc)) {
|
||||
// Insufficient current allocation to store the new string
|
||||
|
||||
// Check if there is a too big offset that can be reduced
|
||||
if (v->offset > v->size / 8 && v->offset > 16) {
|
||||
// Move back the effective bytes to the beginning of the buffer
|
||||
memmove(&v->ptr[0], &v->ptr[v->offset], v->size);
|
||||
v->offset = 0;
|
||||
// If it is now sufficient, we can return.
|
||||
if (size_alloc <= v->alloc)
|
||||
return v->ptr;
|
||||
}
|
||||
|
||||
// Alloc a new array. Compute the requested allocation size
|
||||
size_t alloc = (v->offset + size_alloc + (exact_alloc ? 0 : size_alloc / 2));
|
||||
alloc = M_MAX(alloc, 16);
|
||||
if (M_UNLIKELY_NOMEM (alloc < size_alloc)) {
|
||||
goto allocation_error;
|
||||
}
|
||||
uint8_t *ptr = M_MEMORY_REALLOC (m_context, uint8_t, v->ptr, v->alloc, alloc);
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
|
||||
goto allocation_error;
|
||||
}
|
||||
// The string cannot be fully embedded anymore.
|
||||
v->ptr = ptr;
|
||||
v->alloc = alloc;
|
||||
// Size is not set as the function is called in context
|
||||
// where the caller already know the final size of the string,
|
||||
// so it will update it.
|
||||
return &v->ptr[v->offset];
|
||||
}
|
||||
return &v->ptr[v->offset];
|
||||
allocation_error:
|
||||
M_MEMORY_FULL(uint8_t, size_alloc);
|
||||
}
|
||||
|
||||
/* Push the byte 'c' in the bstring 'v' */
|
||||
M_P(void, m_bstring, _push_back, m_bstring_t v, uint8_t c)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
const size_t size = v->size;
|
||||
// To overflow size+1, we need to have exhausted all memory
|
||||
// which is not possible.
|
||||
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, size+1, false);
|
||||
ptr[size] = c;
|
||||
v->size = size + 1;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
/* Push the array of bytes from 'buffer' in the bstring 'v' */
|
||||
M_P(void, m_bstring, _push_back_bytes, m_bstring_t v, size_t n, const void *buffer)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
const size_t size = v->size;
|
||||
M_ASSERT_INDEX (size, size + n);
|
||||
// If size+n overflows, it means we are giving two objects which sizes
|
||||
// sum to be greater than the available memory. The memory should have exhausted before that.
|
||||
// The case is not possible, or the given size of the buffer is "faked".
|
||||
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, size+n, false);
|
||||
memcpy(&ptr[size], buffer, n);
|
||||
v->size = size + n;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _splice, m_bstring_t dst, m_bstring_t src)
|
||||
{
|
||||
M_BSTRING_CONTRACT (dst);
|
||||
M_BSTRING_CONTRACT (src);
|
||||
M_ASSERT(src != dst);
|
||||
m_bstring_push_back_bytes M_R(dst, m_bstring_size(src), m_bstr1ng_cstr(src));
|
||||
m_bstring_reset(src);
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _init_set, m_bstring_t v, const m_bstring_t org)
|
||||
{
|
||||
m_bstring_init(v);
|
||||
m_bstring_push_back_bytes M_R(v, m_bstring_size(org), m_bstr1ng_cstr(org));
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _set, m_bstring_t v, const m_bstring_t org)
|
||||
{
|
||||
m_bstring_reset(v);
|
||||
m_bstring_push_back_bytes M_R(v, m_bstring_size(org), m_bstr1ng_cstr(org));
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_init_move(m_bstring_t v, m_bstring_t org)
|
||||
{
|
||||
M_BSTRING_CONTRACT (org);
|
||||
memcpy(v, org, sizeof (m_bstring_t));
|
||||
// Make org an invalid representation
|
||||
org->offset = (size_t) -1;
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _move, m_bstring_t v, m_bstring_t org)
|
||||
{
|
||||
m_bstring_clear M_R(v);
|
||||
m_bstring_init_move(v, org);
|
||||
}
|
||||
|
||||
M_INLINE size_t
|
||||
m_bstring_hash(const m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
return m_core_hash(m_bstr1ng_cstr(v), m_bstring_size(v));
|
||||
}
|
||||
|
||||
M_INLINE int
|
||||
m_bstring_cmp(const m_bstring_t v1, const m_bstring_t v2)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v1);
|
||||
M_BSTRING_CONTRACT (v2);
|
||||
const size_t s1 = m_bstring_size(v1);
|
||||
const size_t s2 = m_bstring_size(v2);
|
||||
int ret = memcmp(m_bstr1ng_cstr(v1), m_bstr1ng_cstr(v2), M_MIN(s1, s2));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (s1 != s2)
|
||||
return s1 < s2 ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_INLINE int
|
||||
m_bstring_cmp_bytes(const m_bstring_t v1, size_t s2, const void *p2)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v1);
|
||||
const size_t s1 = m_bstring_size(v1);
|
||||
int ret = memcmp(m_bstr1ng_cstr(v1), p2, M_MIN(s1, s2));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (s1 != s2)
|
||||
return s1 < s2 ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_INLINE bool
|
||||
m_bstring_equal_p(const m_bstring_t v1, const m_bstring_t v2)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v1);
|
||||
M_BSTRING_CONTRACT (v2);
|
||||
const size_t s1 = m_bstring_size(v1);
|
||||
const size_t s2 = m_bstring_size(v2);
|
||||
if (s1 != s2)
|
||||
return false;
|
||||
return memcmp(m_bstr1ng_cstr(v1), m_bstr1ng_cstr(v2), s1) == 0;
|
||||
}
|
||||
|
||||
M_INLINE bool
|
||||
m_bstring_equal_bytes_p(const m_bstring_t v1, size_t s2, const void *buffer)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v1);
|
||||
const size_t s1 = m_bstring_size(v1);
|
||||
if (s1 != s2)
|
||||
return false;
|
||||
return memcmp(m_bstr1ng_cstr(v1), buffer, s1) == 0;
|
||||
}
|
||||
|
||||
M_INLINE uint8_t
|
||||
m_bstring_pop_back(m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX(0, v->size);
|
||||
uint8_t c = m_bstr1ng_cstr(v)[v->size-1];
|
||||
v->size --;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
return c;
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_pop_back_bytes(size_t n, void *buffer, m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX(n-1, v->size);
|
||||
memcpy(buffer, &m_bstr1ng_cstr(v)[v->size-n], n);
|
||||
v->size -= n;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_INLINE uint8_t
|
||||
m_bstring_pop_front(m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX(0, v->size);
|
||||
uint8_t c = m_bstr1ng_cstr(v)[0];
|
||||
v->size --;
|
||||
v->offset++;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
return c;
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_pop_front_bytes(size_t n, void *buffer, m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX(n-1, v->size);
|
||||
memcpy(buffer, &m_bstr1ng_cstr(v)[0], n);
|
||||
v->size -= n;
|
||||
v->offset += n;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _push_bytes_at, m_bstring_t v, size_t pos, size_t n, const void *buffer)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
const size_t size = v->size;
|
||||
M_ASSERT_INDEX (pos, size+1); //pos == size ==> equivalent to _push_back_bytes
|
||||
M_ASSERT_INDEX (size, size + n);
|
||||
// If size+n overflows, it means we are giving two objects which sizes
|
||||
// sum to be greater than the available memory. The memory should have exhausted before that.
|
||||
// The case is not possible.
|
||||
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, size+n, false);
|
||||
memmove(&ptr[pos+n], &ptr[pos], size-pos);
|
||||
memcpy(&ptr[pos], buffer, n);
|
||||
v->size = size + n;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_pop_bytes_at(size_t n, void *buffer, m_bstring_t v, size_t pos)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
const size_t size = v->size;
|
||||
M_ASSERT_INDEX (pos, size);
|
||||
M_ASSERT_INDEX(n-1, size-pos);
|
||||
uint8_t *ptr = m_bstr1ng_cstr(v);
|
||||
memcpy(buffer, &ptr[pos], n);
|
||||
memmove(&ptr[pos], &ptr[pos+n], size-pos-n);
|
||||
v->size -= n;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _resize, m_bstring_t v, size_t n)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
const size_t size = v->size;
|
||||
if (n > size) {
|
||||
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, n, true);
|
||||
memset(&ptr[size], 0, n-size);
|
||||
}
|
||||
v->size = n;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_P(void, m_bstring, _reserve, m_bstring_t v, size_t n)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
const size_t size = v->size;
|
||||
if ( (n != 0 || v->size != 0) && n < v->offset + size) {
|
||||
n = v->offset + size;
|
||||
}
|
||||
if (n == 0) {
|
||||
M_MEMORY_FREE(m_context, uint8_t, v->ptr, v->alloc);
|
||||
v->offset = 0;
|
||||
v->size = 0;
|
||||
v->alloc = 0;
|
||||
v->ptr = NULL;
|
||||
} else {
|
||||
uint8_t *ptr = M_MEMORY_REALLOC (m_context, uint8_t, v->ptr, v->alloc, n);
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
|
||||
M_MEMORY_FULL(uint8_t, n);
|
||||
}
|
||||
v->ptr = ptr;
|
||||
v->alloc = n;
|
||||
}
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
M_INLINE const uint8_t *
|
||||
m_bstring_view(const m_bstring_t v, size_t offset, size_t size_requested)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX (offset+size_requested, v->size+1);
|
||||
(void) size_requested;
|
||||
return &m_bstr1ng_cstr(v)[offset];
|
||||
}
|
||||
|
||||
// Acquire / release direct write access: once acquired, user can directly wite at the returned pointer for the requested size
|
||||
// All other operations on the bstring are forbidden during direct access.
|
||||
// This allows passing the bstring buffer to be formatted by different API
|
||||
M_INLINE uint8_t *
|
||||
m_bstring_acquire_access(m_bstring_t v, size_t offset, size_t size_requested)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT_INDEX (offset+size_requested, v->size+1);
|
||||
(void) size_requested;
|
||||
// Break canonical representation (this will generate assertion if a normal API is called now)
|
||||
M_SWAP(size_t, v->size, v->alloc);
|
||||
return &m_bstr1ng_cstr(v)[offset];
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_bstring_release_access(m_bstring_t v)
|
||||
{
|
||||
M_ASSERT( v->size >= v->alloc + v->offset );
|
||||
// Restore canonical representation
|
||||
M_SWAP(size_t, v->size, v->alloc);
|
||||
M_BSTRING_CONTRACT (v);
|
||||
}
|
||||
|
||||
// No Iterator planned for this container (No added value)
|
||||
|
||||
#if M_USE_STDIO
|
||||
|
||||
M_INLINE size_t
|
||||
m_bstring_fwrite(FILE *f, const m_bstring_t v)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT (f != NULL);
|
||||
if (M_UNLIKELY(m_bstring_size(v) == 0)) {
|
||||
return 0;
|
||||
}
|
||||
M_ASSERT(m_bstr1ng_cstr(v) != NULL);
|
||||
return fwrite(m_bstr1ng_cstr(v), 1, m_bstring_size(v), f);
|
||||
}
|
||||
|
||||
M_P(bool, m_bstring, _fread, m_bstring_t v, FILE *f, size_t num)
|
||||
{
|
||||
M_BSTRING_CONTRACT (v);
|
||||
M_ASSERT (f != NULL);
|
||||
m_bstring_reset(v);
|
||||
//TODO: option to read all the file num=-1?
|
||||
if (M_UNLIKELY(num == 0)) {
|
||||
return true;
|
||||
}
|
||||
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, num, true);
|
||||
size_t n = fread(ptr, 1, num, f);
|
||||
M_ASSERT (n <= num);
|
||||
v->size = n;
|
||||
return n == num;
|
||||
}
|
||||
|
||||
#endif // Have stdio
|
||||
|
||||
// No formatted string input/output. Only serializer
|
||||
|
||||
/* Transform the Byte String 'v2'
|
||||
and output it in the given serializer
|
||||
See serialization for return code.
|
||||
*/
|
||||
M_P(m_serial_return_code_t, m_bstring, _out_serial, m_serial_write_t serial, const m_bstring_t v)
|
||||
{
|
||||
M_ASSERT (serial != NULL && serial->m_interface != NULL);
|
||||
M_BSTRING_CONTRACT (v);
|
||||
m_serial_local_t local;
|
||||
m_serial_return_code_t ret;
|
||||
size_t size = m_bstring_size(v);
|
||||
uint8_t *ptr = m_bstr1ng_cstr(v);
|
||||
ret = serial->m_interface->write_array_start M_R(local, serial, size);
|
||||
if (size > 0) {
|
||||
ret |= serial->m_interface->write_integer M_R(serial, ptr[0], 1);
|
||||
for(size_t i = 1; i < size; i++) {
|
||||
ret |= serial->m_interface->write_array_next M_R(local, serial);
|
||||
ret |= serial->m_interface->write_integer M_R(serial, ptr[i], 1);
|
||||
}
|
||||
}
|
||||
ret |= serial->m_interface->write_array_end M_R(local, serial);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read from the serializer
|
||||
and set the converted value in the byte string 'v'.
|
||||
See serialization for return code.
|
||||
*/
|
||||
M_P(m_serial_return_code_t, m_bstring, _in_serial, m_bstring_t v, m_serial_read_t f)
|
||||
{
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL);
|
||||
M_BSTRING_CONTRACT (v);
|
||||
m_serial_return_code_t ret;
|
||||
m_serial_local_t local;
|
||||
size_t estimated_size = 0, real_size = 0;
|
||||
m_bstring_reset(v);
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size);
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) {
|
||||
return ret;
|
||||
}
|
||||
uint8_t *ptr = m_bstr1ng_fit2size M_R(v, estimated_size, true);
|
||||
do {
|
||||
long long val;
|
||||
ret = f->m_interface->read_integer(f, &val, 1);
|
||||
if (ret != M_SERIAL_OK_DONE) { break; }
|
||||
ptr[real_size++] = (uint8_t) val;
|
||||
ret = f->m_interface->read_array_next(local, f);
|
||||
} while (ret == M_SERIAL_OK_CONTINUE);
|
||||
v->size = real_size;
|
||||
M_BSTRING_CONTRACT (v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Define the OPLIST of a BYTE STRING */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_BSTRING_OPLIST \
|
||||
(INIT(m_bstring_init),INIT_SET(m_bstring_init_set), SET(m_bstring_set), \
|
||||
INIT_MOVE(m_bstring_init_move), MOVE(m_bstring_move), \
|
||||
SWAP(m_bstring_swap), RESET(m_bstring_reset), EMPTY_P(m_bstring_empty_p), \
|
||||
SUBTYPE(uint8_t), PUSH(m_bstring_push), \
|
||||
CLEAR(m_bstring_clear), HASH(m_bstring_hash), EQUAL(m_bstring_equal_p), \
|
||||
CMP(m_bstring_cmp), TYPE(m_bstring_t), GENTYPE(struct m_bstring_s*), \
|
||||
OUT_SERIAL(m_bstring_out_serial), IN_SERIAL(m_bstring_in_serial), \
|
||||
)
|
||||
#else
|
||||
#define M_BSTRING_OPLIST \
|
||||
(INIT(m_bstring_init),INIT_SET(API_0P(m_bstring_init_set)), SET(API_0P(m_bstring_set)), \
|
||||
INIT_MOVE(m_bstring_init_move), MOVE(API_0P(m_bstring_move)), \
|
||||
SWAP(m_bstring_swap), RESET(m_bstring_reset), EMPTY_P(m_bstring_empty_p), \
|
||||
SUBTYPE(uint8_t), PUSH(API_0P(m_bstring_push)), \
|
||||
CLEAR(API_0P(m_bstring_clear)), HASH(m_bstring_hash), EQUAL(m_bstring_equal_p), \
|
||||
CMP(m_bstring_cmp), TYPE(m_bstring_t), GENTYPE(struct m_bstring_s*), \
|
||||
OUT_SERIAL(API_0P(m_bstring_out_serial)), IN_SERIAL(API_0P(m_bstring_in_serial=) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/********************************************************************************/
|
||||
/* */
|
||||
/* Define the small name (i.e. without the prefix) of the API provided by this */
|
||||
/* header if it is needed */
|
||||
/* */
|
||||
/********************************************************************************/
|
||||
#if M_USE_SMALL_NAME
|
||||
|
||||
#define bstring_t m_bstring_t
|
||||
#define bstring_size m_bstring_size
|
||||
#define bstring_capacity m_bstring_capacity
|
||||
#define bstring_init m_bstring_init
|
||||
#define bstring_clear m_bstring_clear
|
||||
#define bstring_reset m_bstring_reset
|
||||
#define bstring_get_byte m_bstring_get_byte
|
||||
#define bstring_set_byte m_bstring_set_byte
|
||||
#define bstring_empty_p m_bstring_empty_p
|
||||
#define bstring_push_back m_bstring_push_back
|
||||
#define bstring_push_back_bytes m_bstring_push_back_bytes
|
||||
#define bstring_init_set m_bstring_init_set
|
||||
#define bstring_set m_bstring_set
|
||||
#define bstring_init_move m_bstring_init_move
|
||||
#define bstring_move m_bstring_move
|
||||
#define bstring_cmp m_bstring_cmp
|
||||
#define bstring_cmp_bytes m_bstring_cmp_bytes
|
||||
#define bstring_equal_p m_bstring_equal_p
|
||||
#define bstring_equal_bytes_p m_bstring_equal_bytes_p
|
||||
#define bstring_pop_back m_bstring_pop_back
|
||||
#define bstring_pop_back_bytes m_bstring_pop_back_bytes
|
||||
#define bstring_pop_front m_bstring_pop_front
|
||||
#define bstring_pop_front_bytes m_bstring_pop_front_bytes
|
||||
#define bstring_push_bytes_at m_bstring_push_bytes_at
|
||||
#define bstring_pop_bytes_at m_bstring_pop_bytes_at
|
||||
#define bstring_resize m_bstring_resize
|
||||
#define bstring_reserve m_bstring_reserve
|
||||
#define bstring_view m_bstring_view
|
||||
#define bstring_acquire_access m_bstring_acquire_access
|
||||
#define bstring_release_access m_bstring_release_access
|
||||
#define bstring_hash m_bstring_hash
|
||||
#define bstring_fread m_bstring_fread
|
||||
#define bstring_fwrite m_bstring_fwrite
|
||||
#define bstring_out_serial m_bstring_out_serial
|
||||
#define bstring_in_serial m_bstring_in_serial
|
||||
#define bstring_swap m_bstring_swap
|
||||
#define bstring_splice m_bstring_splice
|
||||
#define BSTRING_OPLIST M_BSTRING_OPLIST
|
||||
|
||||
#endif
|
||||
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
#endif
|
||||
+216
-339
File diff suppressed because it is too large.
Load diff
-835
@@ -1,835 +0,0 @@
|
||||
/*
|
||||
* M*LIB - Concurrent memory pool allocator
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_CONCURRENT_MEMPOOL_H
|
||||
#define MSTARLIB_CONCURRENT_MEMPOOL_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
#include "m-genint.h"
|
||||
|
||||
M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* Minimum number of nodes per group of nodes */
|
||||
#define M_CMEMP00L_MIN_NODE_PER_GROUP 16
|
||||
|
||||
#define M_C_MEMPOOL_DEF(name, type_t) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
|
||||
M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
|
||||
M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
|
||||
M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
|
||||
M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
/* Classic internal Singly List without allocation */
|
||||
#define M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
|
||||
\
|
||||
typedef struct M_F(name, _slist_node_s) { \
|
||||
struct M_F(name, _slist_node_s) *next; \
|
||||
type_t data; \
|
||||
} M_F(name, _slist_node_ct); \
|
||||
\
|
||||
typedef struct M_F(name, _slist_node_s) *M_F(name, _slist_ct)[1]; \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _slist_init)(M_F(name, _slist_ct) list) \
|
||||
{ \
|
||||
*list = NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _slist_push)(M_F(name, _slist_ct) list, \
|
||||
M_F(name, _slist_node_ct) *node) \
|
||||
{ \
|
||||
node->next = *list; \
|
||||
*list = node; \
|
||||
} \
|
||||
\
|
||||
M_INLINE M_F(name, _slist_node_ct) * \
|
||||
M_F(name, _slist_pop)(M_F(name, _slist_ct) list) \
|
||||
{ \
|
||||
M_ASSERT (*list != NULL); \
|
||||
M_F(name, _slist_node_ct) *node = *list; \
|
||||
*list = node->next; \
|
||||
M_IF_DEBUG(node->next = NULL;) \
|
||||
return node; \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _slist_empty_p)(M_F(name, _slist_ct) list) \
|
||||
{ \
|
||||
return *list == NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _slist_move)(M_F(name, _slist_ct) list, \
|
||||
M_F(name, _slist_ct) src) \
|
||||
{ \
|
||||
*list = *src; \
|
||||
*src = NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _slist_clear)(M_F(name, _slist_ct) list) \
|
||||
{ \
|
||||
M_F(name, _slist_node_ct) *it = *list, *next; \
|
||||
while (it) { \
|
||||
next = it->next; \
|
||||
M_MEMORY_DEL(it); \
|
||||
it = next; \
|
||||
} \
|
||||
*list = NULL; \
|
||||
} \
|
||||
|
||||
|
||||
/* Lock Free free queue list (not generic one) of lists without allocation
|
||||
Based on Michael & Scott Lock Free Queue List algorithm.
|
||||
Each list is considered empty if there is only one node within.
|
||||
This LF Queue List doesn't try to prevent the ABA problem. It is up to the
|
||||
caller to avoid recycling the nodes too fast.
|
||||
Each list has its own unique NIL ptr in order to avoid issues when
|
||||
migrating a node from a Q to another: in the following scenario,
|
||||
- Thread 1 performs a PUSH of N in Q1 with Q1 empty (only node is NA)
|
||||
NA.next is NIL.
|
||||
- Thread 1 is interrupted just before the CAS on NA.next
|
||||
- Thread 2 performs a sucessfull push of NB in Q1. NA.next is set to NB.
|
||||
- Thread 2 performs a sucessfull pop of NA in Q1
|
||||
- Thread 2 performs a sucessfull push of NA in Q2. NA.next is set to NIL.
|
||||
- Thread 1 is restored and will succeed as NA.next is once again NIL.
|
||||
In order to prevent the last CAS to succeed, each queue uses its own NIL pointer.
|
||||
It is a derived problem of the ABA problem.
|
||||
*/
|
||||
/* TODO: Optimize alignement to reduce memory consumption. NIL object can use []
|
||||
to reduce memory consumption too (non compatible with C++ ...) */
|
||||
#define M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
|
||||
\
|
||||
typedef struct M_F(name, _lf_node_s) { \
|
||||
M_ATTR_EXTENSION _Atomic(struct M_F(name, _lf_node_s) *) next; \
|
||||
m_gc_atomic_ticket_ct cpt; \
|
||||
M_F(name, _slist_ct) list; \
|
||||
} M_F(name, _lf_node_t); \
|
||||
\
|
||||
typedef struct M_F(name, _lflist_s) { \
|
||||
M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) head; \
|
||||
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_ATTR_EXTENSION _Atomic(M_F(name, _lf_node_t) *) tail; \
|
||||
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_F(name, _lf_node_t) nil; \
|
||||
} M_F(name, _lflist_ct)[1]; \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _lflist_init)(M_F(name, _lflist_ct) list, \
|
||||
M_F(name, _lf_node_t) *node) \
|
||||
{ \
|
||||
atomic_init(&list->head, node); \
|
||||
atomic_init(&list->tail, node); \
|
||||
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _lflist_empty_p)(M_F(name, _lflist_ct) list) \
|
||||
{ \
|
||||
return atomic_load(&list->tail) == atomic_load(&list->head); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _lflist_push)(M_F(name, _lflist_ct) list, \
|
||||
M_F(name, _lf_node_t) *node, m_core_backoff_ct bkoff) \
|
||||
{ \
|
||||
M_F(name, _lf_node_t) *tail; \
|
||||
M_F(name, _lf_node_t) *next; \
|
||||
\
|
||||
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
|
||||
m_core_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load_explicit(&tail->next, memory_order_acquire); \
|
||||
if (M_UNLIKELY(next != &list->nil)) { \
|
||||
/* Tail was not pointing to the last node \
|
||||
Try to swing Tail to the next node */ \
|
||||
atomic_compare_exchange_weak_explicit(&list->tail, \
|
||||
&tail, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Try to link node at the end of the linked list */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&tail->next, \
|
||||
&next, node, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) \
|
||||
break; \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
/* Enqueue is done. Try to swing Tail to the inserted node \
|
||||
If it fails, someone else will do it or has already did it. */ \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, node, \
|
||||
memory_order_acq_rel, \
|
||||
memory_order_relaxed); \
|
||||
} \
|
||||
\
|
||||
M_INLINE M_F(name, _lf_node_t) * \
|
||||
M_F(name, _lflist_pop)(M_F(name, _lflist_ct) list, m_core_backoff_ct bkoff) \
|
||||
{ \
|
||||
M_F(name, _lf_node_t) *head; \
|
||||
M_F(name, _lf_node_t) *tail; \
|
||||
M_F(name, _lf_node_t) *next; \
|
||||
\
|
||||
/* Reinitialize backoff */ \
|
||||
m_core_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
head = atomic_load(&list->head); \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load(&head->next); \
|
||||
/* Are head, tail, and next consistent?*/ \
|
||||
if (M_LIKELY(head == \
|
||||
atomic_load_explicit(&list->head, memory_order_relaxed))) \
|
||||
{ \
|
||||
/* Is queue empty or Tail falling behind? */ \
|
||||
if (head == tail) { \
|
||||
/* Is queue empty? */ \
|
||||
if (next == &list->nil) \
|
||||
return NULL; \
|
||||
/* Tail is falling behind. Try to advance it */ \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, \
|
||||
next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Try to swing Head to the next node */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&list->head, \
|
||||
&head, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) { \
|
||||
break; \
|
||||
} \
|
||||
/* Failure: perform a random exponential backoff */ \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
/* dequeue returns an element that becomes the new dummy element (the new head), \
|
||||
and the former dummy element (the former head) is removed: \
|
||||
Since we want a link of free list, and we don't care about the content itsef, \
|
||||
provided that the node we return is older than the one we should return, \
|
||||
Therefore, we return the previous dummy head. \
|
||||
As such, it is not the original MSqueue algorithm. */ \
|
||||
M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \
|
||||
return head; \
|
||||
} \
|
||||
\
|
||||
/* Dequeue a node if the node is old enough */ \
|
||||
M_INLINE M_F(name, _lf_node_t) * \
|
||||
M_F(name, _lflist_pop_if)(M_F(name, _lflist_ct) list, \
|
||||
m_gc_ticket_ct age, m_core_backoff_ct bkoff) \
|
||||
{ \
|
||||
M_F(name, _lf_node_t) *head; \
|
||||
M_F(name, _lf_node_t) *tail; \
|
||||
M_F(name, _lf_node_t) *next; \
|
||||
\
|
||||
m_core_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
head = atomic_load(&list->head); \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load(&head->next); \
|
||||
if (M_LIKELY(head == atomic_load_explicit(&list->head, memory_order_relaxed))) \
|
||||
{ \
|
||||
if (head == tail) { \
|
||||
if (next == &list->nil) \
|
||||
return NULL; \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Test if the node is old enought to be popped */ \
|
||||
if (atomic_load_explicit(&next->cpt, memory_order_relaxed) >= age) \
|
||||
return NULL; \
|
||||
/* Try to swing Head to the next node */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&list->head, \
|
||||
&head, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) { \
|
||||
break; \
|
||||
} \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
M_IF_DEBUG(atomic_store(&head->next, (M_F(name, _lf_node_t) *) 0);) \
|
||||
return head; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _lflist_clear)(M_F(name, _lflist_ct) list) \
|
||||
{ \
|
||||
m_core_backoff_ct bkoff; \
|
||||
m_core_backoff_init(bkoff); \
|
||||
while (true) { \
|
||||
M_F(name, _lf_node_t) *node = M_F(name, _lflist_pop)(list, bkoff); \
|
||||
if (node == NULL) break; \
|
||||
M_F(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
|
||||
memory_order_relaxed); \
|
||||
M_F(name, _slist_clear)(node->list); \
|
||||
M_MEMORY_DEL(node); \
|
||||
node = next; \
|
||||
} \
|
||||
/* Dummy node to free too */ \
|
||||
M_F(name, _lf_node_t) *dummy; \
|
||||
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
|
||||
M_F(name, _slist_clear)(dummy->list); \
|
||||
M_MEMORY_DEL(dummy); \
|
||||
} \
|
||||
|
||||
/* System node allocator: request memory to the system.
|
||||
As such it is a non Lock-Free path. */
|
||||
#define M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
|
||||
\
|
||||
M_INLINE M_F(name, _lf_node_t) * \
|
||||
M_F(name, _alloc_node)(unsigned int initial) \
|
||||
{ \
|
||||
M_F(name, _lf_node_t) * node; \
|
||||
node = M_MEMORY_ALLOC(M_F(name, _lf_node_t)); \
|
||||
if (M_UNLIKELY_NOMEM (node == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_F(name, _lf_node_t))); \
|
||||
return NULL; \
|
||||
} \
|
||||
atomic_init(&node->next, (M_F(name, _lf_node_t) *) 0); \
|
||||
atomic_init(&node->cpt, 0UL); \
|
||||
M_F(name, _slist_init)(node->list); \
|
||||
for(unsigned i = 0; i < initial; i++) { \
|
||||
M_F(name, _slist_node_ct) *n; \
|
||||
n = M_MEMORY_ALLOC(M_F(name, _slist_node_ct)); \
|
||||
if (M_UNLIKELY_NOMEM (n == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_F(name, _lf_node_t))); \
|
||||
return NULL; \
|
||||
} \
|
||||
M_F(name, _slist_push)(node->list, n); \
|
||||
} \
|
||||
return node; \
|
||||
} \
|
||||
|
||||
/* Concurrent Memory pool
|
||||
The data structure is the following.
|
||||
Each thread has its own pool of nodes (local) that only it can
|
||||
access (it is a singly list). If there is no longer any node in this
|
||||
pool, it requests a new pool to the lock free queue of pool (group of
|
||||
nodes). If it fails, it requests a new pool to the system allocator
|
||||
(and from there it is no longer lock free).
|
||||
This memory pool can only be lock free if the initial state is
|
||||
sufficiently dimensionned to avoid calling the system allocator during
|
||||
the normal processing.
|
||||
Then each thread pushs its deleted node into another pool of nodes,
|
||||
where the node is logically deleted (no contain of the node is destroyed
|
||||
at this point and the node can be freely accessed by other threads).
|
||||
Once the thread mempool is put to sleep, the age of the pool of logical
|
||||
deleted nodes is computed and this pool is move to the Lock Free Queue
|
||||
List of pools to be reclaimed. Then A Garbage Collector is performed
|
||||
on this Lock Free Queue list to reclaim all pools thare are sufficiently
|
||||
aged (taking into account the grace period of the pool) to be moved back
|
||||
to the Lock Free Queue of the free pools.
|
||||
|
||||
Each pool of nodes can be in the following state:
|
||||
* FREE state if it is present in the Lock Free Queue of free pools.
|
||||
* EMPTY state if it is present in the Lock Free Queue of empty pools
|
||||
which means that the nodes present in it has been USED directly by a thread,
|
||||
* TO_BE_RECLAIMED state if it is present in the Lock Free Queue of TBR pools
|
||||
|
||||
A pool of nodes will go to the following state:
|
||||
FREE --> EMPTY --> TO_BE_RECLAIMED
|
||||
^ |
|
||||
+----------------------+
|
||||
|
||||
The ABA problem is taken into account as a node cannot be reused in the
|
||||
same queue without performing a full cycle of its state. Moreover
|
||||
it can only move from TO_BE_RECLAIMED to FREE if and only if a grace
|
||||
period is finished (and then we are sure that no thread references any
|
||||
older node).
|
||||
|
||||
Each thread has its own backoff structure (with local pseudo-random
|
||||
generator).
|
||||
|
||||
The grace period is detected through a global age counter (ticket)
|
||||
that is incremented each time a thread is awaken / sleep.
|
||||
Each thread has its own age that is set to the global ticket on sleep/awaken.
|
||||
The age of the pool to be reclaimed is also set to this global age counter.
|
||||
|
||||
To ensure that the grace period is finished, it tests if all threads
|
||||
are younger than the age of the pool to be reclaimed.
|
||||
|
||||
From a performance point of view, this puts a bottleneck on the global
|
||||
age counter that is shared and incremented by all threads. However,
|
||||
the sleep/awaken operations are much less frequent than other operations.
|
||||
Thus, it shall not have a huge impact on the performance if the user
|
||||
code is intelligent with the sleep/awaken operations.
|
||||
|
||||
As such it won't support more than ULONG_MAX sleep for all threads.
|
||||
*/
|
||||
#define M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
|
||||
\
|
||||
typedef struct M_F(name, _lfmp_thread_s) { \
|
||||
M_F(name, _slist_ct) free; \
|
||||
M_F(name, _slist_ct) to_be_reclaimed; \
|
||||
M_CACHELINE_ALIGN(align1, M_F(name, _slist_ct), M_F(name, _slist_ct)); \
|
||||
} M_F(name, _lfmp_thread_ct); \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _lfmp_thread_init)(M_F(name, _lfmp_thread_ct) *t) \
|
||||
{ \
|
||||
M_F(name, _slist_init)(t->free); \
|
||||
M_F(name, _slist_init)(t->to_be_reclaimed); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _lfmp_thread_clear)(M_F(name, _lfmp_thread_ct) *t) \
|
||||
{ \
|
||||
M_ASSERT(M_F(name, _slist_empty_p)(t->to_be_reclaimed)); \
|
||||
M_F(name, _slist_clear)(t->free); \
|
||||
M_F(name, _slist_clear)(t->to_be_reclaimed); \
|
||||
} \
|
||||
|
||||
/* NOTE: once a node is deleted, its data are kept readable until the future GC */
|
||||
#define M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \
|
||||
\
|
||||
typedef struct M_F(name, _s) { \
|
||||
unsigned initial; \
|
||||
M_F(name, _lfmp_thread_ct) *thread_data; \
|
||||
M_F(name, _lflist_ct) free; \
|
||||
M_F(name, _lflist_ct) to_be_reclaimed; \
|
||||
M_F(name, _lflist_ct) empty; \
|
||||
m_cmemp00l_list_ct mempool_node; \
|
||||
struct m_gc_s *gc_mem; \
|
||||
} M_F(name, _t)[1]; \
|
||||
\
|
||||
/* Garbage collect of the nodes of the mempool on sleep */ \
|
||||
M_INLINE void \
|
||||
M_C3(m_cmemp00l_,name,_gc_on_sleep)(m_gc_t gc_mem, m_cmemp00l_list_ct *data, \
|
||||
m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket) \
|
||||
{ \
|
||||
/* Get back the mempool from the node */ \
|
||||
struct M_F(name, _s) *mempool = \
|
||||
M_TYPE_FROM_FIELD(struct M_F(name, _s), data, m_cmemp00l_list_ct, mempool_node); \
|
||||
\
|
||||
/* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \
|
||||
if (!M_F(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
|
||||
M_F(name, _lf_node_t) *node; \
|
||||
/* Get a new empty group of nodes */ \
|
||||
node = M_F(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \
|
||||
if (M_UNLIKELY (node == NULL)) { \
|
||||
/* Fail to get an empty group of node. \
|
||||
Alloc a new one from the system */ \
|
||||
node = M_F(name, _alloc_node)(0); \
|
||||
M_ASSERT(node != NULL); \
|
||||
} \
|
||||
M_ASSERT(M_F(name, _slist_empty_p)(node->list)); \
|
||||
M_F(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
|
||||
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \
|
||||
M_F(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
\
|
||||
/* Perform a GC of the freelist of nodes */ \
|
||||
while (true) { \
|
||||
M_F(name, _lf_node_t) *node; \
|
||||
node = M_F(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
|
||||
min_ticket, gc_mem->thread_data[id].bkoff); \
|
||||
if (node == NULL) break; \
|
||||
M_F(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _init)(M_F(name, _t) mem, m_gc_t gc_mem, \
|
||||
unsigned init_node_count, unsigned init_group_count) \
|
||||
{ \
|
||||
const size_t max_thread = gc_mem->max_thread; \
|
||||
/* Initialize the thread data of the mempool */ \
|
||||
mem->thread_data = M_MEMORY_REALLOC(M_F(name, _lfmp_thread_ct), NULL, max_thread); \
|
||||
if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) { \
|
||||
M_MEMORY_FULL(max_thread * sizeof(M_F(name, _lfmp_thread_ct))); \
|
||||
return; \
|
||||
} \
|
||||
for(unsigned i = 0; i < max_thread;i++) { \
|
||||
M_F(name, _lfmp_thread_init)(&mem->thread_data[i]); \
|
||||
} \
|
||||
/* Preallocate some group of nodes for the mempool */ \
|
||||
mem->initial = M_MAX(M_CMEMP00L_MIN_NODE_PER_GROUP, init_node_count); \
|
||||
M_F(name, _lflist_init)(mem->free, M_F(name, _alloc_node)(init_node_count)); \
|
||||
M_F(name, _lflist_init)(mem->to_be_reclaimed, M_F(name, _alloc_node)(init_node_count)); \
|
||||
M_F(name, _lflist_init)(mem->empty, M_F(name, _alloc_node)(0)); \
|
||||
for(unsigned i = 1; i < init_group_count; i++) { \
|
||||
M_F(name, _lflist_push)(mem->free, M_F(name, _alloc_node)(init_node_count), \
|
||||
gc_mem->thread_data[0].bkoff); \
|
||||
M_F(name, _lflist_push)(mem->empty, M_F(name, _alloc_node)(0), \
|
||||
gc_mem->thread_data[0].bkoff); \
|
||||
} \
|
||||
/* Register the mempool in the GC */ \
|
||||
mem->mempool_node.gc_on_sleep = M_C3(m_cmemp00l_,name,_gc_on_sleep); \
|
||||
mem->mempool_node.next = gc_mem->mempool_list; \
|
||||
gc_mem->mempool_list = &mem->mempool_node; \
|
||||
mem->gc_mem = gc_mem; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(M_F(name, _t) mem) \
|
||||
{ \
|
||||
const unsigned max_thread = mem->gc_mem->max_thread; \
|
||||
for(unsigned i = 0; i < max_thread;i++) { \
|
||||
M_F(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
|
||||
} \
|
||||
M_MEMORY_FREE(mem->thread_data); \
|
||||
mem->thread_data = NULL; \
|
||||
M_F(name, _lflist_clear)(mem->empty); \
|
||||
M_F(name, _lflist_clear)(mem->free); \
|
||||
M_ASSERT(M_F(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
|
||||
M_F(name, _lflist_clear)(mem->to_be_reclaimed); \
|
||||
/* TODO: Unregister from the GC? */ \
|
||||
} \
|
||||
\
|
||||
M_INLINE type_t * \
|
||||
M_F(name, _new)(M_F(name, _t) mem, m_gc_tid_t id) \
|
||||
{ \
|
||||
M_F(name, _slist_node_ct) *snode; \
|
||||
M_F(name, _lf_node_t) *node; \
|
||||
while (true) { \
|
||||
/* Fast & likely path where we access the thread pool of nodes */ \
|
||||
if (M_LIKELY(!M_F(name, _slist_empty_p)(mem->thread_data[id].free))) { \
|
||||
snode = M_F(name, _slist_pop)(mem->thread_data[id].free); \
|
||||
return &snode->data; \
|
||||
} \
|
||||
/* Request a group node to the freelist of groups */ \
|
||||
node = M_F(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \
|
||||
if (M_UNLIKELY (node == NULL)) { \
|
||||
/* Request a new group to the system. Non Lock Free path */ \
|
||||
M_ASSERT(mem->initial > 0); \
|
||||
node = M_F(name, _alloc_node)(mem->initial); \
|
||||
M_ASSERT(node != NULL); \
|
||||
M_ASSERT(!M_F(name, _slist_empty_p)(node->list)); \
|
||||
} \
|
||||
M_F(name, _slist_move)(mem->thread_data[id].free, node->list); \
|
||||
/* Push back the empty group */ \
|
||||
M_ASSERT (M_F(name, _slist_empty_p)(node->list)); \
|
||||
M_F(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _del)(M_F(name, _t) mem, type_t *d, m_gc_tid_t id) \
|
||||
{ \
|
||||
M_F(name, _slist_node_ct) *snode; \
|
||||
M_ASSERT( d != NULL); \
|
||||
snode = M_TYPE_FROM_FIELD(M_F(name, _slist_node_ct), d, type_t, data); \
|
||||
M_F(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
|
||||
} \
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/* Define the ID of a thread */
|
||||
typedef unsigned int m_gc_tid_t;
|
||||
|
||||
/* Define the age of a node */
|
||||
/* TODO: Compute if sufficient (worst cast ULONG_MAX is 32 bits) */
|
||||
typedef unsigned long m_gc_ticket_ct;
|
||||
typedef atomic_ulong m_gc_atomic_ticket_ct;
|
||||
|
||||
/* Define the Linked List of mempools that are registered in the GC */
|
||||
struct m_gc_s;
|
||||
typedef struct m_cmemp00l_list_s {
|
||||
struct m_cmemp00l_list_s *next;
|
||||
void (*gc_on_sleep)(struct m_gc_s *gc_mem,
|
||||
struct m_cmemp00l_list_s *data, m_gc_tid_t id,
|
||||
m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket);
|
||||
void *data;
|
||||
} m_cmemp00l_list_ct;
|
||||
|
||||
/* Define the Garbage collector thread data */
|
||||
typedef struct m_gc_lfmp_thread_s {
|
||||
m_gc_atomic_ticket_ct ticket;
|
||||
m_core_backoff_ct bkoff;
|
||||
M_CACHELINE_ALIGN(align1, atomic_ulong, m_core_backoff_ct);
|
||||
} m_gc_lfmp_thread_ct;
|
||||
|
||||
/* Define the Garbage collector coordinator */
|
||||
typedef struct m_gc_s {
|
||||
m_gc_atomic_ticket_ct ticket;
|
||||
m_gc_tid_t max_thread;
|
||||
m_genint_t thread_alloc;
|
||||
m_gc_lfmp_thread_ct *thread_data;
|
||||
m_cmemp00l_list_ct *mempool_list;
|
||||
} m_gc_t[1];
|
||||
|
||||
M_INLINE void
|
||||
m_gc_init(m_gc_t gc_mem, size_t max_thread)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL);
|
||||
M_ASSERT(max_thread > 0 && max_thread < INT_MAX);
|
||||
|
||||
atomic_init(&gc_mem->ticket, 0UL);
|
||||
m_genint_init(gc_mem->thread_alloc, (unsigned int) max_thread);
|
||||
gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_ct, NULL, max_thread);
|
||||
if (M_UNLIKELY_NOMEM (gc_mem->thread_data == NULL)) {
|
||||
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_ct));
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX);
|
||||
m_core_backoff_init(gc_mem->thread_data[i].bkoff);
|
||||
}
|
||||
gc_mem->max_thread = (unsigned int) max_thread;
|
||||
gc_mem->mempool_list = NULL;
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_gc_clear(m_gc_t gc_mem)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
|
||||
for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) {
|
||||
m_core_backoff_clear(gc_mem->thread_data[i].bkoff);
|
||||
}
|
||||
M_MEMORY_FREE(gc_mem->thread_data);
|
||||
gc_mem->thread_data = NULL;
|
||||
m_genint_clear(gc_mem->thread_alloc);
|
||||
}
|
||||
|
||||
M_INLINE m_gc_tid_t
|
||||
m_gc_attach_thread(m_gc_t gc_mem)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
|
||||
unsigned id = m_genint_pop(gc_mem->thread_alloc);
|
||||
return M_ASSIGN_CAST(m_gc_tid_t, id);
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
M_ASSERT(id < gc_mem->max_thread);
|
||||
M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
|
||||
|
||||
m_genint_push(gc_mem->thread_alloc, id);
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
M_ASSERT(id < gc_mem->max_thread);
|
||||
M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
|
||||
|
||||
m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1;
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, t);
|
||||
}
|
||||
|
||||
M_INLINE m_gc_ticket_ct
|
||||
m_cmemp00l_gc_min_ticket(m_gc_t gc_mem)
|
||||
{
|
||||
m_gc_ticket_ct min = atomic_load(&gc_mem->thread_data[0].ticket);
|
||||
for(m_gc_tid_t i = 1; i < gc_mem->max_thread; i++) {
|
||||
m_gc_ticket_ct t = atomic_load(&gc_mem->thread_data[i].ticket);
|
||||
min = M_MIN(t, min);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
/* Increase life time of the thread */
|
||||
m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL);
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, t+1);
|
||||
const m_gc_ticket_ct min_ticket = m_cmemp00l_gc_min_ticket(gc_mem);
|
||||
/* Iterate over all registered mempools */
|
||||
m_cmemp00l_list_ct *it = gc_mem->mempool_list;
|
||||
|
||||
while (it) {
|
||||
/* Perform a garbage collect of the mempool */
|
||||
it->gc_on_sleep(gc_mem, it, id, t, min_ticket);
|
||||
/* Next mempool to scan for GC */
|
||||
it = it->next;
|
||||
}
|
||||
/* Sleep the thread */
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, ULONG_MAX);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* */
|
||||
/* Variable Length Array MEMPOOL */
|
||||
/* */
|
||||
/***********************************************************************/
|
||||
|
||||
M_CMEMP00L_DEF_SINGLY_LIST(m_vlapool, char)
|
||||
M_CMEMP00L_DEF_LF_QUEUE(m_vlapool, char)
|
||||
M_CMEMP00L_DEF_SYSTEM_ALLOC(m_vlapool, char)
|
||||
|
||||
typedef struct m_vlapool_lfmp_thread_s {
|
||||
m_vlapool_slist_ct to_be_reclaimed;
|
||||
M_CACHELINE_ALIGN(align1, m_vlapool_slist_ct);
|
||||
} m_vlapool_lfmp_thread_ct;
|
||||
|
||||
M_INLINE void
|
||||
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_ct *t)
|
||||
{
|
||||
m_vlapool_slist_init(t->to_be_reclaimed);
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_ct *t)
|
||||
{
|
||||
M_ASSERT(m_vlapool_slist_empty_p(t->to_be_reclaimed));
|
||||
m_vlapool_slist_clear(t->to_be_reclaimed);
|
||||
}
|
||||
|
||||
typedef struct m_vlapool_s {
|
||||
m_vlapool_lflist_ct to_be_reclaimed;
|
||||
m_vlapool_lflist_ct empty;
|
||||
m_vlapool_lfmp_thread_ct *thread_data;
|
||||
m_cmemp00l_list_ct mvla_node;
|
||||
struct m_gc_s *gc_mem;
|
||||
} m_vlapool_t[1];
|
||||
|
||||
/* Garbage collect of the nodes of the vla mempool on sleep */
|
||||
M_INLINE void
|
||||
m_cmemp00l_vlapool_on_sleep(m_gc_t gc_mem, m_cmemp00l_list_ct *data,
|
||||
m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket)
|
||||
{
|
||||
/* Get back the mempool from the node */
|
||||
struct m_vlapool_s *vlapool =
|
||||
M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_cmemp00l_list_ct, mvla_node);
|
||||
|
||||
/* Move the local nodes of the vlapool to be reclaimed to the thread into the global pool */
|
||||
if (!m_vlapool_slist_empty_p(vlapool->thread_data[id].to_be_reclaimed)) {
|
||||
m_vlapool_lf_node_t *node;
|
||||
/* Get a new empty group of nodes */
|
||||
node = m_vlapool_lflist_pop(vlapool->empty, gc_mem->thread_data[id].bkoff);
|
||||
if (M_UNLIKELY (node == NULL)) {
|
||||
/* Fail to get an empty group of node.
|
||||
Alloc a new one from the system */
|
||||
node = m_vlapool_alloc_node(0);
|
||||
M_ASSERT(node != NULL);
|
||||
}
|
||||
M_ASSERT(m_vlapool_slist_empty_p(node->list));
|
||||
m_vlapool_slist_move(node->list, vlapool->thread_data[id].to_be_reclaimed);
|
||||
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed);
|
||||
m_vlapool_lflist_push(vlapool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff);
|
||||
}
|
||||
|
||||
/* Perform a GC of the freelist of nodes */
|
||||
while (true) {
|
||||
m_vlapool_lf_node_t *node;
|
||||
node = m_vlapool_lflist_pop_if(vlapool->to_be_reclaimed,
|
||||
min_ticket, gc_mem->thread_data[id].bkoff);
|
||||
if (node == NULL) break;
|
||||
// No reuse of VLA nodes. Free physically the node back to the system
|
||||
m_vlapool_slist_clear(node->list);
|
||||
// Add back the empty group of nodes
|
||||
m_vlapool_slist_init(node->list);
|
||||
m_vlapool_lflist_push(vlapool->empty, node, gc_mem->thread_data[id].bkoff);
|
||||
}
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem)
|
||||
{
|
||||
const size_t max_thread = gc_mem->max_thread;
|
||||
|
||||
/* Initialize the thread data of the vlapool */
|
||||
mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_ct, NULL, max_thread);
|
||||
if (M_UNLIKELY_NOMEM (mem->thread_data == NULL)) {
|
||||
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_ct));
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
m_vlapool_lfmp_thread_init(&mem->thread_data[i]);
|
||||
}
|
||||
|
||||
/* Initialize the lists */
|
||||
m_vlapool_lflist_init(mem->to_be_reclaimed, m_vlapool_alloc_node(0));
|
||||
m_vlapool_lflist_init(mem->empty, m_vlapool_alloc_node(0));
|
||||
|
||||
/* Register the mempool in the GC */
|
||||
mem->mvla_node.gc_on_sleep = m_cmemp00l_vlapool_on_sleep;
|
||||
mem->mvla_node.next = gc_mem->mempool_list;
|
||||
gc_mem->mempool_list = &mem->mvla_node;
|
||||
mem->gc_mem = gc_mem;
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_vlapool_clear(m_vlapool_t mem)
|
||||
{
|
||||
const unsigned max_thread = mem->gc_mem->max_thread;
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
m_vlapool_lfmp_thread_clear(&mem->thread_data[i]);
|
||||
}
|
||||
M_MEMORY_FREE(mem->thread_data);
|
||||
mem->thread_data = NULL;
|
||||
m_vlapool_lflist_clear(mem->empty);
|
||||
M_ASSERT(m_vlapool_lflist_empty_p(mem->to_be_reclaimed));
|
||||
m_vlapool_lflist_clear(mem->to_be_reclaimed);
|
||||
/* TODO: Unregister from the GC? */
|
||||
}
|
||||
|
||||
M_INLINE void *
|
||||
m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size)
|
||||
{
|
||||
M_ASSERT(mem != NULL && mem->gc_mem != NULL);
|
||||
M_ASSERT(id < mem->gc_mem->max_thread);
|
||||
M_ASSERT( atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
|
||||
|
||||
// Nothing to do with theses parameters yet
|
||||
(void) mem;
|
||||
(void) id;
|
||||
|
||||
// Ensure the size is big enough to also represent a node
|
||||
size += offsetof(struct m_vlapool_slist_node_s, data);
|
||||
|
||||
// Simply wrap around a system call to get the memory
|
||||
char *ptr = M_MEMORY_REALLOC(char, NULL, size);
|
||||
return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data));
|
||||
}
|
||||
|
||||
M_INLINE void
|
||||
m_vlapool_del(m_vlapool_t mem, void *d, m_gc_tid_t id)
|
||||
{
|
||||
M_ASSERT(mem != NULL && mem->gc_mem != NULL);
|
||||
M_ASSERT(id < mem->gc_mem->max_thread);
|
||||
M_ASSERT(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
|
||||
M_ASSERT(d != NULL);
|
||||
|
||||
// Get back the pointer to a struct m_vlapool_slist_node_s.
|
||||
d = M_ASSIGN_CAST(void *, M_ASSIGN_CAST(char *, d) - offsetof(struct m_vlapool_slist_node_s, data));
|
||||
m_vlapool_slist_node_ct *snode = M_ASSIGN_CAST(m_vlapool_slist_node_ct *, d);
|
||||
// Push the logicaly free memory into the list of the nodes to be reclaimed.
|
||||
m_vlapool_slist_push(mem->thread_data[id].to_be_reclaimed, snode);
|
||||
}
|
||||
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
#if M_USE_SMALL_NAME
|
||||
#define C_MEMPOOL_DEF M_C_MEMPOOL_DEF
|
||||
#endif
|
||||
|
||||
#endif
|
||||
-925
@@ -1,925 +0,0 @@
|
||||
/*
|
||||
* M*LIB - Basic Protected Concurrent module over container.
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_CONCURRENT_H
|
||||
#define MSTARLIB_CONCURRENT_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-thread.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on the given container.
|
||||
USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define M_CONCURRENT_DEF(name, ...) \
|
||||
M_CONCURRENT_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on the given container as the given name name_t
|
||||
USAGE: CONCURRENT_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
|
||||
#define M_CONCURRENT_DEF_AS(name, name_t, ...) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_C0NCURRENT_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
|
||||
(name, __VA_ARGS__, name_t ))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on its given container. Operations that perform only read of the container
|
||||
can be done in parallel.
|
||||
USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define M_CONCURRENT_RP_DEF(name, ...) \
|
||||
M_CONCURRENT_RP_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
as the given name name_t
|
||||
based on its given container. Operations that perform only read of the container
|
||||
can be done in parallel.
|
||||
USAGE: CONCURRENT_RP_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
|
||||
#define M_CONCURRENT_RP_DEF_AS(name, name_t, ...) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_C0NCURRENT_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
|
||||
(name, __VA_ARGS__, name_t ))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/* Define the oplist of a protected concurrent container given its name and its oplist.
|
||||
USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */
|
||||
#define M_CONCURRENT_OPLIST(...) \
|
||||
M_C0NCURRENT_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_BASIC_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************** INTERNAL ***********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_C0NCURRENT_OPLIST_P1(arg) M_C0NCURRENT_OPLIST_P2 arg
|
||||
|
||||
/* Validation of the given oplist */
|
||||
#define M_C0NCURRENT_OPLIST_P2(name, oplist) \
|
||||
M_IF_OPLIST(oplist)(M_C0NCURRENT_OPLIST_P3, M_C0NCURRENT_OPLIST_FAILURE)(name, oplist)
|
||||
|
||||
/* Prepare a clean compilation failure */
|
||||
#define M_C0NCURRENT_OPLIST_FAILURE(name, oplist) \
|
||||
((M_LIB_ERROR(ARGUMENT_OF_CONCURRENT_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
/* OPLIST definition
|
||||
GET_KEY is not present as its interface is not compatible with a concurrent
|
||||
container (_get returns a pointer to an internal data, data that may be
|
||||
destroyed by another thread).
|
||||
*/
|
||||
#define M_C0NCURRENT_OPLIST_P3(name, oplist) \
|
||||
(M_IF_METHOD(INIT, oplist)(INIT(M_F(name, _init)),) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_F(name, _init_set)),) \
|
||||
,M_IF_METHOD(SET, oplist)(SET(M_F(name, _set)),) \
|
||||
,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_F(name, _clear)),) \
|
||||
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \
|
||||
,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \
|
||||
,M_IF_METHOD(SWAP,oplist)(SWAP(M_F(name, _swap)),) \
|
||||
,NAME(name) \
|
||||
,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \
|
||||
,SUBTYPE(M_F(name, _subtype_ct)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(EMPTY_P, oplist)(EMPTY_P(M_F(name,_empty_p)),) \
|
||||
,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_F(name,_size)),) \
|
||||
,M_IF_METHOD(RESET, oplist)(RESET(M_F(name,_reset)),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_OPLIST(M_GET_KEY_OPLIST oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_OPLIST(M_GET_VALUE_OPLIST oplist), ) \
|
||||
,M_IF_METHOD(SET_KEY, oplist)(SET_KEY(M_F(name, _set_at)),) \
|
||||
,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_F(name, _erase)),) \
|
||||
,M_IF_METHOD(PUSH, oplist)(PUSH(M_F(name,_push)),) \
|
||||
,M_IF_METHOD(POP, oplist)(POP(M_F(name,_pop)),) \
|
||||
,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_F(name,_push_move)),) \
|
||||
,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_F(name,_pop_move)),) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \
|
||||
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \
|
||||
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
|
||||
|
||||
/******************************** INTERNAL ***********************************/
|
||||
|
||||
/* Internal contract
|
||||
NOTE: Can't check too much without locking the container itself
|
||||
*/
|
||||
#define M_C0NCURRENT_CONTRACT(c) do { \
|
||||
M_ASSERT ((c) != NULL); \
|
||||
M_ASSERT ((c)->self == (c)); \
|
||||
} while (0)
|
||||
|
||||
/* Deferred evaluation for the concurrent definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_C0NCURRENT_DEF_P1(arg) M_ID( M_C0NCURRENT_DEF_P2 arg )
|
||||
|
||||
/* Validate the value oplist before going further */
|
||||
#define M_C0NCURRENT_DEF_P2(name, type, oplist, concurrent_t) \
|
||||
M_IF_OPLIST(oplist)(M_C0NCURRENT_DEF_P3, M_C0NCURRENT_DEF_FAILURE)(name, type, oplist, concurrent_t)
|
||||
|
||||
/* Stop processing with a compilation failure */
|
||||
#define M_C0NCURRENT_DEF_FAILURE(name, type, oplist, concurrent_t) \
|
||||
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
|
||||
|
||||
/* Internal concurrent definition
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_F(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define M_C0NCURRENT_DEF_P3(name, type, oplist, concurrent_t) \
|
||||
M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \
|
||||
M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t)
|
||||
|
||||
/* Define the type of a concurrent container */
|
||||
#define M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
/* Define a concurrent container using a lock */ \
|
||||
typedef struct M_F(name, _s) { \
|
||||
struct M_F(name, _s) *self; \
|
||||
m_mutex_t lock; \
|
||||
m_cond_t there_is_data; /* condition raised when there is data */ \
|
||||
type data; \
|
||||
} concurrent_t[1]; \
|
||||
\
|
||||
/* Define alias for pointer types */ \
|
||||
typedef struct M_F(name, _s) *M_F(name, _ptr); \
|
||||
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
|
||||
\
|
||||
/* Internal types for oplist */ \
|
||||
typedef concurrent_t M_F(name, _ct); \
|
||||
typedef type M_F(name, _subtype_ct); \
|
||||
\
|
||||
/* Cannot define iterator as it cannot be reliable in a concurrent type */ \
|
||||
|
||||
/* Define the internal services used for the lock strategy */
|
||||
#define M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
/* Initial the fields of the concurrent object not associated to the \
|
||||
sub-container. */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _internal_init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
\
|
||||
/* Clear the fields of the concurrent object not associated to the \
|
||||
sub-container. */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _internal_clear)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_clear(out->lock); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
out->self = NULL; \
|
||||
} \
|
||||
\
|
||||
/* Get the read lock. Multiple threads can get it, but only for reading. \
|
||||
write lock is exclusive. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _read_lock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Free the read lock. See above. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _read_unlock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Wait for a thread pushing some data in the container. \
|
||||
CONSTRAINT: the read lock shall be get before calling this service */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _read_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_cond_wait(out->self->there_is_data, out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Get the write lock. Only one threads can get it, and no other threads \
|
||||
can get the read lock too. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _write_lock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
} \
|
||||
\
|
||||
/* Free the write lock. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _write_unlock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
/* Wait for a thread pushing some data in the container. \
|
||||
CONSTRAINT: the write lock shall be get before calling this service */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _write_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_cond_wait(out->self->there_is_data, out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Wait to all threads that some data are available in the container. \
|
||||
CONSTRAINT: the write lock shall be get before calling this service */ \
|
||||
M_INLINE void \
|
||||
M_F(name, _write_signal)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
/* We need to signal this to ALL waiting threads as multiple threads \
|
||||
may wait on a some data of this container. */ \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
} \
|
||||
|
||||
/* Internal definition of the functions commons to concurrent and rp-concurrent
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_F(name, _t) [ type of the container ]
|
||||
A function is defined only if the underlying container exports the needed
|
||||
services. It is usually one service declared per service exported.
|
||||
*/
|
||||
#define M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _init)(concurrent_t out) \
|
||||
{ \
|
||||
M_F(name, _internal_init)(out); \
|
||||
M_CALL_INIT(oplist, out->data); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(INIT_SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _init_set)(concurrent_t out, concurrent_t const src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
M_ASSERT (out != src); \
|
||||
M_F(name, _internal_init)(out); \
|
||||
M_F(name, _read_lock)(src); \
|
||||
M_CALL_INIT_SET(oplist, out->data, src->data); \
|
||||
M_F(name, _read_unlock)(src); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _set)(concurrent_t out, concurrent_t const src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
if (M_UNLIKELY (out == src)) return; \
|
||||
/* Need to order the locks in a total way to avoid lock deadlock. \
|
||||
Indeed, two call to _set can be done in two threads with : \
|
||||
T1: A := B \
|
||||
T2: B := A \
|
||||
If we lock first the mutex of out, then the src, it could be possible \
|
||||
in the previous scenario that both mutexs are locked: T1 has locked A \
|
||||
and T2 has locked B, and T1 is waiting for locking B, and T2 is waiting \
|
||||
for locking A, resulting in a deadlock. \
|
||||
To avoid this problem, we **always** lock the mutex which address is \
|
||||
the lowest. */ \
|
||||
if (out < src) { \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_F(name, _read_lock)(src); \
|
||||
} else { \
|
||||
M_F(name, _read_lock)(src); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
} \
|
||||
M_CALL_SET(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
M_F(name, _read_lock)(src); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} else { \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
M_F(name, _read_unlock)(src); \
|
||||
} \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(CLEAR, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
/* No need to lock. A clear is supposed to be called when all operations \
|
||||
of the container in other threads are terminated */ \
|
||||
M_CALL_CLEAR(oplist, out->data); \
|
||||
M_F(name, _internal_clear)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(INIT_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _init_move)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
M_ASSERT (out != src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
M_F(name, _internal_init)(out); \
|
||||
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
|
||||
M_F(name, _internal_clear)(src); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_MOVE(oplist, out->data, src->data); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
M_F(name, _internal_clear)(src); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SWAP, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _swap)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
if (M_UNLIKELY (out == src)) return; \
|
||||
/* See comment above */ \
|
||||
if (out < src) { \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_F(name, _write_lock)(src); \
|
||||
} else { \
|
||||
M_F(name, _write_lock)(src); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
} \
|
||||
M_CALL_SWAP(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
M_F(name, _write_unlock)(src); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} else { \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
M_F(name, _write_unlock)(src); \
|
||||
} \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(RESET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_RESET(oplist, out->data); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(EMPTY_P, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _empty_p)(concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
bool b = M_CALL_EMPTY_P(oplist, out->data); \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_SIZE, oplist)( \
|
||||
M_INLINE size_t \
|
||||
M_F(name, _size)(concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
size_t r = M_CALL_GET_SIZE(oplist, out->data); \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET_KEY, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_SET_KEY(oplist, out->data, key, data); \
|
||||
M_F(name, _write_signal)(out); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (out_data != NULL); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
} \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
return p != NULL; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SAFE_GET_KEY, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _safe_get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (out_data != NULL); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_SAFE_GET_KEY(oplist, out->data, key); \
|
||||
M_ASSERT (p != NULL); \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(ERASE_KEY, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
|
||||
/* We suppose that the container has 'infinite' capacity, so \
|
||||
we won't signal that a free space has been created */ \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PUSH, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_PUSH(oplist, out->data, data); \
|
||||
M_F(name, _write_signal)(out); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
\
|
||||
M_EMPLACE_QUEUE_DEF(name, concurrent_t, M_F(name, _emplace), M_GET_OPLIST oplist, M_EMPLACE_QUEUE_GENE) \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(POP, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
/* See comment above */ \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PUSH_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_PUSH_MOVE(oplist, out->data, data); \
|
||||
M_F(name, _write_signal)(out); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(POP_MOVE, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
/* See comment above */ \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _get_str)(m_string_t str, concurrent_t const out, bool a) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
M_CALL_GET_STR(oplist, str, out->data, a); \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(OUT_STR, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _out_str)(FILE *f, concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
M_CALL_OUT_STR(oplist, f, out->data); \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PARSE_STR, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
|
||||
M_F(name, _write_signal)(out); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _in_str)(concurrent_t out, FILE *f) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
bool b = M_CALL_IN_STR(oplist, out->data, f); \
|
||||
M_F(name, _write_signal)(out); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(IN_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _in_serial)(concurrent_t out, m_serial_read_t f) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _write_lock)(out); \
|
||||
m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \
|
||||
M_F(name, _write_signal)(out); \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out1); \
|
||||
M_C0NCURRENT_CONTRACT(out2); \
|
||||
if (M_UNLIKELY (out1 == out2)) return true; \
|
||||
/* See comment above on mutal mutexs */ \
|
||||
if (out1 < out2) { \
|
||||
M_F(name, _read_lock)(out1); \
|
||||
M_F(name, _read_lock)(out2); \
|
||||
} else { \
|
||||
M_F(name, _read_lock)(out2); \
|
||||
M_F(name, _read_lock)(out1); \
|
||||
} \
|
||||
bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \
|
||||
if (out1 < out2) { \
|
||||
M_F(name, _read_unlock)(out2); \
|
||||
M_F(name, _read_unlock)(out1); \
|
||||
} else { \
|
||||
M_F(name, _read_unlock)(out1); \
|
||||
M_F(name, _read_unlock)(out2); \
|
||||
} \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (out_data != NULL); \
|
||||
bool ret = false; \
|
||||
M_F(name, _read_lock)(out); \
|
||||
while (true) { \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
/* No data: wait for a write to signal some data */ \
|
||||
M_F(name, _read_wait)(out); \
|
||||
} \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD2(POP, EMPTY_P, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (p != NULL); \
|
||||
bool ret = false; \
|
||||
M_F(name, _write_lock)(out); \
|
||||
while (true) { \
|
||||
if (!M_CALL_EMPTY_P(oplist, out->data)) { \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
/* No data: wait for a write to signal some data */ \
|
||||
M_F(name, _write_wait)(out); \
|
||||
} \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD2(POP_MOVE, EMPTY_P, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (p != NULL); \
|
||||
bool ret = false; \
|
||||
M_F(name, _write_lock)(out); \
|
||||
while (true) { \
|
||||
if (!M_CALL_EMPTY_P(oplist, out->data)) { \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
/* No data: wait for a write to signal some data */ \
|
||||
M_F(name, _write_wait)(out); \
|
||||
} \
|
||||
M_F(name, _write_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(HASH, oplist)( \
|
||||
M_INLINE size_t \
|
||||
M_F(name, _hash)(concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_F(name, _read_lock)(out); \
|
||||
size_t h = M_CALL_HASH(oplist, out->data); \
|
||||
M_F(name, _read_unlock)(out); \
|
||||
/* The hash is unchanged by the concurrent container */ \
|
||||
return h; \
|
||||
} \
|
||||
,) \
|
||||
|
||||
|
||||
/******************************** INTERNAL ***********************************/
|
||||
|
||||
/* Deferred evaluation for the RP concurrent definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_C0NCURRENT_RP_DEF_P1(arg) M_ID( M_C0NCURRENT_RP_DEF_P2 arg )
|
||||
|
||||
/* Validate the value oplist before going further */
|
||||
#define M_C0NCURRENT_RP_DEF_P2(name, type, oplist, concurrent_t) \
|
||||
M_IF_OPLIST(oplist)(M_C0NCURRENT_RP_DEF_P3, M_C0NCURRENT_RP_DEF_FAILURE)(name, type, oplist, concurrent_t)
|
||||
|
||||
/* Stop processing with a compilation failure */
|
||||
#define M_C0NCURRENT_RP_DEF_FAILURE(name, type, oplist, concurrent_t) \
|
||||
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_RP_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
|
||||
|
||||
/* Internal RP concurrent definition
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_F(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define M_C0NCURRENT_RP_DEF_P3(name, type, oplist, concurrent_t) \
|
||||
M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \
|
||||
M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t)
|
||||
|
||||
/* Define the type of a RP concurrent container */
|
||||
#define M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
typedef struct M_F(name, _s) { \
|
||||
struct M_F(name, _s) *self; \
|
||||
m_mutex_t lock; \
|
||||
m_cond_t rw_done; \
|
||||
size_t read_count; \
|
||||
bool writer_waiting; \
|
||||
m_cond_t there_is_data; /* condition raised when there is data */ \
|
||||
type data; \
|
||||
} concurrent_t[1]; \
|
||||
\
|
||||
typedef struct M_F(name, _s) *M_F(name, _ptr); \
|
||||
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
|
||||
\
|
||||
typedef type M_F(name, _subtype_ct); \
|
||||
|
||||
/* Define the internal services for the lock strategy of a RP container */
|
||||
#define M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _internal_init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->rw_done); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
out->read_count = 0; \
|
||||
out->writer_waiting = false; \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _internal_clear)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_clear(out->lock); \
|
||||
m_cond_clear(out->rw_done); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
out->self = NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _read_lock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
struct M_F(name, _s) *self = out->self; \
|
||||
m_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); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _read_unlock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
struct M_F(name, _s) *self = out->self; \
|
||||
m_mutex_lock (self->lock); \
|
||||
self->read_count --; \
|
||||
if (self->read_count == 0) { \
|
||||
m_cond_broadcast (self->rw_done); \
|
||||
} \
|
||||
m_mutex_unlock (self->lock); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _write_lock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
while (out->writer_waiting == true) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
out->writer_waiting = true; \
|
||||
while (out->read_count > 0) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _write_unlock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
out->writer_waiting = false; \
|
||||
m_cond_broadcast (out->rw_done); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _read_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
struct M_F(name, _s) *self = out->self; \
|
||||
M_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); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _write_wait)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
out->writer_waiting = false; \
|
||||
m_cond_broadcast (out->rw_done); \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
while (out->writer_waiting == true) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
out->writer_waiting = true; \
|
||||
while (out->read_count > 0) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _write_signal)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
|
||||
|
||||
/******************************** INTERNAL ***********************************/
|
||||
|
||||
#if M_USE_SMALL_NAME
|
||||
#define CONCURRENT_DEF M_CONCURRENT_DEF
|
||||
#define CONCURRENT_DEF_AS M_CONCURRENT_DEF_AS
|
||||
#define CONCURRENT_RP_DEF M_CONCURRENT_RP_DEF
|
||||
#define CONCURRENT_RP_DEF_AS M_CONCURRENT_RP_DEF_AS
|
||||
#define CONCURRENT_OPLIST M_CONCURRENT_OPLIST
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - DEQUE module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -99,8 +99,15 @@
|
||||
M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \
|
||||
M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_back), oplist, M_D3QUE_EMPLACE_BACK_DEF) \
|
||||
M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_front), oplist, M_D3QUE_EMPLACE_FRONT_DEF)
|
||||
M_EMPLACE_QUEUE_DEF(name, deque_t, _emplace_back, oplist, M_D3QUE_EMPLACE_BACK_DEF) \
|
||||
M_EMPLACE_QUEUE_DEF(name, deque_t, _emplace_front, oplist, M_D3QUE_EMPLACE_FRONT_DEF)
|
||||
|
||||
// OPLIST for the node deletion
|
||||
#ifdef M_USE_CONTEXT
|
||||
#define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(M_F(name, _node_list_i_del)))
|
||||
#else
|
||||
#define M_D3QU3_NODE_DEL_OPLIST(name) (DEL(API(M_F(name, _node_list_i_del), NONE, ARG2)))
|
||||
#endif
|
||||
|
||||
/* Define the types.
|
||||
It is a linked list of buckets of the types,
|
||||
@@ -115,8 +122,8 @@
|
||||
#define M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \
|
||||
\
|
||||
typedef struct M_F(name, _node_s) { \
|
||||
ILIST_INTERFACE(M_F(name, _node_list), struct M_F(name, _node_s)); \
|
||||
size_t size; \
|
||||
M_ILIST_INTERFACE(M_F(name, _node_list), struct M_F(name, _node_s)); \
|
||||
size_t size, capacity; \
|
||||
type data[M_MIN_FLEX_ARRAY_SIZE]; \
|
||||
} node_t; \
|
||||
\
|
||||
@@ -124,10 +131,13 @@
|
||||
M_GET_REALLOC for the allocation). But we want to delete the nodes \
|
||||
automatically with the intrusive list used for storing the nodes: \
|
||||
so we register as a DEL operator the FREE operator of the oplist. \
|
||||
The interfaces are compatible. \
|
||||
*/ \
|
||||
/* FIXME: How can I separate public types and private implementation? */ \
|
||||
ILIST_DEF(M_F(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \
|
||||
M_P(void, name, _node_list_i_del, node_t *ptr) \
|
||||
{ \
|
||||
M_CALL_FREE(oplist, char, ptr, sizeof(node_t) + ptr->capacity * sizeof(type)); \
|
||||
} \
|
||||
M_ILIST_DEF(M_F(name, _node_list), node_t, M_D3QU3_NODE_DEL_OPLIST(name) ) \
|
||||
\
|
||||
/* Define an internal iterator */ \
|
||||
typedef struct M_F(name, _it2_s) { \
|
||||
@@ -170,25 +180,22 @@
|
||||
#define M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \
|
||||
\
|
||||
/* Allocate a new node for a deque */ \
|
||||
M_INLINE node_t* \
|
||||
M_C3(m_d3qu3_,name,_new_node)(deque_t d) \
|
||||
M_P(node_t*, name, _i_new_node, deque_t d) \
|
||||
{ \
|
||||
size_t def = d->default_size; \
|
||||
/* Test for overflow of the size computation */ \
|
||||
if (M_UNLIKELY_NOMEM (def > SIZE_MAX / sizeof (type) - sizeof(node_t))) { \
|
||||
M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL(char, sizeof(node_t)+def * sizeof(type)); \
|
||||
} \
|
||||
/* Alloc a new node with dynamic size */ \
|
||||
node_t*n = (node_t*) (void*) \
|
||||
M_CALL_REALLOC(oplist, char, NULL, \
|
||||
sizeof(node_t) + def * sizeof(type) ); \
|
||||
M_CALL_REALLOC(oplist, char, NULL, 0, sizeof(node_t) + def * sizeof(type) ); \
|
||||
if (M_UNLIKELY_NOMEM (n==NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL(char, sizeof(node_t)+def * sizeof(type)); \
|
||||
} \
|
||||
/* Initialize the node */ \
|
||||
n->size = def; \
|
||||
n->capacity = def; \
|
||||
M_F(name, _node_list_init_field)(n); \
|
||||
/* Increase the next bucket allocation */ \
|
||||
/* Do not increase it too much if there are few items */ \
|
||||
@@ -198,14 +205,12 @@
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _init)(deque_t d) \
|
||||
M_P(void, name, _init, deque_t d) \
|
||||
{ \
|
||||
M_F(name, _node_list_init)(d->list); \
|
||||
d->default_size = M_USE_DEQUE_DEFAULT_SIZE; \
|
||||
d->count = 0; \
|
||||
node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \
|
||||
if (n == NULL) return; \
|
||||
node_t *n = M_F(name,_i_new_node)M_R(d); \
|
||||
M_F(name, _node_list_push_back)(d->list, n); \
|
||||
d->front->node = n; \
|
||||
d->front->index = M_USE_DEQUE_DEFAULT_SIZE/2; \
|
||||
@@ -214,10 +219,10 @@
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(deque_t d) \
|
||||
M_P(void, name, _reset, deque_t d) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
node_t *min_node = NULL; \
|
||||
for(node_t *n = d->front->node; \
|
||||
n != NULL ; \
|
||||
@@ -239,32 +244,29 @@
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(deque_t d) \
|
||||
M_P(void, name, _clear, deque_t d) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_F(name, _reset)(d); \
|
||||
/* We have registered the delete operator to clear all objects */ \
|
||||
M_F(name, _node_list_clear)(d->list); \
|
||||
/* It is safer to clean some variables */ \
|
||||
M_F(name, _reset)M_R(d); \
|
||||
/* We have registered the delete operator to clear all node objects */ \
|
||||
M_F(name, _node_list_clear)M_R(d->list); \
|
||||
/* It is safer to clean some variables to mark d as invalid */ \
|
||||
d->front->node = NULL; \
|
||||
d->back->node = NULL; \
|
||||
d->count = 0; \
|
||||
} \
|
||||
\
|
||||
M_INLINE type * \
|
||||
M_F(name, _push_back_raw)(deque_t d) \
|
||||
M_P(type *, name, _push_back_raw, deque_t d) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
node_t *n = d->back->node; \
|
||||
size_t index = d->back->index; \
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
/* try to get an already allocated node */ \
|
||||
/* Cannot fit in the current node, try to get another allocated node */ \
|
||||
n = M_F(name, _node_list_next_obj)(d->list, n); \
|
||||
if (n == NULL) { \
|
||||
/* No node exists, allocate a new one */ \
|
||||
n = M_C3(m_d3qu3_,name,_new_node)(d); \
|
||||
if (M_UNLIKELY (n == NULL)) return NULL; \
|
||||
/* No another node exists: allocate a new one */ \
|
||||
n = M_F(name,_i_new_node)M_R(d); \
|
||||
M_F(name, _node_list_push_back)(d->list, n); \
|
||||
} \
|
||||
d->back->node = n; \
|
||||
@@ -278,46 +280,33 @@
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
/* Internal, for INIT_WITH */ \
|
||||
M_INLINE type * \
|
||||
M_F(name, _push_raw)(deque_t d) \
|
||||
M_P(void, name, _push_back, deque_t d, type const x) \
|
||||
{ \
|
||||
return M_F(name, _push_back_raw)(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _push_back)(deque_t d, type const x) \
|
||||
{ \
|
||||
type *p = M_F(name, _push_back_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
type *p = M_F(name, _push_back_raw)M_R(d); \
|
||||
M_ON_EXCEPTION(d->back->index --, d->count--) { \
|
||||
M_CALL_INIT_SET(oplist, *p, x); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE type * \
|
||||
M_F(name, _push_back_new)(deque_t d) \
|
||||
M_P(type *, name, _push_back_new, deque_t d) \
|
||||
{ \
|
||||
type *p = M_F(name, _push_back_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
type *p = M_F(name, _push_back_raw)M_R(d); \
|
||||
M_ON_EXCEPTION(d->back->index --, d->count--) { \
|
||||
M_CALL_INIT(oplist, *p); \
|
||||
} \
|
||||
return p; \
|
||||
} \
|
||||
, ) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _push_back_move)(deque_t d, type *x) \
|
||||
M_P(void, name, _push_back_move, deque_t d, type *x) \
|
||||
{ \
|
||||
M_ASSERT (x != NULL); \
|
||||
type *p = M_F(name, _push_back_raw)(d); \
|
||||
if (M_UNLIKELY(p == NULL)) \
|
||||
return; \
|
||||
M_DO_INIT_MOVE (oplist, *p, *x); \
|
||||
type *p = M_F(name, _push_back_raw)M_R(d); \
|
||||
M_CALL_INIT_MOVE (oplist, *p, *x); \
|
||||
} \
|
||||
\
|
||||
M_INLINE type* \
|
||||
M_F(name, _push_front_raw)(deque_t d) \
|
||||
M_P(type *, name, _push_front_raw, deque_t d) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
node_t *n = d->front->node; \
|
||||
@@ -327,8 +316,7 @@
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
n = M_F(name, _node_list_previous_obj)(d->list, n); \
|
||||
if (n == NULL) { \
|
||||
n = M_C3(m_d3qu3_,name,_new_node)(d); \
|
||||
if (M_UNLIKELY (n == NULL)) return NULL; \
|
||||
n = M_F(name,_i_new_node)M_R(d); \
|
||||
M_F(name, _node_list_push_front)(d->list, n); \
|
||||
} \
|
||||
d->front->node = n; \
|
||||
@@ -341,42 +329,37 @@
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _push_front)(deque_t d, type const x) \
|
||||
M_P(void, name, _push_front, deque_t d, type const x) \
|
||||
{ \
|
||||
type *p = M_F(name, _push_front_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
type *p = M_F(name, _push_front_raw)M_R(d); \
|
||||
M_ON_EXCEPTION(d->front->index ++, d->count--) { \
|
||||
M_CALL_INIT_SET(oplist, *p, x); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE type * \
|
||||
M_F(name, _push_front_new)(deque_t d) \
|
||||
M_P(type *, name, _push_front_new, deque_t d) \
|
||||
{ \
|
||||
type *p = M_F(name, _push_front_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
type *p = M_F(name, _push_front_raw)M_R(d); \
|
||||
M_ON_EXCEPTION(d->front->index ++, d->count--) { \
|
||||
M_CALL_INIT(oplist, *p); \
|
||||
} \
|
||||
return p; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _push_front_move)(deque_t d, type *x) \
|
||||
M_P(void, name, _push_front_move, deque_t d, type *x) \
|
||||
{ \
|
||||
M_ASSERT (x != NULL); \
|
||||
type *p = M_F(name, _push_front_raw)(d); \
|
||||
if (M_UNLIKELY(p == NULL)) \
|
||||
return; \
|
||||
M_DO_INIT_MOVE (oplist, *p, *x); \
|
||||
type *p = M_F(name, _push_front_raw)M_R(d); \
|
||||
M_CALL_INIT_MOVE (oplist, *p, *x); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_back)(type *ptr, deque_t d) \
|
||||
M_P(void, name, _pop_back, type *ptr, deque_t d) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_ASSERT(d->count > 0); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
node_t *n = d->back->node; \
|
||||
size_t index = d->back->index; \
|
||||
index --; \
|
||||
@@ -396,42 +379,35 @@
|
||||
index = n->size-1; \
|
||||
} \
|
||||
if (ptr != NULL) \
|
||||
M_IF_METHOD(MOVE, oplist) ( \
|
||||
M_CALL_MOVE(oplist, *ptr, n->data[index]); else \
|
||||
, \
|
||||
M_CALL_SET(oplist, *ptr, n->data[index]); \
|
||||
) \
|
||||
M_CALL_CLEAR(oplist, n->data[index]); \
|
||||
M_DO_MOVE(oplist, *ptr, n->data[index]); \
|
||||
else \
|
||||
M_CALL_CLEAR(oplist, n->data[index]); \
|
||||
d->count --; \
|
||||
d->back->index = index; \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_back_move)(type *ptr, deque_t d) \
|
||||
M_P(void, name, _pop_back_move, type *ptr, deque_t d) \
|
||||
{ \
|
||||
M_ASSERT(ptr != NULL); \
|
||||
/* Note: Lazy implementation. Can be improved if needed */ \
|
||||
M_CALL_INIT(oplist, *ptr); \
|
||||
M_F(name, _pop_back)(ptr, d); \
|
||||
M_F(name, _pop_back)M_R(ptr, d); \
|
||||
} \
|
||||
, ) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_front)(type *ptr, deque_t d) \
|
||||
M_P(void, name, _pop_front, type *ptr, deque_t d) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_ASSERT(d->count > 0); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
node_t *n = d->front->node; \
|
||||
size_t index = d->front->index; \
|
||||
if (ptr != NULL) \
|
||||
M_IF_METHOD(MOVE, oplist) ( \
|
||||
M_CALL_MOVE(oplist, *ptr, n->data[index]); else \
|
||||
, \
|
||||
M_CALL_SET(oplist, *ptr, n->data[index]); \
|
||||
) \
|
||||
M_CALL_CLEAR(oplist, n->data[index]); \
|
||||
M_DO_MOVE(oplist, *ptr, n->data[index]); \
|
||||
else \
|
||||
M_CALL_CLEAR(oplist, n->data[index]); \
|
||||
index++; \
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
/* If there is a previous node, \
|
||||
@@ -449,7 +425,7 @@
|
||||
/* No next obj. \
|
||||
It is only possible if there was only 1 element */ \
|
||||
M_ASSERT(d->count == 1); \
|
||||
/* Reset the deque to the midle of the current node */ \
|
||||
/* Reset the deque to the middle of the current node */ \
|
||||
d->back->node = d->front->node; \
|
||||
index = d->front->node->size/2; \
|
||||
d->back->index = d->front->node->size/2; \
|
||||
@@ -464,13 +440,12 @@
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _pop_front_move)(type *ptr, deque_t d) \
|
||||
M_P(void, name, _pop_front_move, type *ptr, deque_t d) \
|
||||
{ \
|
||||
M_ASSERT(ptr != NULL); \
|
||||
/* Note: Lazy implementation */ \
|
||||
M_CALL_INIT(oplist, *ptr); \
|
||||
M_F(name, _pop_front)(ptr, d); \
|
||||
M_F(name, _pop_front)M_R(ptr, d); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
@@ -676,8 +651,7 @@
|
||||
return M_CONST_CAST(type, &it->node->data[it->index]); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _remove)(deque_t d, it_t it) \
|
||||
M_P(void, name, _remove, deque_t d, it_t it) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_ASSERT (it != NULL && it->node != NULL); \
|
||||
@@ -724,7 +698,7 @@
|
||||
/* Node deletion */ \
|
||||
M_ASSERT(d->count > 1); \
|
||||
M_F(name, _node_list_unlink)(n); \
|
||||
M_CALL_FREE(oplist, n); \
|
||||
M_CALL_FREE(oplist, char, n, sizeof(node_t) + n->capacity * sizeof(type) ); \
|
||||
} else { \
|
||||
memmove(&n->data[it->index], &n->data[it->index+1], \
|
||||
sizeof(type) * (it->node->size - it->index - 1)); \
|
||||
@@ -739,16 +713,14 @@
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _init_set)(deque_t d, const deque_t src) \
|
||||
M_P(void, name, _init_set, deque_t d, const deque_t src) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(src); \
|
||||
M_ASSERT (d != NULL); \
|
||||
M_F(name, _node_list_init)(d->list); \
|
||||
d->default_size = M_USE_DEQUE_DEFAULT_SIZE + src->count; \
|
||||
node_t *n = M_F(name,_i_new_node)M_R(d); \
|
||||
d->count = src->count; \
|
||||
node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \
|
||||
if (n == NULL) return; \
|
||||
d->default_size /= 2; \
|
||||
M_F(name, _node_list_push_back)(d->list, n); \
|
||||
d->front->node = n; \
|
||||
@@ -756,7 +728,8 @@
|
||||
d->back->node = n; \
|
||||
d->back->index = M_USE_DEQUE_DEFAULT_SIZE/2 + src->count; \
|
||||
it_t it; \
|
||||
size_t i = M_USE_DEQUE_DEFAULT_SIZE/2; \
|
||||
size_t m_volatile i = M_USE_DEQUE_DEFAULT_SIZE/2; \
|
||||
M_ON_EXCEPTION(d->count = i - M_USE_DEQUE_DEFAULT_SIZE/2, d->back->index = i, M_F(name, _clear)(d) ) \
|
||||
for(M_F(name, _it)(it, src); !M_F(name, _end_p)(it) ; M_F(name, _next)(it)) { \
|
||||
type const *obj = M_F(name, _cref)(it); \
|
||||
M_CALL_INIT_SET(oplist, n->data[i], *obj); \
|
||||
@@ -766,14 +739,15 @@
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _set)(deque_t d, deque_t const src) \
|
||||
M_P(void, name, _set, deque_t d, deque_t const src) \
|
||||
{ \
|
||||
if (M_UNLIKELY (src == d)) \
|
||||
return; \
|
||||
/* TODO: Reuse memory of d! */ \
|
||||
M_F(name, _clear)(d); \
|
||||
M_F(name, _init_set)(d, src); \
|
||||
M_ON_EXCEPTION( M_F(name, _init)(d) ) { \
|
||||
M_F(name, _clear)M_R(d); \
|
||||
M_F(name, _init_set)M_R(d, src); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
@@ -792,12 +766,11 @@
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(deque_t d, deque_t src) \
|
||||
M_P(void, name, _move, deque_t d, deque_t src) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_D3QU3_CONTRACT(src); \
|
||||
M_F(name, _clear)(d); \
|
||||
M_F(name, _clear)M_R(d); \
|
||||
M_F(name, _init_move)(d, src); \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
} \
|
||||
@@ -843,10 +816,10 @@
|
||||
return M_CONST_CAST(type, M_F(name, _get)(d, key)); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _set_at)(deque_t d, size_t key, type const x) \
|
||||
M_P(void, name, _set_at, deque_t d, size_t key, type const x) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(d); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_ASSERT_INDEX (key, d->count); \
|
||||
type *p = M_F(name, _get)(d, key); \
|
||||
M_CALL_SET(oplist, *p, x); \
|
||||
@@ -906,11 +879,10 @@
|
||||
, /* NO SWAP */) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _get_str)(m_string_t str, deque_t const deque, bool append) \
|
||||
M_P(void, name, _get_str, m_string_t str, deque_t const deque, bool append) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
|
||||
it_t it; \
|
||||
for (M_F(name, _it)(it, deque) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
@@ -918,9 +890,9 @@
|
||||
type const *item = M_F(name, _cref)(it); \
|
||||
M_CALL_GET_STR(oplist, str, *item, true); \
|
||||
if (!M_F(name, _last_p)(it)) \
|
||||
m_string_push_back (str, M_GET_SEPARATOR oplist); \
|
||||
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
m_string_push_back (str, ']'); \
|
||||
m_string_push_back M_R(str, ']'); \
|
||||
} \
|
||||
, /* no GET_STR */ ) \
|
||||
\
|
||||
@@ -945,137 +917,122 @@
|
||||
, /* no OUT_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _parse_str)(deque_t deque, const char str[], const char **endp) \
|
||||
M_P(bool, name, _parse_str, deque_t deque, const char str[], const char **endp) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
M_ASSERT (str != NULL); \
|
||||
M_F(name,_reset)(deque); \
|
||||
bool success = false; \
|
||||
M_F(name,_reset)M_R(deque); \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) goto exit; \
|
||||
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
|
||||
c = *str++; \
|
||||
if (M_UNLIKELY (c == ']')) {success = true; goto exit;} \
|
||||
if (M_UNLIKELY (c == 0)) goto exit; \
|
||||
if (M_UNLIKELY (c == ']' || c == 0)) goto exit; \
|
||||
str--; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false || c == 0) { goto exit_clear; } \
|
||||
M_F(name, _push_back)(deque, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
success = (c == ']'); \
|
||||
exit_clear: \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (b == false || c == 0) { c = 0; break; } \
|
||||
M_F(name, _push_back)M_R(deque, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no PARSE_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_STR, INIT, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _in_str)(deque_t deque, FILE *file) \
|
||||
M_P(bool, name, _in_str, deque_t deque, FILE *file) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
M_ASSERT (file != NULL); \
|
||||
M_F(name,_reset)(deque); \
|
||||
M_F(name,_reset)M_R(deque); \
|
||||
int c = fgetc(file); \
|
||||
if (M_UNLIKELY (c != '[')) return false; \
|
||||
c = fgetc(file); \
|
||||
if (M_UNLIKELY (c == ']')) return true; \
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) { break; } \
|
||||
M_F(name, _push_back)(deque, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
c = m_core_fgetc_nospace(file); \
|
||||
if (b == false || c == EOF) { c = 0; break; } \
|
||||
M_F(name, _push_back)M_R(deque, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no IN_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _out_serial)(m_serial_write_t f, const deque_t deque) \
|
||||
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const deque_t deque) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
bool first_done = false; \
|
||||
ret = f->m_interface->write_array_start(local, f, deque->count); \
|
||||
ret = f->m_interface->write_array_start M_R(local, f, deque->count); \
|
||||
M_F(name, _it_ct) it; \
|
||||
for (M_F(name, _it)(it, deque) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
M_F(name, _next)(it)){ \
|
||||
type const *item = M_F(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= f->m_interface->write_array_next M_R(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
ret |= f->m_interface->write_array_end M_R(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _in_serial)(deque_t deque, m_serial_read_t f) \
|
||||
M_P(m_serial_return_code_t, name, _in_serial, deque_t deque, m_serial_read_t f) \
|
||||
{ \
|
||||
M_D3QU3_CONTRACT(deque); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
M_F(name,_reset)(deque); \
|
||||
M_F(name,_reset)M_R(deque); \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_F(name, _push_back)(deque, item); \
|
||||
ret = f->m_interface->read_array_next(local, f); \
|
||||
} while (ret == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_F(name, _push_back)M_R(deque, item); \
|
||||
ret = f->m_interface->read_array_next(local, f); \
|
||||
} while (ret == M_SERIAL_OK_CONTINUE); \
|
||||
} \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no IN_SERIAL & INIT */ ) \
|
||||
|
||||
/* Definition of the emplace_back function for deque */
|
||||
#define M_D3QUE_EMPLACE_BACK_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
|
||||
M_INLINE void \
|
||||
function_name(name_t v \
|
||||
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
|
||||
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
|
||||
{ \
|
||||
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
|
||||
M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)M_R(v); \
|
||||
M_ON_EXCEPTION(v->back->index --, v->count--) { \
|
||||
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
/* Definition of the emplace_front function for deque */
|
||||
#define M_D3QUE_EMPLACE_FRONT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
|
||||
M_INLINE void \
|
||||
function_name(name_t v \
|
||||
M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
|
||||
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
|
||||
{ \
|
||||
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
|
||||
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)M_R(v); \
|
||||
M_ON_EXCEPTION(v->front->index ++, v->count--) { \
|
||||
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
@@ -1094,6 +1051,7 @@
|
||||
((M_LIB_ERROR(ARGUMENT_OF_DEQUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
/* OPLIST definition of a deque */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_D3QU3_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)) \
|
||||
,INIT_SET(M_F(name, _init_set)) \
|
||||
@@ -1123,7 +1081,7 @@
|
||||
,RESET(M_F(name,_reset)) \
|
||||
,GET_SIZE(M_F(name, _size)) \
|
||||
,PUSH(M_F(name,_push_back)) \
|
||||
,POP(M_F(name,_pop_back)) \
|
||||
,POP(M_F(name,_pop_front)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \
|
||||
@@ -1134,7 +1092,48 @@
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
|
||||
#else
|
||||
#define M_D3QU3_OPLIST_P3(name, oplist) \
|
||||
(INIT(API_0P(M_F(name, _init))) \
|
||||
,INIT_SET(API_0P(M_F(name, _init_set))) \
|
||||
,INIT_WITH(API_1(M_INIT_WITH_VAI)) \
|
||||
,SET(API_0P(M_F(name, _set))) \
|
||||
,CLEAR(API_0P(M_F(name, _clear))) \
|
||||
,INIT_MOVE(M_F(name, _init_move)) \
|
||||
,MOVE(API_0P(M_F(name, _move))) \
|
||||
,SWAP(M_F(name, _swap)) \
|
||||
,NAME(name) \
|
||||
,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \
|
||||
,SUBTYPE(M_F(name, _subtype_ct)) \
|
||||
,EMPTY_P(M_F(name,_empty_p)) \
|
||||
,IT_TYPE(M_F(name,_it_ct)) \
|
||||
,IT_FIRST(M_F(name,_it)) \
|
||||
,IT_LAST(M_F(name,_it_last)) \
|
||||
,IT_END(M_F(name,_it_end)) \
|
||||
,IT_SET(M_F(name,_it_set)) \
|
||||
,IT_END_P(M_F(name,_end_p)) \
|
||||
,IT_LAST_P(M_F(name,_last_p)) \
|
||||
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
|
||||
,IT_NEXT(M_F(name,_next)) \
|
||||
,IT_PREVIOUS(M_F(name,_previous)) \
|
||||
,IT_REF(M_F(name,_ref)) \
|
||||
,IT_CREF(M_F(name,_cref)) \
|
||||
,IT_REMOVE(API_0P(M_F(name,_remove))) \
|
||||
,RESET(API_0P(M_F(name,_reset))) \
|
||||
,GET_SIZE(M_F(name, _size)) \
|
||||
,PUSH(API_0P(M_F(name,_push_back))) \
|
||||
,POP(API_0P(M_F(name,_pop_front))) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(API_0P(GET_STR(M_F(name, _get_str))),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(API_0P(PARSE_STR(M_F(name, _parse_str))),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(API_0P(M_F(name, _in_str))),) \
|
||||
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),) \
|
||||
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/********************************* INTERNAL **********************************/
|
||||
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Function Object module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -96,7 +96,7 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
/* To be used by M_IF_FUNCOBJ macro defined in m-core.
|
||||
NOTE: It is reversed (0 instead of 1) so that it can be used in M_IF reliabely.
|
||||
NOTE: It is reversed (0 instead of 1) so that it can be used in M_IF reliably.
|
||||
*/
|
||||
#define M_FUNC0BJ_IS_NOT_DEFINED 0
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
*
|
||||
* Structure definitions are specialized in function of the presence or not
|
||||
* of parameters and/or attributes
|
||||
* FIXME: How to factorize reasonnably well between the definitions?
|
||||
* FIXME: How to factorize reasonably well between the definitions?
|
||||
*/
|
||||
|
||||
/* Specialization of the OPLIST in function if there is at least one member or not */
|
||||
|
||||
+8
-34
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Generic module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -82,6 +82,9 @@
|
||||
#define out_serial(x, y) M_G3N_CALL_2(OUT_SERIAL, y, m_serial_write_t, x, TYPE, y)
|
||||
#define in_serial(x, y) M_G3N_CALL_2(IN_SERIAL, x, TYPE, x, m_serial_read_t, y)
|
||||
|
||||
#define let(...) M_LET(__VA_ARGS__)
|
||||
#define print(...) M_PRINT(__VA_ARGS__)
|
||||
|
||||
// for each(item, container)
|
||||
#define each(item, container) \
|
||||
M_G3N_EACHI(item, container, M_C(m_local_it_, __LINE__), M_C(local_pass_, __LINE__))
|
||||
@@ -98,35 +101,6 @@
|
||||
/********************************** INTERNAL *********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Definition of the typeof keyword
|
||||
In C23, we can use the standard keyword.
|
||||
Otherwise we use some extensions of the compilers. */
|
||||
#ifdef _MSC_VER
|
||||
#define m_typeof(x) decltype(x)
|
||||
#elif defined(__GNUC__)
|
||||
#define m_typeof(x) __typeof__(x)
|
||||
#else
|
||||
#define m_typeof(x) typeof(x)
|
||||
#endif
|
||||
|
||||
/* Warnings disabled for CLANG in C mode:
|
||||
Due to the genericity of the _Generic generation,
|
||||
we cannot avoid generating both T and const T in the generic association. */
|
||||
#if defined(__clang__) && __clang_major__ >= 15
|
||||
#define M_G3N_BEGIN_PROTECTED_CODE \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wunreachable-code-generic-assoc\"")
|
||||
|
||||
#define M_G3N_END_PROTECTED_CODE \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
#else
|
||||
|
||||
#define M_G3N_BEGIN_PROTECTED_CODE
|
||||
#define M_G3N_END_PROTECTED_CODE
|
||||
|
||||
#endif
|
||||
|
||||
// Instead of using expensive M_SEQ(1,50), precompute it:
|
||||
#define M_G3N_SEQ_INT \
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, \
|
||||
@@ -183,7 +157,7 @@ typedef const char **m_g3n_cstring_end;
|
||||
// which takes one argument.
|
||||
#define M_G3N_CALL_1(op, x) \
|
||||
M_G3N_BEGIN_PROTECTED_CODE \
|
||||
_Generic( ((void)0, (x)), \
|
||||
_Generic( (x), \
|
||||
M_MAP2(M_G3N_CALL_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \
|
||||
struct m_g3neric_dummy_s *: /* cannot happen */ (void)0) \
|
||||
M_G3N_END_PROTECTED_CODE
|
||||
@@ -247,7 +221,7 @@ typedef const char **m_g3n_cstring_end;
|
||||
// which takes one argument, it_type
|
||||
#define M_G3N_CALL_1i(op, x) \
|
||||
M_G3N_BEGIN_PROTECTED_CODE \
|
||||
_Generic( ((void)0, (x)), \
|
||||
_Generic( (x), \
|
||||
M_MAP2(M_G3N_CALL_1i_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \
|
||||
struct m_g3neric_dummy_s *: /* cannot happen */ (void)0) \
|
||||
M_G3N_END_PROTECTED_CODE
|
||||
@@ -268,7 +242,7 @@ typedef const char **m_g3n_cstring_end;
|
||||
// Create a variable of type OPERATOR call of the oplist registered to the variable 'x'
|
||||
#define M_G3N_TYPE_1(op, x) \
|
||||
M_G3N_BEGIN_PROTECTED_CODE \
|
||||
_Generic( ((void)0, (x)), \
|
||||
_Generic( (x), \
|
||||
M_MAP2(M_G3N_TYPE_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \
|
||||
struct m_g3neric_dummy_s *: /* cannot happen */ (void)0) \
|
||||
M_G3N_END_PROTECTED_CODE
|
||||
@@ -329,7 +303,7 @@ typedef const char **m_g3n_cstring_end;
|
||||
( M_FPRINT_ARG_OUT_STR(f, M_PAIR_2 x, M_PAIR_1 x), \
|
||||
M_FPRINT_ARG_G3N(f, x) )
|
||||
|
||||
/* Ovverride M_PRINT */
|
||||
/* Override M_PRINT */
|
||||
#undef M_PRINT
|
||||
#define M_PRINT(...) do { M_REDUCE2(M_G3N_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, stdout, __VA_ARGS__); } while (0)
|
||||
|
||||
|
||||
+7
-10
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Integer Generator (GENINT) module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -97,22 +97,20 @@ typedef struct m_genint_s {
|
||||
|((M_GEN1NT_ABA_CPT_T)((master) + 1)))
|
||||
|
||||
/* Initialize an integer generator (CONSTRUCTOR).
|
||||
* Initialy, the container is full of all the integers up to 'n-1'
|
||||
* Initially, the container is full of all the integers up to 'n-1'
|
||||
* The typical sequence is to initialize the container, and pop
|
||||
* the integer from it. Each pop integer is **unique** for all threads,
|
||||
* meaning it can be used to index global unique resources shared
|
||||
* for all threads.
|
||||
*/
|
||||
M_INLINE void
|
||||
m_genint_init(m_genint_t s, unsigned int n)
|
||||
M_P(void, m_genint, _init, m_genint_t s, unsigned int n)
|
||||
{
|
||||
M_ASSERT (s != NULL && n > 0 && n <= M_GENINT_MAX_ALLOC);
|
||||
const size_t alloc = (n + M_GEN1NT_LIMBSIZE - 1) / M_GEN1NT_LIMBSIZE;
|
||||
const unsigned int index = n % M_GEN1NT_LIMBSIZE;
|
||||
atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, alloc);
|
||||
atomic_ullong *ptr = M_MEMORY_REALLOC (m_context, atomic_ullong, NULL, 0, alloc);
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
|
||||
M_MEMORY_FULL(alloc);
|
||||
return;
|
||||
M_MEMORY_FULL(atomic_ullong, alloc);
|
||||
}
|
||||
s->n = n;
|
||||
s->data = ptr;
|
||||
@@ -126,11 +124,10 @@ m_genint_init(m_genint_t s, unsigned int n)
|
||||
}
|
||||
|
||||
/* Clear an integer generator (Destructor) */
|
||||
M_INLINE void
|
||||
m_genint_clear(m_genint_t s)
|
||||
M_P(void, m_genint, _clear, m_genint_t s)
|
||||
{
|
||||
M_GEN1NT_CONTRACT(s);
|
||||
M_MEMORY_FREE(s->data);
|
||||
M_MEMORY_FREE(m_context, atomic_ullong, s->data, s->max+1);
|
||||
s->data = NULL;
|
||||
}
|
||||
|
||||
|
||||
+16
-25
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Intrusive List module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -63,7 +63,7 @@
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/* Define the oplist of a doubly-linked instrusive list of type.
|
||||
/* Define the oplist of a doubly-linked intrusive list of type.
|
||||
USAGE:
|
||||
ILIST_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define M_ILIST_OPLIST(...) \
|
||||
@@ -71,6 +71,10 @@
|
||||
((__VA_ARGS__, M_BASIC_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
#define M_ILIST_INIT_FIELD(name, obj) do { \
|
||||
(obj).name.next = NULL; \
|
||||
(obj).name.prev = NULL; \
|
||||
} while(0)
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************** INTERNAL ***********************************/
|
||||
@@ -117,7 +121,6 @@ typedef struct m_il1st_head_s {
|
||||
IT_REF(M_F(name,_ref)), \
|
||||
IT_CREF(M_F(name,_cref)), \
|
||||
IT_REMOVE(M_F(name,_remove)), \
|
||||
M_IF_METHOD(NEW, oplist)(IT_INSERT(M_F(name,_insert)),), \
|
||||
OPLIST(oplist), \
|
||||
SPLICE_BACK(M_F(name,_splice_back)) \
|
||||
)
|
||||
@@ -207,8 +210,7 @@ typedef struct m_il1st_head_s {
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(list_t list) \
|
||||
M_P(void, name, _reset, list_t list) \
|
||||
{ \
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
for(struct m_il1st_head_s *it = list->name.next, *next ; \
|
||||
@@ -229,12 +231,11 @@ typedef struct m_il1st_head_s {
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(list_t list) \
|
||||
M_P(void, name, _clear, list_t list) \
|
||||
{ \
|
||||
/* Nothing to do more than clean the list itself */ \
|
||||
M_F(name, _reset)(list); \
|
||||
/* For safety purpose (create invalid represenation of object) */ \
|
||||
M_F(name, _reset)M_R(list); \
|
||||
/* For safety purpose (create invalid representation of object) */ \
|
||||
list->name.next = NULL; \
|
||||
list->name.prev = NULL; \
|
||||
} \
|
||||
@@ -264,10 +265,9 @@ typedef struct m_il1st_head_s {
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(list_t list, list_t ref) \
|
||||
M_P(void, name, _move, list_t list, list_t ref) \
|
||||
{ \
|
||||
M_F(name, _clear)(list); \
|
||||
M_F(name, _clear)M_R(list); \
|
||||
M_F(name, _init_move)(list, ref); \
|
||||
} \
|
||||
\
|
||||
@@ -508,8 +508,7 @@ typedef struct m_il1st_head_s {
|
||||
return M_CONST_CAST(type, ptr); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _remove)(list_t list, it_t it) \
|
||||
M_P(void, name, _remove, list_t list, it_t it) \
|
||||
{ \
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
M_IL1ST_NODE_CONTRACT(it->current); \
|
||||
@@ -517,30 +516,22 @@ typedef struct m_il1st_head_s {
|
||||
type *obj = M_TYPE_FROM_FIELD(type, it->current, \
|
||||
struct m_il1st_head_s, name); \
|
||||
M_F(name, _unlink)(obj); \
|
||||
M_CALL_CLEAR(oplist, obj); \
|
||||
M_CALL_CLEAR(oplist, *obj); \
|
||||
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
|
||||
M_F(name, _next)(it); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD2(NEW, INIT_SET, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _insert)(list_t list, it_t it, type x) \
|
||||
M_F(name, _insert)(list_t list, it_t it, type *p) \
|
||||
{ \
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
M_IL1ST_NODE_CONTRACT(it->current); \
|
||||
type *p = M_CALL_NEW(oplist, type); \
|
||||
if (M_UNLIKELY_NOMEM (p == NULL)) { \
|
||||
M_MEMORY_FULL (sizeof (type)); \
|
||||
return ; \
|
||||
} \
|
||||
M_CALL_INIT_SET(oplist, *p, x); \
|
||||
type *obj = M_F(name, _ref)(it); \
|
||||
M_F(name, _push_after)(obj, p); \
|
||||
it->current = p; \
|
||||
it->current = &p->name; \
|
||||
(void) list; \
|
||||
M_IL1ST_CONTRACT(name, list); \
|
||||
} \
|
||||
, /* NEW & INIT_SET not defined */) \
|
||||
\
|
||||
M_INLINE type * \
|
||||
M_F(name, _pop_back)(list_t list) \
|
||||
|
||||
-255
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* M*LIB - INTRUSIVE SHARED PTR Module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_I_SHARED_PTR_H
|
||||
#define MSTARLIB_I_SHARED_PTR_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* Define the oplist of a intrusive shared pointer.
|
||||
USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define M_ISHARED_PTR_OPLIST(...) \
|
||||
M_ISHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_BASIC_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/* Interface to add to a structure to allow intrusive support.
|
||||
name: name of the intrusive shared pointer.
|
||||
type: name of the type of the structure (aka. struct test_s) - not used currently.
|
||||
NOTE: There can be only one interface of this kind in a type! */
|
||||
#define M_ISHARED_PTR_INTERFACE(name, type) \
|
||||
atomic_int M_F(name, _cpt)
|
||||
|
||||
|
||||
/* Value of the interface field for static intialization (Uses C99 designated element). */
|
||||
#define M_ISHARED_PTR_STATIC_DESIGNATED_INIT(name, type) \
|
||||
.M_F(name, _cpt) = M_ATOMIC_VAR_INIT(0)
|
||||
|
||||
/* Value of the interface field for static intialization (Uses C89 designated element). */
|
||||
#define M_ISHARED_PTR_STATIC_INIT(name, type) \
|
||||
M_ATOMIC_VAR_INIT(0)
|
||||
|
||||
|
||||
/* Define the intrusive shared pointer type and its M_INLINE functions.
|
||||
USAGE: ISHARED_PTR_DEF(name, type, [, oplist]) */
|
||||
#define M_ISHARED_PTR_DEF(name, ...) \
|
||||
M_ISHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define the intrusive shared pointer type and its M_INLINE functions
|
||||
as the name name_t
|
||||
USAGE: ISHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */
|
||||
#define M_ISHARED_PTR_DEF_AS(name, name_t, ...) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_ISHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
|
||||
(name, __VA_ARGS__ , name_t ))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************** INTERNAL ***********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
// Deferred evaluation
|
||||
#define M_ISHAR3D_PTR_OPLIST_P1(arg) M_ISHAR3D_PTR_OPLIST_P2 arg
|
||||
|
||||
/* Validation of the given oplist */
|
||||
#define M_ISHAR3D_PTR_OPLIST_P2(name, oplist) \
|
||||
M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_OPLIST_P3, M_ISHAR3D_PTR_OPLIST_FAILURE)(name, oplist)
|
||||
|
||||
/* Prepare a clean compilation failure */
|
||||
#define M_ISHAR3D_PTR_OPLIST_FAILURE(name, oplist) \
|
||||
((M_LIB_ERROR(ARGUMENT_OF_ISHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
// Define the oplist
|
||||
#define M_ISHAR3D_PTR_OPLIST_P3(name, oplist) ( \
|
||||
INIT(M_INIT_DEFAULT), \
|
||||
INIT_SET(API_4(M_F(name, _init_set))), \
|
||||
SET(M_F(name, _set) M_IPTR), \
|
||||
CLEAR(M_F(name, _clear)), \
|
||||
RESET(M_F(name, _reset) M_IPTR), \
|
||||
NAME(name), \
|
||||
TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \
|
||||
OPLIST(oplist), \
|
||||
SUBTYPE(M_F(name, _subtype_ct)) \
|
||||
)
|
||||
|
||||
|
||||
/******************************** INTERNAL ***********************************/
|
||||
|
||||
// Deferred evaluatioin
|
||||
#define M_ISHAR3D_PTR_DEF_P1(arg) M_ID( M_ISHAR3D_PTR_DEF_P2 arg )
|
||||
|
||||
/* Validate the oplist before going further */
|
||||
#define M_ISHAR3D_PTR_DEF_P2(name, type, oplist, shared_t) \
|
||||
M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_DEF_P3, M_ISHAR3D_PTR_DEF_FAILURE)(name, type, oplist, shared_t)
|
||||
|
||||
/* Stop processing with a compilation failure */
|
||||
#define M_ISHAR3D_PTR_DEF_FAILURE(name, type, oplist, shared_t) \
|
||||
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ISHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist)
|
||||
|
||||
#define M_ISHAR3D_PTR_DEF_P3(name, type, oplist, shared_t) \
|
||||
M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \
|
||||
|
||||
/* Define the types */
|
||||
#define M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \
|
||||
\
|
||||
/* The shared pointer is only a pointer to the type */ \
|
||||
typedef type *shared_t; \
|
||||
\
|
||||
/* Define internal types for oplist */ \
|
||||
typedef shared_t M_F(name, _ct); \
|
||||
typedef type M_F(name, _subtype_ct); \
|
||||
|
||||
/* Define the core functions */
|
||||
#define M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \
|
||||
\
|
||||
M_INLINE shared_t \
|
||||
M_F(name, _init)(type *ptr) \
|
||||
{ \
|
||||
/* Initialize the type referenced by the pointer */ \
|
||||
if (M_LIKELY (ptr != NULL)) { \
|
||||
atomic_init(&ptr->M_F(name, _cpt), 2); \
|
||||
} \
|
||||
return ptr; \
|
||||
} \
|
||||
\
|
||||
M_INLINE shared_t \
|
||||
M_F(name, _init_set)(shared_t shared) \
|
||||
{ \
|
||||
if (M_LIKELY (shared != NULL)) { \
|
||||
int n = atomic_fetch_add(&(shared->M_F(name, _cpt)), 2); \
|
||||
(void) n; \
|
||||
} \
|
||||
return shared; \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
M_IF_DISABLED_METHOD(NEW, oplist) \
|
||||
( \
|
||||
/* This function is only for static object */ \
|
||||
M_INLINE shared_t \
|
||||
M_F(name, _init_once)(type *shared) \
|
||||
{ \
|
||||
if (M_LIKELY (shared != NULL)) { \
|
||||
/* Pretty much like atomic_add, except the first one increment by 1, others by 2 */ \
|
||||
int o = atomic_load(&(shared->M_F(name, _cpt))); \
|
||||
int n; \
|
||||
do { \
|
||||
n = o + 1 + (o != 0); \
|
||||
} while (!atomic_compare_exchange_strong(&(shared->M_F(name, _cpt)), &o, n)); \
|
||||
if (o == 0) { \
|
||||
/* Partial initialization: _cpt is odd */ \
|
||||
/* Call the INIT function once */ \
|
||||
M_CALL_INIT(oplist, *shared); \
|
||||
/* Finish initialization: _cpt is even */ \
|
||||
atomic_fetch_add(&(shared->M_F(name, _cpt)), 1); \
|
||||
} else if ( (o&1) != 0) { \
|
||||
/* Not fully initialized yet: wait for initialization */ \
|
||||
m_core_backoff_ct bkoff; \
|
||||
m_core_backoff_init(bkoff); \
|
||||
/* Wait for _cpt to be _even */ \
|
||||
while ((atomic_load(&(shared->M_F(name, _cpt)))&1) != 0 ) { \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
M_ASSERT( (atomic_load(&(shared->M_F(name, _cpt)))&1) == 0); \
|
||||
} \
|
||||
return shared; \
|
||||
} \
|
||||
, \
|
||||
/* This function is only for dynamic object */ \
|
||||
M_INLINE shared_t \
|
||||
M_F(name, _init_new)(void) \
|
||||
{ \
|
||||
type *ptr = M_CALL_NEW(oplist, type); \
|
||||
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(type)); \
|
||||
return NULL; \
|
||||
} \
|
||||
M_CALL_INIT(oplist, *ptr); \
|
||||
atomic_init (&ptr->M_F(name, _cpt), 2); \
|
||||
return ptr; \
|
||||
} \
|
||||
/* End of NEW */) \
|
||||
, /* End of INIT */) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(shared_t shared) \
|
||||
{ \
|
||||
if (shared != NULL) { \
|
||||
if (atomic_fetch_sub(&(shared->M_F(name, _cpt)), 2) == 2) { \
|
||||
M_CALL_CLEAR(oplist, *shared); \
|
||||
M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear_ptr)(shared_t *shared) \
|
||||
{ \
|
||||
M_ASSERT(shared != NULL); \
|
||||
M_F(name, _clear)(*shared); \
|
||||
*shared = NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(shared_t *shared) \
|
||||
{ \
|
||||
M_F(name, _clear)(*shared); \
|
||||
*shared = NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _set)(shared_t *ptr, shared_t shared) \
|
||||
{ \
|
||||
M_ASSERT (ptr != NULL); \
|
||||
if (M_LIKELY (*ptr != shared)) { \
|
||||
M_F(name, _clear)(*ptr); \
|
||||
*ptr = M_F(name, _init_set)(shared); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
/******************************** INTERNAL ***********************************/
|
||||
|
||||
#if M_USE_SMALL_NAME
|
||||
#define ISHARED_PTR_OPLIST M_ISHARED_PTR_OPLIST
|
||||
#define ISHARED_PTR_INTERFACE M_ISHARED_PTR_INTERFACE
|
||||
#define ISHARED_PTR_STATIC_DESIGNATED_INIT M_ISHARED_PTR_STATIC_DESIGNATED_INIT
|
||||
#define ISHARED_PTR_STATIC_INIT M_ISHARED_PTR_STATIC_INIT
|
||||
#define ISHARED_PTR_DEF M_ISHARED_PTR_DEF
|
||||
#define ISHARED_PTR_DEF_AS M_ISHARED_PTR_DEF_AS
|
||||
#endif
|
||||
|
||||
#endif
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* M*LIB - MEMPOOL module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_MEMPOOL_H
|
||||
#define MSTARLIB_MEMPOOL_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
/* Fast, fixed size, thread unsafe allocator based on memory regions.
|
||||
No oplist is needed.
|
||||
USAGE:
|
||||
MEMPOOL_DEF(name, type)
|
||||
Example:
|
||||
MEMPOOL_DEF(mempool_uint, unsigned int)
|
||||
...
|
||||
mempool_uint_t m;
|
||||
mempool_uint_init(m);
|
||||
unsigned int *ptr = mempool_uint_alloc(m);
|
||||
*ptr = 17;
|
||||
mempool_uint_free(m, ptr);
|
||||
mempool_uint_clear(m); // Give back memory to system
|
||||
*/
|
||||
#define M_MEMPOOL_DEF(name, type) \
|
||||
M_MEMPOOL_DEF_AS(name, M_F(name,_t), type)
|
||||
|
||||
|
||||
/* Fast, fixed Size, thread unsafe allocator based on memory region.
|
||||
USAGE:
|
||||
MEMPOOL_DEF_AS(name, name_t, type)
|
||||
*/
|
||||
#define M_MEMPOOL_DEF_AS(name, name_t, type) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_M3MPOOL_DEF_P2(name, type, name_t ) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/* User shall be able to cutomize the size of the region segment and/or
|
||||
the minimun number of elements.
|
||||
The default is the number of elements that fits in 16KB, or 256
|
||||
is the size of the type is too big.
|
||||
*/
|
||||
#ifndef M_USE_MEMPOOL_MAX_PER_SEGMENT
|
||||
#define M_USE_MEMPOOL_MAX_PER_SEGMENT(type) \
|
||||
M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U)
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************** INTERNAL *********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
Technically, it uses a list of memory regions, where multiple
|
||||
allocations are performed in each region. However, it
|
||||
can not use m-list since it may be expanded from LIST_DEF
|
||||
(recursive dependency problem). */
|
||||
#define M_M3MPOOL_DEF_P2(name, type, name_t) \
|
||||
M_M3MPOOL_DEF_TYPE(name, type, name_t) \
|
||||
M_M3MPOOL_DEF_CORE(name, type, name_t)
|
||||
|
||||
/* Define the types of the mempool */
|
||||
#define M_M3MPOOL_DEF_TYPE(name, type, name_t) \
|
||||
\
|
||||
/* Define the type of element in a segment of the mempool. \
|
||||
Either it is the basic type or a pointer to another one. */ \
|
||||
typedef union M_F(name,_union_s) { \
|
||||
type t; \
|
||||
union M_F(name,_union_s) *next; \
|
||||
} M_F(name,_union_ct); \
|
||||
\
|
||||
/* Define a segment of a mempool. \
|
||||
It is an array of basic type, each segment is in a linked list */ \
|
||||
typedef struct M_F(name,_segment_s) { \
|
||||
unsigned int count; \
|
||||
struct M_F(name,_segment_s) *next; \
|
||||
M_F(name,_union_ct) tab[M_USE_MEMPOOL_MAX_PER_SEGMENT(type)]; \
|
||||
} M_F(name,_segment_ct); \
|
||||
\
|
||||
/* Define a mempool. \
|
||||
It is a pointer to the first free object within the segments \
|
||||
and the segments themselves */ \
|
||||
typedef struct M_F(name, _s) { \
|
||||
M_F(name,_union_ct) *free_list; \
|
||||
M_F(name,_segment_ct) *current_segment; \
|
||||
} name_t[1]; \
|
||||
|
||||
|
||||
/* Define the core functions of the mempool */
|
||||
#define M_M3MPOOL_DEF_CORE(name, type, name_t) \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name,_init)(name_t mem) \
|
||||
{ \
|
||||
mem->free_list = NULL; \
|
||||
mem->current_segment = M_MEMORY_ALLOC(M_F(name,_segment_ct)); \
|
||||
if (M_UNLIKELY_NOMEM(mem->current_segment == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (M_F(name,_segment_ct))); \
|
||||
return; \
|
||||
} \
|
||||
mem->current_segment->next = NULL; \
|
||||
mem->current_segment->count = 0; \
|
||||
M_M3MPOOL_CONTRACT(mem, type); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name,_clear)(name_t mem) \
|
||||
{ \
|
||||
M_M3MPOOL_CONTRACT(mem, type); \
|
||||
M_F(name,_segment_ct) *segment = mem->current_segment; \
|
||||
while (segment != NULL) { \
|
||||
M_F(name,_segment_ct) *next = segment->next; \
|
||||
M_MEMORY_DEL (segment); \
|
||||
segment = next; \
|
||||
} \
|
||||
/* Clean pointers to be safer */ \
|
||||
mem->free_list = NULL; \
|
||||
mem->current_segment = NULL; \
|
||||
} \
|
||||
\
|
||||
M_INLINE type * \
|
||||
M_F(name,_alloc)(name_t mem) \
|
||||
{ \
|
||||
M_M3MPOOL_CONTRACT(mem, type); \
|
||||
/* Test if one object is in the free list */ \
|
||||
M_F(name,_union_ct) *ret = mem->free_list; \
|
||||
if (ret != NULL) { \
|
||||
/* Yes, so return it, and pop it from the free list */ \
|
||||
mem->free_list = ret->next; \
|
||||
return &ret->t; \
|
||||
} \
|
||||
/* No cheap free object exist. Test within a segment */ \
|
||||
M_F(name,_segment_ct) *segment = mem->current_segment; \
|
||||
M_ASSERT(segment != NULL); \
|
||||
unsigned int count = segment->count; \
|
||||
/* If segment is full, allocate a new one from the system */ \
|
||||
if (M_UNLIKELY (count >= M_USE_MEMPOOL_MAX_PER_SEGMENT(type))) { \
|
||||
M_F(name,_segment_ct) *new_segment = M_MEMORY_ALLOC (M_F(name,_segment_ct)); \
|
||||
if (M_UNLIKELY_NOMEM (new_segment == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (M_F(name,_segment_ct))); \
|
||||
return NULL; \
|
||||
} \
|
||||
new_segment->next = segment; \
|
||||
new_segment->count = 0; \
|
||||
mem->current_segment = new_segment; \
|
||||
segment = new_segment; \
|
||||
count = 0; \
|
||||
} \
|
||||
/* Return the object as the last element of the current segment */ \
|
||||
ret = &segment->tab[count]; \
|
||||
segment->count = count + 1; \
|
||||
M_M3MPOOL_CONTRACT(mem, type); \
|
||||
return &ret->t; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name,_free)(name_t mem, type *ptr) \
|
||||
{ \
|
||||
M_M3MPOOL_CONTRACT(mem, type); \
|
||||
/* NOTE: Unsafe cast: suppose that the given pointer \
|
||||
was allocated by the previous alloc function. */ \
|
||||
M_F(name,_union_ct) *ret = (M_F(name,_union_ct) *)(uintptr_t)ptr; \
|
||||
/* Add the object back in the free list */ \
|
||||
ret->next = mem->free_list; \
|
||||
mem->free_list = ret; \
|
||||
/* NOTE: the objects are NOT given back to the system until the mempool \
|
||||
is fully cleared */ \
|
||||
M_M3MPOOL_CONTRACT(mem, type); \
|
||||
} \
|
||||
|
||||
/* MEMPOOL contract. We only control the current segment. */
|
||||
#define M_M3MPOOL_CONTRACT(mempool, type) do { \
|
||||
M_ASSERT((mempool) != NULL); \
|
||||
M_ASSERT((mempool)->current_segment != NULL); \
|
||||
M_ASSERT((mempool)->current_segment->count <= M_USE_MEMPOOL_MAX_PER_SEGMENT(type)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/********************************** INTERNAL *********************************/
|
||||
|
||||
#if M_USE_SMALL_NAME
|
||||
#define MEMPOOL_DEF M_MEMPOOL_DEF
|
||||
#define MEMPOOL_DEF_AS M_MEMPOOL_DEF_AS
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* M*LIB - Thin Mutex & Thread wrapper (compatibility layer)
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#warning "m-mutex.h is an obsolete header. Use m-thread.h instead."
|
||||
#endif
|
||||
#include "m-thread.h"
|
||||
+70
-49
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - dynamic priority queue module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,18 +25,17 @@
|
||||
#ifndef MSTARLIB_PRIOQUEUE_H
|
||||
#define MSTARLIB_PRIOQUEUE_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-array.h" /* Priority queue are built upon array */
|
||||
|
||||
/* Priority queue based on binary heap implementation */
|
||||
|
||||
/* Define a prioqueue of a given type and its associated functions.
|
||||
/* Define a priority queue (prioqueue) of a given type and its associated functions.
|
||||
USAGE: PRIOQUEUE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define M_PRIOQUEUE_DEF(name, ...) \
|
||||
M_PRIOQUEUE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a prioqueue of a given type and its associated functions.
|
||||
/* Define a priority queue (prioqueue) of a given type and its associated functions.
|
||||
as the name name_t with an iterator named it_t
|
||||
USAGE: PRIOQUEUE_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */
|
||||
#define M_PRIOQUEUE_DEF_AS(name, name_t, it_t, ...) \
|
||||
@@ -72,6 +71,7 @@
|
||||
((M_LIB_ERROR(ARGUMENT_OF_PRIOQUEUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
/* Define oplist of a priority queue */
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_PR1OQUEUE_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)) \
|
||||
,INIT_SET(M_F(name, _init_set)) \
|
||||
@@ -106,7 +106,42 @@
|
||||
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \
|
||||
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \
|
||||
)
|
||||
|
||||
#else
|
||||
#define M_PR1OQUEUE_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)) \
|
||||
,INIT_SET(API_0P(M_F(name, _init_set))) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
,SET(API_0P(M_F(name, _set))) \
|
||||
,CLEAR(API_0P(M_F(name, _clear))) \
|
||||
,INIT_MOVE(M_F(name, _init_move)) \
|
||||
,MOVE(API_0P(M_F(name, _move))) \
|
||||
,SWAP(M_F(name, _swap)) \
|
||||
,NAME(name) \
|
||||
,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \
|
||||
,SUBTYPE(M_F(name, _subtype_ct)) \
|
||||
,RESET(API_0P(M_F(name,_reset))) \
|
||||
,PUSH(API_0P(M_F(name,_push))) \
|
||||
,POP(API_0P(M_F(name,_pop))) \
|
||||
,OPLIST(oplist) \
|
||||
,EMPTY_P(M_F(name, _empty_p)) \
|
||||
,GET_SIZE(M_F(name, _size)) \
|
||||
,IT_TYPE(M_F(name, _it_ct)) \
|
||||
,IT_FIRST(M_F(name,_it)) \
|
||||
,IT_END(M_F(name,_it_end)) \
|
||||
,IT_SET(M_F(name,_it_set)) \
|
||||
,IT_END_P(M_F(name,_end_p)) \
|
||||
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
|
||||
,IT_LAST_P(M_F(name,_last_p)) \
|
||||
,IT_NEXT(M_F(name,_next)) \
|
||||
,IT_CREF(M_F(name,_cref)) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(API_0P(M_F(name, _get_str))),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(API_0P(M_F(name, _in_str))),) \
|
||||
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),) \
|
||||
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/********************************** INTERNAL *********************************/
|
||||
|
||||
@@ -131,13 +166,13 @@
|
||||
*/
|
||||
#define M_PR1OQUEUE_DEF_P3(name, type, oplist, prioqueue_t, it_t) \
|
||||
/* Definition of the internal array used to construct the priority queue */ \
|
||||
ARRAY_DEF(M_F(name, _array), type, oplist) \
|
||||
M_ARRAY_DEF(M_F(name, _array), type, oplist) \
|
||||
M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_PR1OQUEUE_DEF_CORE(name, type, oplist, prioqueue_t, it_t) \
|
||||
M_PR1OQUEUE_DEF_IT(name, type, oplist, prioqueue_t, it_t) \
|
||||
M_PR1OQUEUE_DEF_IO(name, type, oplist, prioqueue_t, it_t) \
|
||||
M_EMPLACE_QUEUE_DEF(name, prioqueue_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
|
||||
M_EMPLACE_QUEUE_DEF(name, prioqueue_t, _emplace, oplist, M_EMPLACE_QUEUE_GENE)
|
||||
|
||||
/* Define the types */
|
||||
#define M_PR1OQUEUE_DEF_TYPE(name, type, oplist, prioqueue_t, it_t) \
|
||||
@@ -167,22 +202,19 @@
|
||||
M_F(name, _array_init)(p->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _init_set)(prioqueue_t p, prioqueue_t const o) \
|
||||
M_P(void, name, _init_set, prioqueue_t p, prioqueue_t const o) \
|
||||
{ \
|
||||
M_F(name, _array_init_set)(p->array, o->array); \
|
||||
M_F(name, _array_init_set) M_R(p->array, o->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _set)(prioqueue_t p, prioqueue_t const o) \
|
||||
M_P(void, name, _set, prioqueue_t p, prioqueue_t const o) \
|
||||
{ \
|
||||
M_F(name, _array_set)(p->array, o->array); \
|
||||
M_F(name, _array_set) M_R(p->array, o->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(prioqueue_t p) \
|
||||
M_P(void, name, _clear, prioqueue_t p) \
|
||||
{ \
|
||||
M_F(name, _array_clear)(p->array); \
|
||||
M_F(name, _array_clear) M_R(p->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
@@ -191,10 +223,9 @@
|
||||
M_F(name, _array_init_move)(p->array, o->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(prioqueue_t p, prioqueue_t o) \
|
||||
M_P(void, name, _move, prioqueue_t p, prioqueue_t o) \
|
||||
{ \
|
||||
M_F(name, _array_move)(p->array, o->array); \
|
||||
M_F(name, _array_move) M_R(p->array, o->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
@@ -203,10 +234,9 @@
|
||||
M_F(name, _array_swap)(p->array, o->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(prioqueue_t p) \
|
||||
M_P(void, name, _reset, prioqueue_t p) \
|
||||
{ \
|
||||
M_F(name, _array_reset)(p->array); \
|
||||
M_F(name, _array_reset)M_R(p->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE size_t \
|
||||
@@ -249,11 +279,10 @@
|
||||
return M_F(name, _array_size)(p->array); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _push)(prioqueue_t p, type const x) \
|
||||
M_P(void, name, _push, prioqueue_t p, type const x) \
|
||||
{ \
|
||||
/* Push back the new element at the end of the array */ \
|
||||
M_F(name, _array_push_back)(p->array, x); \
|
||||
M_F(name, _array_push_back) M_R(p->array, x); \
|
||||
\
|
||||
/* Reorder the array by swapping with its parent \
|
||||
* until it reaches the right position */ \
|
||||
@@ -273,14 +302,13 @@
|
||||
return M_F(name, _array_cget)(p->array, 0); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _pop)(type *x, prioqueue_t p) \
|
||||
M_P(void, name, _pop, type *x, prioqueue_t p) \
|
||||
{ \
|
||||
/* Swap the front element with the last element */ \
|
||||
size_t size = M_F(name, _array_size)(p->array)-1; \
|
||||
M_F(name, _array_swap_at) (p->array, 0, size); \
|
||||
/* Swap the new last element */ \
|
||||
M_F(name, _array_pop_back)(x, p->array); \
|
||||
M_F(name, _array_pop_back) M_R(x, p->array); \
|
||||
\
|
||||
/* Reorder the heap */ \
|
||||
size_t i = 0; \
|
||||
@@ -323,8 +351,7 @@
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _erase)(prioqueue_t p, type const x) \
|
||||
M_P(bool, name, _erase, prioqueue_t p, type const x) \
|
||||
{ \
|
||||
/* First pass: search for an item EQUAL to x */ \
|
||||
size_t size = M_F(name, _array_size)(p->array); \
|
||||
@@ -335,7 +362,7 @@
|
||||
/* Swap the found item and the last element */ \
|
||||
size--; \
|
||||
M_F(name, _array_swap_at) (p->array, i, size); \
|
||||
M_F(name, _array_pop_back)(NULL, p->array); \
|
||||
M_F(name, _array_pop_back) M_R(NULL, p->array); \
|
||||
/* Move back the last swapped element to its right position in the heap */ \
|
||||
while (true) { \
|
||||
size_t child = M_F(name, _i_lchild)(i); \
|
||||
@@ -352,8 +379,7 @@
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _update)(prioqueue_t p, type const xold, type const xnew) \
|
||||
M_P(void, name, _update, prioqueue_t p, type const xold, type const xnew) \
|
||||
{ \
|
||||
/* NOTE: xold can be the same pointer than xnew */ \
|
||||
/* First pass: search for an item EQUAL to x */ \
|
||||
@@ -364,7 +390,7 @@
|
||||
/* Test if the position of the old data is further or nearer than the new */ \
|
||||
int cmp = M_CALL_CMP(oplist, *M_F(name, _array_cget)(p->array, i), xnew); \
|
||||
/* Set the found item to the new element */ \
|
||||
M_F(name, _array_set_at) (p->array, i, xnew); \
|
||||
M_F(name, _array_set_at) M_R(p->array, i, xnew); \
|
||||
if (cmp < 0) { \
|
||||
/* Move back the updated element to its new position, further in the heap */ \
|
||||
while (true) { \
|
||||
@@ -467,42 +493,37 @@
|
||||
,/* No OUT_STR */) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _in_str)(prioqueue_t p, FILE *file) \
|
||||
M_P(bool, name, _in_str, prioqueue_t p, FILE *file) \
|
||||
{ \
|
||||
return M_F(name, _array_in_str)(p->array, file); \
|
||||
return M_F(name, _array_in_str)M_R(p->array, file); \
|
||||
} \
|
||||
,/* No IN_STR */) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
M_INLINE void \
|
||||
M_F(name, _get_str)(string_t str, const prioqueue_t p, bool append) \
|
||||
M_P(void, name, _get_str, m_string_t str, const prioqueue_t p, bool append) \
|
||||
{ \
|
||||
M_F(name, _array_get_str)(str, p->array, append); \
|
||||
M_F(name, _array_get_str)M_R(str, p->array, append); \
|
||||
} \
|
||||
,/* No GET_STR */) \
|
||||
\
|
||||
M_IF_METHOD(PARSE_STR, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _parse_str)(prioqueue_t p, const char str[], const char **endp) \
|
||||
M_P(bool, name, _parse_str, prioqueue_t p, const char str[], const char **endp) \
|
||||
{ \
|
||||
return M_F(name, _array_parse_str)(p->array, str, endp); \
|
||||
return M_F(name, _array_parse_str)M_R(p->array, str, endp); \
|
||||
} \
|
||||
,/* No PARSE_STR */) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _out_serial)(m_serial_write_t f, const prioqueue_t p) \
|
||||
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, const prioqueue_t p) \
|
||||
{ \
|
||||
return M_F(name, _array_out_serial)(f, p->array); \
|
||||
return M_F(name, _array_out_serial) M_R(f, p->array); \
|
||||
} \
|
||||
,/* No OUT_SERIAL */) \
|
||||
\
|
||||
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _in_serial)(prioqueue_t p, m_serial_read_t f) \
|
||||
M_P(m_serial_return_code_t, name, _in_serial, prioqueue_t p, m_serial_read_t f) \
|
||||
{ \
|
||||
return M_F(name, _array_in_serial)(p->array, f); \
|
||||
return M_F(name, _array_in_serial)M_R(p->array, f); \
|
||||
} \
|
||||
,/* No in_SERIAL */) \
|
||||
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* M*LIB - Fixed size (Bounded) QUEUE & STACK interface (Not Thread safe)
|
||||
*
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_QUEUE_H
|
||||
#define MSTARLIB_QUEUE_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
/* Define a fixed size queue.
|
||||
If size is 0, then the size will only be defined at run-time when initializing the queue,
|
||||
otherwise the size will be a compile time constant embedded in the structure.
|
||||
USAGE: QUEUE_DEF(name, type, size_of_QUEUE_or_0[, oplist]) */
|
||||
#define M_QUEUE_DEF(name, ... ) M_QUEUE_DEF_AS(name, M_F(name, _t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a fixed size queue
|
||||
as the provided type name_t.
|
||||
USAGE: QUEUE_DEF_AS(name, name_t, type, size_of_QUEUE_or_0[, oplist of type]) */
|
||||
#define M_QUEUE_DEF_AS(name, name_t, type, ... ) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_QUEU3_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t, 0), \
|
||||
(name, type, __VA_ARGS__, name_t, 0))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
/* Define a fixed size stack.
|
||||
If size is 0, then the size will only be defined at run-time when initializing the stack,
|
||||
otherwise the size will be a compile time constant embedded in the structure.
|
||||
USAGE: STACK_DEF(name, type, size_of_stack_or_0[, oplist]) */
|
||||
#define M_STACK_DEF(name, ... ) M_STACK_DEF_AS(name, M_F(name, _t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a fixed size stack
|
||||
as the provided type name_t.
|
||||
USAGE: QUEUE_DEF_AS(name, name_t, type, size_of_stack_or_0[, oplist of type]) */
|
||||
#define M_STACK_DEF_AS(name, name_t, type, ... ) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_QUEU3_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, type, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(type)(), name_t, 1), \
|
||||
(name, type, __VA_ARGS__, name_t, 1))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
/* Define the oplist of a fixed size queue given its name and its oplist.
|
||||
USAGE: QUEUE_OPLIST(name[, oplist of the type]) */
|
||||
#define M_QUEUE_OPLIST(...) \
|
||||
M_QUEU3_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_BASIC_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************** INTERNAL *********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_QUEU3_OPLIST_P1(arg) M_QUEU3_OPLIST_P2 arg
|
||||
|
||||
/* Validation of the given oplist */
|
||||
#define M_QUEU3_OPLIST_P2(name, oplist) \
|
||||
M_IF_OPLIST(oplist)(M_QUEU3_OPLIST_P3, M_QUEU3_OPLIST_FAILURE)(name, oplist)
|
||||
|
||||
/* Prepare a clean compilation failure */
|
||||
#define M_QUEU3_OPLIST_FAILURE(name, oplist) \
|
||||
((M_LIB_ERROR(ARGUMENT_OF_BUFFER_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
/* OPLIST definition of a buffer */
|
||||
#define M_QUEU3_OPLIST_P3(name, oplist) \
|
||||
(INIT(API_0P(M_F(name, _i_init))) \
|
||||
,INIT_SET(API_0P(M_F(name, _init_set))) \
|
||||
,SET(API_0P(M_F(name, _set))) \
|
||||
,CLEAR(API_0P(M_F(name, _clear))) \
|
||||
,NAME(name) \
|
||||
,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \
|
||||
,SUBTYPE(M_F(name, _subtype_ct)) \
|
||||
,RESET(API_0P(M_F(name,_reset))) \
|
||||
,PUSH(API_0P(M_F(name,_push))) \
|
||||
,POP(API_0P(M_F(name,_pop))) \
|
||||
,OPLIST(oplist) \
|
||||
,EMPTY_P(M_F(name, _empty_p)), \
|
||||
,FULL_P(M_F(name, _full_p)), \
|
||||
,GET_SIZE(M_F(name, _size)) \
|
||||
)
|
||||
|
||||
|
||||
|
||||
/* Test if the size is only run-time or build time */
|
||||
#define M_QUEU3_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size))
|
||||
|
||||
/* Return the size (run time or build time).
|
||||
NOTE: It assumed that the queue variable name is 'v' */
|
||||
#define M_QUEU3_SIZE(v, m_size) \
|
||||
M_QUEU3_IF_CTE_SIZE(m_size) (m_size, v->capacity)
|
||||
|
||||
/* Contract of a queue. */
|
||||
#define M_QUEU3_CONTRACT(queue, size) do { \
|
||||
M_ASSERT (queue != NULL); \
|
||||
M_ASSERT (queue->data != NULL); \
|
||||
}while (0)
|
||||
|
||||
/* Deferred evaluation for the definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_QUEU3_DEF_P1(arg) M_ID( M_QUEU3_DEF_P2 arg )
|
||||
|
||||
/* Validate the value oplist before going further */
|
||||
#define M_QUEU3_DEF_P2(name, type, m_size, oplist, queue_t, stack_mode) \
|
||||
M_IF_OPLIST(oplist)(M_QUEU3_DEF_P3, M_QUEU3_DEF_FAILURE)(name, type, m_size, oplist, queue_t, stack_mode)
|
||||
|
||||
/* Stop processing with a compilation failure */
|
||||
#define M_QUEU3_DEF_FAILURE(name, type, m_size, oplist, queue_t, stack_mode) \
|
||||
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(M_QUEUE_DEF/M_STACK_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
|
||||
|
||||
/* Define the queue type using mutex lock and its functions.
|
||||
- name: main prefix of the container
|
||||
- type: type of an element of the queue
|
||||
- m_size: constant to 0 if variable runtime size, or else the fixed size of the queue
|
||||
- oplist: the oplist of the type of an element of the queue
|
||||
- queue_t: name of the queue
|
||||
*/
|
||||
#define M_QUEU3_DEF_P3(name, type, m_size, oplist, queue_t, stack_mode) \
|
||||
M_QUEU3_DEF_TYPE(name, type, m_size, oplist, queue_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_QUEU3_DEF_CORE(name, type, m_size, oplist, queue_t, stack_mode) \
|
||||
M_EMPLACE_QUEUE_DEF(name, queue_t, _emplace, oplist, M_EMPLACE_QUEUE_GENE)
|
||||
|
||||
/* Define the type of a queue */
|
||||
#define M_QUEU3_DEF_TYPE(name, type, m_size, oplist, queue_t) \
|
||||
\
|
||||
typedef struct M_F(name, _s) { \
|
||||
unsigned idx_prod; /* Index of the production threads */ \
|
||||
M_QUEU3_IF_CTE_SIZE(m_size)( ,unsigned capacity;) /* Capacity of the queue */ \
|
||||
unsigned idx_cons; /* Index of the consumption threads */ \
|
||||
unsigned number; /* Number of elements in the queue */ \
|
||||
/* If fixed size, array of elements, otherwise pointer to element */ \
|
||||
type M_QUEU3_IF_CTE_SIZE(m_size)(data[m_size], *data); \
|
||||
} queue_t[1]; \
|
||||
\
|
||||
typedef struct M_F(name, _s) *M_F(name, _ptr); \
|
||||
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
|
||||
/* Internal types used by the oplist */ \
|
||||
typedef type M_F(name, _subtype_ct); \
|
||||
typedef queue_t M_F(name, _ct); \
|
||||
|
||||
/* Define the core functionalities of a queue */
|
||||
#define M_QUEU3_DEF_CORE(name, type, m_size, oplist, queue_t, stack_mode) \
|
||||
\
|
||||
M_P(void, name, _init, queue_t v, size_t size) \
|
||||
{ \
|
||||
M_ASSERT(size <= UINT_MAX); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_QUEU3_IF_CTE_SIZE(m_size)(M_ASSERT(size == m_size), v->capacity = (unsigned) size); \
|
||||
v->idx_prod = v->idx_cons = v->number = 0; \
|
||||
\
|
||||
M_QUEU3_IF_CTE_SIZE(m_size)( /* Statically allocated */ , \
|
||||
v->data = M_CALL_REALLOC(oplist, type, NULL, 0, M_QUEU3_SIZE(v, m_size)); \
|
||||
if (M_UNLIKELY_NOMEM (v->data == NULL)) { \
|
||||
M_MEMORY_FULL (type, M_QUEU3_SIZE(v, m_size)); \
|
||||
} \
|
||||
) \
|
||||
for(size_t i = 0; i < size; i++) { \
|
||||
M_CALL_INIT(oplist, v->data[i]); \
|
||||
} \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
} \
|
||||
\
|
||||
M_QUEU3_IF_CTE_SIZE(m_size)( \
|
||||
M_P(void, name,_i_init, queue_t v) \
|
||||
{ \
|
||||
M_F(name, _init)M_R(v, m_size); \
|
||||
} \
|
||||
, ) \
|
||||
\
|
||||
M_P(void, name, _i_clear_obj, queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
for(size_t i = 0; i < M_QUEU3_SIZE(v, m_size); i++) { \
|
||||
M_CALL_CLEAR(oplist, v->data[i]); \
|
||||
} \
|
||||
v->idx_prod = v->idx_cons = v->number = 0; \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
} \
|
||||
\
|
||||
M_P(void, name, _clear, queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
M_F(name,_i_clear_obj)M_R(v); \
|
||||
M_QUEU3_IF_CTE_SIZE(m_size)( , \
|
||||
M_CALL_FREE(oplist, type, v->data, M_QUEU3_SIZE(v, m_size)); \
|
||||
v->data = NULL; \
|
||||
) \
|
||||
} \
|
||||
\
|
||||
M_N(void, name, _reset, queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
v->idx_prod = v->idx_cons = v->number = 0; \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
} \
|
||||
\
|
||||
M_P(void, name, _init_set, queue_t dest, const queue_t src) \
|
||||
{ \
|
||||
M_ASSERT (dest != src); \
|
||||
M_QUEU3_CONTRACT(src,m_size); \
|
||||
M_F(name, _init) M_R(dest, M_QUEU3_SIZE(src, m_size)); \
|
||||
for(size_t i = 0; i < M_QUEU3_SIZE(src, m_size); i++) { \
|
||||
M_CALL_SET(oplist, dest->data[i], src->data[i]); \
|
||||
} \
|
||||
dest->idx_prod = src->idx_prod; \
|
||||
dest->idx_cons = src->idx_cons; \
|
||||
dest->number = src->number; \
|
||||
M_QUEU3_CONTRACT(dest, m_size); \
|
||||
} \
|
||||
\
|
||||
M_P(void, name, _set, queue_t dest, const queue_t src) \
|
||||
{ \
|
||||
M_F(name, _clear) M_R(dest); \
|
||||
M_F(name, _init_set) M_R(dest, src); \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _empty_p)(const queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
return v->number == 0; \
|
||||
} \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _full_p)(const queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
return v->number == M_QUEU3_SIZE(v, m_size); \
|
||||
} \
|
||||
\
|
||||
M_INLINE size_t \
|
||||
M_F(name, _size)(const queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
return v->number; \
|
||||
} \
|
||||
\
|
||||
M_P(bool, name, _push, queue_t v, type const data) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
if (M_F(name, _full_p)(v)) \
|
||||
return false; \
|
||||
\
|
||||
unsigned idx = v->idx_prod; \
|
||||
M_CALL_SET(oplist, v->data[idx], data); \
|
||||
idx++; \
|
||||
M_IF(stack_mode)(, idx = (idx == M_QUEU3_SIZE(v, m_size)) ? 0 : idx); \
|
||||
v->idx_prod = idx; \
|
||||
\
|
||||
v->number++; \
|
||||
\
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
M_P(bool, name, _pop, type *data, queue_t v) \
|
||||
{ \
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
M_ASSERT (data != NULL); \
|
||||
M_UNUSED_CONTEXT(); \
|
||||
if (M_F(name, _empty_p)(v)) \
|
||||
return false; \
|
||||
\
|
||||
/* POP data from the queue and update INDEX */ \
|
||||
M_IF(stack_mode)( \
|
||||
/* STACK queue */ \
|
||||
v->idx_prod --; \
|
||||
M_CALL_SET(oplist, *data, v->data[v->idx_prod]); \
|
||||
, \
|
||||
/* FIFO queue */ \
|
||||
M_CALL_SET(oplist, *data, v->data[v->idx_cons]); \
|
||||
v->idx_cons = (v->idx_cons == M_QUEU3_SIZE(v, m_size)-1) ? 0 : (v->idx_cons + 1); \
|
||||
) \
|
||||
v->number--; \
|
||||
\
|
||||
M_QUEU3_CONTRACT(v,m_size); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
M_INLINE size_t \
|
||||
M_F(name, _capacity)(const queue_t v) \
|
||||
{ \
|
||||
(void) v; /* may be unused */ \
|
||||
return M_QUEU3_SIZE(v, m_size); \
|
||||
} \
|
||||
|
||||
|
||||
#if M_USE_SMALL_NAME
|
||||
#define QUEUE_DEF M_QUEUE_DEF
|
||||
#define QUEUE_DEF_AS M_QUEUE_DEF_AS
|
||||
#define STACK_DEF M_STACK_DEF
|
||||
#define STACK_DEF_AS M_STACK_DEF_AS
|
||||
#define QUEUE_OPLIST M_QUEUE_OPLIST
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+119
-129
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - RED BLACK TREE module
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -71,6 +71,7 @@
|
||||
/* OPLIST definition of a rbtree
|
||||
NOTE: IT_REF is not exported so that the container appears as not modifiable
|
||||
by algorithm.*/
|
||||
#ifndef M_USE_CONTEXT
|
||||
#define M_RBTR33_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)), \
|
||||
INIT_SET(M_F(name, _init_set)), \
|
||||
@@ -110,7 +111,46 @@
|
||||
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
|
||||
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
|
||||
#else
|
||||
#define M_RBTR33_OPLIST_P3(name, oplist) \
|
||||
(INIT(M_F(name, _init)), \
|
||||
INIT_SET(API_0P(M_F(name, _init_set))), \
|
||||
INIT_WITH(API_1(M_INIT_EMPLACE_VAI)), \
|
||||
SET(API_0P(M_F(name, _set))), \
|
||||
CLEAR(API_0P(M_F(name, _clear))), \
|
||||
INIT_MOVE(M_F(name, _init_move)), \
|
||||
MOVE(API_0P(M_F(name, _move))), \
|
||||
SWAP(M_F(name, _swap)), \
|
||||
NAME(name), TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \
|
||||
SUBTYPE(M_F(name, _subtype_ct)), \
|
||||
EMPTY_P(M_F(name,_empty_p)), \
|
||||
GET_SIZE(M_F(name, _size)), \
|
||||
IT_TYPE(M_F(name, _it_ct)), \
|
||||
IT_FIRST(M_F(name,_it)), \
|
||||
IT_SET(M_F(name,_it_set)), \
|
||||
IT_LAST(M_F(name,_it_last)), \
|
||||
IT_END(M_F(name,_it_end)), \
|
||||
IT_END_P(M_F(name,_end_p)), \
|
||||
IT_LAST_P(M_F(name,_last_p)), \
|
||||
IT_EQUAL_P(M_F(name,_it_equal_p)), \
|
||||
IT_NEXT(M_F(name,_next)), \
|
||||
IT_PREVIOUS(M_F(name,_previous)), \
|
||||
IT_CREF(M_F(name,_cref)), \
|
||||
IT_REMOVE(M_F(name,_remove)), \
|
||||
RESET(API_0P(M_F(name,_reset))), \
|
||||
PUSH(API_0P(M_F(name,_push))), \
|
||||
GET_MIN(M_F(name,_min)), \
|
||||
GET_MAX(M_F(name,_max)), \
|
||||
M_IF_METHOD(GET_STR, oplist)(GET_STR(API_0P(M_F(name, _get_str))),), \
|
||||
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(API_0P(M_F(name, _parse_str))),), \
|
||||
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),), \
|
||||
M_IF_METHOD(IN_STR, oplist)(IN_STR(API_0P(M_F(name, _in_str))),), \
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(API_0P(M_F(name, _out_serial))),), \
|
||||
M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(API_0P(M_F(name, _in_serial))),), \
|
||||
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \
|
||||
M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/********************************** INTERNAL *********************************/
|
||||
|
||||
@@ -176,11 +216,10 @@ typedef enum {
|
||||
*/
|
||||
#define M_RBTR33_DEF_P3(name, type, oplist, tree_t, node_t, it_t) \
|
||||
M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \
|
||||
M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \
|
||||
M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \
|
||||
M_EMPLACE_QUEUE_DEF(name, tree_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE)
|
||||
M_EMPLACE_QUEUE_DEF(name, tree_t, _emplace, oplist, M_EMPLACE_QUEUE_GENE)
|
||||
|
||||
/* Define the types associated to a R/B Tree */
|
||||
#define M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \
|
||||
@@ -215,33 +254,6 @@ typedef enum {
|
||||
typedef tree_t M_F(name, _ct); \
|
||||
typedef it_t M_F(name, _it_ct); \
|
||||
|
||||
/* Define the mempool encapsulation */
|
||||
#define M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \
|
||||
/* Link with fast memory allocator if requested */ \
|
||||
M_IF_METHOD(MEMPOOL, oplist)( \
|
||||
/* Definition of the memory pool of this kind of node */ \
|
||||
MEMPOOL_DEF(M_F(name, _mempool), node_t) \
|
||||
/* Definition of the global variable used to reference this pool */ \
|
||||
M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \
|
||||
/* Allocator function */ \
|
||||
M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \
|
||||
return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \
|
||||
} \
|
||||
/* Deallocator function */ \
|
||||
M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
|
||||
M_F(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \
|
||||
} \
|
||||
\
|
||||
, /* No mempool allocation */ \
|
||||
/* Classic Allocator function (common case) */ \
|
||||
M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \
|
||||
return M_CALL_NEW(oplist, node_t); \
|
||||
} \
|
||||
/* Classic deallocator function (common case) */ \
|
||||
M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \
|
||||
M_CALL_DEL(oplist, ptr); \
|
||||
} ) \
|
||||
|
||||
/* Define the core functions */
|
||||
#define M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \
|
||||
\
|
||||
@@ -254,8 +266,7 @@ typedef enum {
|
||||
M_RBTR33_CONTRACT(tree); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _reset)(tree_t tree) \
|
||||
M_P(void, name, _reset, tree_t tree) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(tree); \
|
||||
node_t *stack[M_RBTR33_MAX_STACK]; \
|
||||
@@ -289,7 +300,7 @@ typedef enum {
|
||||
M_ASSERT (n == stack[cpt - 1]); \
|
||||
/* Clear the bottom left node */ \
|
||||
M_CALL_CLEAR(oplist, n->data); \
|
||||
M_C3(m_rbtr33_,name,_del) (n); \
|
||||
M_CALL_DEL(oplist, n); \
|
||||
M_ASSERT((stack[cpt-1] = NULL) == NULL); \
|
||||
/* Go up to the parent */ \
|
||||
cpt--; \
|
||||
@@ -299,15 +310,13 @@ typedef enum {
|
||||
tree->size = 0; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _clear)(tree_t tree) \
|
||||
M_P(void, name, _clear, tree_t tree) \
|
||||
{ \
|
||||
/* Nothing more than clean the tree as everything is cleared */ \
|
||||
M_F(name, _reset)(tree); \
|
||||
M_F(name, _reset) M_R(tree); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _push)(tree_t tree, type const data) \
|
||||
M_P(void, name, _push, tree_t tree, type const data) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(tree); \
|
||||
node_t *tab[M_RBTR33_MAX_STACK]; \
|
||||
@@ -316,10 +325,9 @@ typedef enum {
|
||||
node_t *n = tree->node; \
|
||||
/* If there is no root node, create a new node */ \
|
||||
if (n == NULL) { \
|
||||
n = M_C3(m_rbtr33_,name,_new)(); \
|
||||
n = M_CALL_NEW(oplist, node_t); \
|
||||
if (M_UNLIKELY_NOMEM (n == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (node_t)); \
|
||||
return; \
|
||||
M_MEMORY_FULL(node_t, 1); \
|
||||
} \
|
||||
/* Copy the data in the root node */ \
|
||||
M_CALL_INIT_SET(oplist, n->data, data); \
|
||||
@@ -357,10 +365,9 @@ typedef enum {
|
||||
return; \
|
||||
} \
|
||||
/* Create new node to store the data */ \
|
||||
n = M_C3(m_rbtr33_,name,_new)(); \
|
||||
n = M_CALL_NEW(oplist, node_t); \
|
||||
if (M_UNLIKELY_NOMEM (n == NULL) ) { \
|
||||
M_MEMORY_FULL (sizeof (node_t)); \
|
||||
return; \
|
||||
M_MEMORY_FULL (node_t, 1); \
|
||||
} \
|
||||
/* Copy the data and mark the node as red */ \
|
||||
M_CALL_INIT_SET(oplist, n->data, data); \
|
||||
@@ -684,41 +691,37 @@ typedef enum {
|
||||
} \
|
||||
\
|
||||
/* Create a copy of the given node (recursively) */ \
|
||||
M_INLINE node_t * \
|
||||
M_C3(m_rbtr33_,name,_copy_node)(const node_t *o) \
|
||||
M_P(node_t *, name, _i_copy_node, const node_t *o) \
|
||||
{ \
|
||||
if (o == NULL) return NULL; \
|
||||
node_t *n = M_C3(m_rbtr33_,name,_new)(); \
|
||||
if (M_UNLIKELY(o == NULL)) return NULL; \
|
||||
node_t *n = M_CALL_NEW(oplist, node_t); \
|
||||
if (M_UNLIKELY_NOMEM (n == NULL) ) { \
|
||||
M_MEMORY_FULL (sizeof (node_t)); \
|
||||
return NULL; \
|
||||
M_MEMORY_FULL (node_t, 1); \
|
||||
} \
|
||||
M_CALL_INIT_SET(oplist, n->data, o->data); \
|
||||
n->child[0] = M_C3(m_rbtr33_,name,_copy_node)(o->child[0]); \
|
||||
n->child[1] = M_C3(m_rbtr33_,name,_copy_node)(o->child[1]); \
|
||||
n->child[0] = M_F(name,_i_copy_node)M_R(o->child[0]); \
|
||||
n->child[1] = M_F(name,_i_copy_node)M_R(o->child[1]); \
|
||||
M_RBTR33_COPY_COLOR (n, o); \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _init_set)(tree_t tree, const tree_t ref) \
|
||||
M_P(void, name, _init_set, tree_t tree, const tree_t ref) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT (ref); \
|
||||
M_ASSERT (tree != NULL && tree != ref); \
|
||||
tree->size = ref->size; \
|
||||
/* Copy the root node recursively */ \
|
||||
tree->node = M_C3(m_rbtr33_,name,_copy_node)(ref->node); \
|
||||
tree->node = M_F(name,_i_copy_node)M_R(ref->node); \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _set)(tree_t tree, const tree_t ref) \
|
||||
M_P(void, name, _set, tree_t tree, const tree_t ref) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
M_RBTR33_CONTRACT (ref); \
|
||||
if (tree == ref) return; \
|
||||
M_F(name,_clear)(tree); \
|
||||
M_F(name,_init_set)(tree, ref); \
|
||||
M_F(name,_clear)M_R(tree); \
|
||||
M_F(name,_init_set)M_R(tree, ref); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
@@ -728,18 +731,18 @@ typedef enum {
|
||||
M_ASSERT (tree != NULL && tree != ref); \
|
||||
tree->size = ref->size; \
|
||||
tree->node = ref->node; \
|
||||
/* Mark ref as an invalid representation */ \
|
||||
ref->node = NULL; \
|
||||
ref->size = 0; \
|
||||
ref->size = 1; \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
} \
|
||||
\
|
||||
M_INLINE void \
|
||||
M_F(name, _move)(tree_t tree, tree_t ref) \
|
||||
M_P(void, name, _move, tree_t tree, tree_t ref) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
M_RBTR33_CONTRACT (ref); \
|
||||
M_ASSERT (tree != ref); \
|
||||
M_F(name,_clear)(tree); \
|
||||
M_F(name,_clear)M_R(tree); \
|
||||
M_F(name,_init_move)(tree, ref); \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
} \
|
||||
@@ -801,8 +804,7 @@ typedef enum {
|
||||
} \
|
||||
) \
|
||||
\
|
||||
M_INLINE bool \
|
||||
M_F(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \
|
||||
M_P(bool, name, _pop_at, type *data_ptr, tree_t tree, type const key) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
node_t *tab[M_RBTR33_MAX_STACK]; \
|
||||
@@ -905,7 +907,7 @@ typedef enum {
|
||||
M_ASSERT (M_C3(m_rbtr33_,name,_black_p)(s)); \
|
||||
} \
|
||||
M_ASSERT (p != NULL && u == p->child[nbChild]); \
|
||||
/* if both childreen of s are black */ \
|
||||
/* if both children of s are black */ \
|
||||
/* perform recoloring and recur on parent if black */ \
|
||||
if (s != NULL \
|
||||
&& M_C3(m_rbtr33_,name,_black_p)(s->child[0]) \
|
||||
@@ -955,24 +957,22 @@ typedef enum {
|
||||
M_DO_MOVE(oplist, *data_ptr, n->data); \
|
||||
else \
|
||||
M_CALL_CLEAR(oplist, n->data); \
|
||||
M_C3(m_rbtr33_,name,_del) (n); \
|
||||
M_CALL_DEL(oplist, n); \
|
||||
tree->size --; \
|
||||
M_RBTR33_CONTRACT (tree); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
M_INLINE void M_F(name,_remove)(tree_t t, it_t it) \
|
||||
M_P(void, name,_remove, tree_t t, it_t it) \
|
||||
{ \
|
||||
/* Not optimum: another search in the tree is performed */ \
|
||||
type data; \
|
||||
M_CALL_INIT_SET(oplist, data, *M_F(name,_cref)(it)); \
|
||||
type const *data = M_F(name,_cref)(it); \
|
||||
M_F(name,_next)(it); \
|
||||
M_F(name, _pop_at)(NULL, t, data); \
|
||||
/* We have changed the tree: the iterator is partialy invalid */ \
|
||||
M_F(name, _pop_at)M_R(NULL, t, *data); \
|
||||
/* We have changed the tree: the iterator is partially invalid */ \
|
||||
if (!M_F(name, _end_p)(it)) { \
|
||||
M_F(name, _it_from)(it, t, *M_F(name,_cref)(it)); \
|
||||
} \
|
||||
M_CALL_CLEAR(oplist, data); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
@@ -983,7 +983,7 @@ typedef enum {
|
||||
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 \
|
||||
structurally different but functionally equal (you get this by \
|
||||
constructing the tree in a different way). We have to \
|
||||
compare the ordered value within the tree. */ \
|
||||
M_F(name, _it)(it1, t1); \
|
||||
@@ -1022,24 +1022,24 @@ typedef enum {
|
||||
/* Define the I/O functions */
|
||||
#define M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
M_INLINE void M_F(name, _get_str)(m_string_t str, \
|
||||
tree_t const t1, bool append) { \
|
||||
M_P(void, name, _get_str, m_string_t str, tree_t const t1, bool append) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(t1); \
|
||||
M_ASSERT(str != NULL); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
|
||||
(append ? m_string_cat_cstr : m_string_set_cstr) M_R(str, "["); \
|
||||
/* NOTE: The print is really naive, and not really efficient */ \
|
||||
bool commaToPrint = false; \
|
||||
it_t it1; \
|
||||
M_F(name, _it)(it1, t1); \
|
||||
while (!M_F(name, _end_p)(it1)) { \
|
||||
if (commaToPrint) \
|
||||
m_string_push_back (str, M_GET_SEPARATOR oplist); \
|
||||
m_string_push_back M_R(str, M_GET_SEPARATOR oplist); \
|
||||
commaToPrint = true; \
|
||||
type const *ref1 = M_F(name, _cref)(it1); \
|
||||
M_CALL_GET_STR(oplist, str, *ref1, true); \
|
||||
M_F(name, _next)(it1); \
|
||||
} \
|
||||
m_string_push_back (str, ']'); \
|
||||
m_string_push_back M_R(str, ']'); \
|
||||
} \
|
||||
, /* NO GET_STR */ ) \
|
||||
\
|
||||
@@ -1066,65 +1066,56 @@ typedef enum {
|
||||
, /* no out_str */ ) \
|
||||
\
|
||||
M_IF_METHOD(PARSE_STR, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \
|
||||
M_P(bool, name, _parse_str, tree_t rbtree, const char str[], const char **endp) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(rbtree); \
|
||||
M_ASSERT (str != NULL); \
|
||||
M_F(name,_reset)(rbtree); \
|
||||
bool success = false; \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) goto exit; \
|
||||
c = *str++; \
|
||||
if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \
|
||||
if (M_UNLIKELY (c == 0)) goto exit; \
|
||||
M_F(name,_reset)M_R(rbtree); \
|
||||
int c = m_core_str_nospace(&str); \
|
||||
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (M_UNLIKELY (c == ']' || c == 0)) goto exit; \
|
||||
str--; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false || c == 0) goto exit_clear; \
|
||||
M_F(name, _push)(rbtree, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
success = (c == ']'); \
|
||||
exit_clear: \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
c = m_core_str_nospace(&str); \
|
||||
if (b == false || c == 0) { c= 0 ; break; }; \
|
||||
M_F(name, _push)M_R(rbtree, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no parse_str */ ) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
M_INLINE bool \
|
||||
M_F(name, _in_str)(tree_t rbtree, FILE *file) \
|
||||
M_P(bool, name, _in_str, tree_t rbtree, FILE *file) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(rbtree); \
|
||||
M_ASSERT (file != NULL); \
|
||||
M_F(name,_reset)(rbtree); \
|
||||
M_F(name,_reset)M_R(rbtree); \
|
||||
int c = fgetc(file); \
|
||||
if (M_UNLIKELY (c != '[')) return false; \
|
||||
c = fgetc(file); \
|
||||
if (M_UNLIKELY (c == ']')) return true; \
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) break; \
|
||||
M_F(name, _push)(rbtree, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
M_QLET(1, item, type, oplist) { \
|
||||
do { \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
c = m_core_fgetc_nospace(file); \
|
||||
if (b == false || c == EOF) { c = 0; break; } \
|
||||
M_F(name, _push)M_R(rbtree, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no in_str */ ) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
|
||||
M_P(m_serial_return_code_t, name, _out_serial, m_serial_write_t f, tree_t const t1) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(t1); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
@@ -1133,41 +1124,40 @@ typedef enum {
|
||||
M_F(name, _subtype_ct) const *item; \
|
||||
bool first_done = false; \
|
||||
it_t it; \
|
||||
ret = f->m_interface->write_array_start(local, f, t1->size); \
|
||||
ret = f->m_interface->write_array_start M_R(local, f, t1->size); \
|
||||
for (M_F(name, _it)(it, t1) ; \
|
||||
!M_F(name, _end_p)(it); \
|
||||
M_F(name, _next)(it)){ \
|
||||
item = M_F(name, _cref)(it); \
|
||||
if (first_done) \
|
||||
ret |= f->m_interface->write_array_next(local, f); \
|
||||
ret |= f->m_interface->write_array_next M_R(local, f); \
|
||||
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
||||
first_done = true; \
|
||||
} \
|
||||
ret |= f->m_interface->write_array_end(local, f); \
|
||||
ret |= f->m_interface->write_array_end M_R(local, f); \
|
||||
return ret & M_SERIAL_FAIL; \
|
||||
} \
|
||||
, /* no OUT_SERIAL */ ) \
|
||||
\
|
||||
M_IF_METHOD(IN_SERIAL, oplist)( \
|
||||
M_INLINE m_serial_return_code_t \
|
||||
M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \
|
||||
M_P(m_serial_return_code_t, name, _in_serial, tree_t t1, m_serial_read_t f) \
|
||||
{ \
|
||||
M_RBTR33_CONTRACT(t1); \
|
||||
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
||||
m_serial_local_t local; \
|
||||
m_serial_return_code_t ret; \
|
||||
size_t estimated_size = 0; \
|
||||
type key; \
|
||||
M_F(name,_reset)(t1); \
|
||||
M_F(name,_reset)M_R(t1); \
|
||||
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
||||
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
||||
M_CALL_INIT(oplist, key); \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_F(name, _push)(t1, key); \
|
||||
} while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
||||
M_CALL_CLEAR(oplist, key); \
|
||||
M_QLET(1, key, type, oplist) { \
|
||||
do { \
|
||||
ret = M_CALL_IN_SERIAL(oplist, key, f); \
|
||||
if (ret != M_SERIAL_OK_DONE) { break; } \
|
||||
M_F(name, _push)M_R(t1, key); \
|
||||
ret = f->m_interface->read_array_next(local, f); \
|
||||
} while (ret == M_SERIAL_OK_CONTINUE); \
|
||||
} \
|
||||
return ret; \
|
||||
} \
|
||||
, /* no in_serial */ ) \
|
||||
|
||||
+30
-30
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Serial BIN
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -111,9 +111,9 @@ m_ser1al_bin_read_size(FILE *f, size_t *size)
|
||||
|
||||
/* Write the boolean 'data' into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_boolean(m_serial_write_t serial, const bool data)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_boolean, m_serial_write_t serial, const bool data)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
size_t n = fwrite (M_ASSIGN_CAST(const void*, &data), sizeof (bool), 1, f);
|
||||
return n == 1 ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
@@ -121,9 +121,9 @@ m_ser1al_bin_write_boolean(m_serial_write_t serial, const bool data)
|
||||
|
||||
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_integer, m_serial_write_t serial,const long long data, const size_t size_of_type)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
size_t n;
|
||||
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -146,9 +146,9 @@ m_ser1al_bin_write_integer(m_serial_write_t serial,const long long data, const s
|
||||
|
||||
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_float, m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
size_t n;
|
||||
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -168,9 +168,9 @@ m_ser1al_bin_write_float(m_serial_write_t serial, const long double data, const
|
||||
|
||||
/* Write the null-terminated string 'data'into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_string(m_serial_write_t serial, const char data[], size_t length)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_string, m_serial_write_t serial, const char data[], size_t length)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
M_ASSERT_SLOW(length == strlen(data) );
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
M_ASSERT(f != NULL && data != NULL);
|
||||
@@ -188,9 +188,9 @@ m_ser1al_bin_write_string(m_serial_write_t serial, const char data[], size_t len
|
||||
Initialize 'local' so that it can be used to serialize the array
|
||||
(local is an unique serialization object of the array).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_array_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; //Unused
|
||||
if (number_of_elements == (size_t)-1) return M_SERIAL_FAIL_RETRY;
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -200,9 +200,9 @@ m_ser1al_bin_write_array_start(m_serial_local_t local, m_serial_write_t serial,
|
||||
|
||||
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_array_next, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // Unused
|
||||
(void) serial; // Unused
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
@@ -210,9 +210,9 @@ m_ser1al_bin_write_array_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* End the writing of an array into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_array_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // Unused
|
||||
(void) serial; // Unused
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
@@ -220,9 +220,9 @@ m_ser1al_bin_write_array_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_map_value, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
@@ -232,9 +232,9 @@ m_ser1al_bin_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
Initialize 'local' so that it can serial the tuple
|
||||
(local is an unique serialization object of the tuple).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_tuple_start, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
@@ -242,9 +242,9 @@ m_ser1al_bin_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_tuple_id, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
(void) field_name;
|
||||
@@ -255,9 +255,9 @@ m_ser1al_bin_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, con
|
||||
|
||||
/* End the write of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_tuple_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_DONE;
|
||||
@@ -268,9 +268,9 @@ m_ser1al_bin_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise
|
||||
Otherwise, the field 'field_name[index]' will be filled.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_variant_start, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) field_name;
|
||||
(void) max;
|
||||
(void) local;
|
||||
@@ -281,9 +281,9 @@ m_ser1al_bin_write_variant_start(m_serial_local_t local, m_serial_write_t serial
|
||||
|
||||
/* End Writing a variant into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _write_variant_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) serial;
|
||||
return M_SERIAL_OK_DONE;
|
||||
@@ -399,15 +399,15 @@ m_ser1al_bin_read_float(m_serial_read_t serial, long double *r, const size_t siz
|
||||
/* Read from the stream 'serial' a string.
|
||||
Set 's' with the string if succeeds
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_bin_read_string(m_serial_read_t serial, struct string_s *s){
|
||||
M_P(m_serial_return_code_t, m_ser1al_bin, _read_string, m_serial_read_t serial, struct m_string_s *s)
|
||||
{
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
M_ASSERT(f != NULL && s != NULL);
|
||||
// First read the number of non null characters
|
||||
size_t length;
|
||||
if (m_ser1al_bin_read_size(f, &length) != true) return m_core_serial_fail();
|
||||
// Use of internal string interface to dimension the string
|
||||
char *p = m_str1ng_fit2size(s, length + 1);
|
||||
char *p = m_str1ng_fit2size M_R(s, length + 1);
|
||||
m_str1ng_set_size(s, length);
|
||||
// Read the characters excluding the final null one.
|
||||
// NOTE: fread supports length == 0.
|
||||
|
||||
+87
-111
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Serial JSON
|
||||
*
|
||||
* Copyright (c) 2017-2023, Patrick Pelissier
|
||||
* Copyright (c) 2017-2025, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -36,9 +36,9 @@ M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* Write the boolean 'data' into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_boolean(m_serial_write_t serial, const bool data)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_boolean, m_serial_write_t serial, const bool data)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%s", data ? "true" : "false");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
@@ -46,9 +46,9 @@ m_ser1al_json_write_boolean(m_serial_write_t serial, const bool data)
|
||||
|
||||
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_integer, m_serial_write_t serial,const long long data, const size_t size_of_type)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) size_of_type; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%lld", data);
|
||||
@@ -57,9 +57,9 @@ m_ser1al_json_write_integer(m_serial_write_t serial,const long long data, const
|
||||
|
||||
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_float, m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) size_of_type; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "%Lf", data);
|
||||
@@ -68,18 +68,20 @@ m_ser1al_json_write_float(m_serial_write_t serial, const long double data, const
|
||||
|
||||
/* Write the null-terminated string 'data'into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t length)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_string, m_serial_write_t serial, const char data[], size_t length)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
M_ASSERT(length == (m_str1ng_size_t) length);
|
||||
M_ASSERT_SLOW(length == strlen(data) );
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
M_ASSERT(f != NULL && data != NULL);
|
||||
/* HACK: Build dummy string to reuse m_string_out_str */
|
||||
m_string_t v2;
|
||||
uintptr_t ptr = (uintptr_t) data;
|
||||
v2->u.heap.size = length;
|
||||
v2->u.heap.alloc = length + 1;
|
||||
v2->ptr = (char*)ptr;
|
||||
v2->u.heap.size = (m_str1ng_size_t) length;
|
||||
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-2] = 1;
|
||||
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-1] = 31;
|
||||
v2->u.heap.ptr = (char*)ptr;
|
||||
m_string_out_str(f, v2);
|
||||
return M_UNLIKELY(ferror(f)) ? m_core_serial_fail() : M_SERIAL_OK_DONE;
|
||||
}
|
||||
@@ -90,9 +92,9 @@ m_ser1al_json_write_string(m_serial_write_t serial, const char data[], size_t le
|
||||
Initialize 'local' so that it can be used to serialize the array
|
||||
(local is an unique serialization object of the array).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_array_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) number_of_elements; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -102,9 +104,9 @@ m_ser1al_json_write_array_start(m_serial_local_t local, m_serial_write_t serial,
|
||||
|
||||
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_array_next, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, ",");
|
||||
@@ -113,9 +115,9 @@ m_ser1al_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* End the writing of an array into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_array_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "]");
|
||||
@@ -128,9 +130,9 @@ m_ser1al_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
Initialize 'local' so that it can be used to serialize the map
|
||||
(local is an unique serialization object of the map).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) number_of_elements; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -140,9 +142,9 @@ m_ser1al_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, c
|
||||
|
||||
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_value, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, ":");
|
||||
@@ -151,9 +153,9 @@ m_ser1al_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* Write a map separator between elements of a map into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_next, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, ",");
|
||||
@@ -162,9 +164,9 @@ m_ser1al_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* End the writing of a map into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_map_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "}");
|
||||
@@ -175,9 +177,9 @@ m_ser1al_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
Initialize 'local' so that it can serial the tuple
|
||||
(local is an unique serialization object of the tuple).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_tuple_start, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "{");
|
||||
@@ -186,9 +188,9 @@ m_ser1al_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
|
||||
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_tuple_id, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) max; // Ignored
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -198,9 +200,9 @@ m_ser1al_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, co
|
||||
|
||||
/* End the write of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_tuple_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "}");
|
||||
@@ -212,9 +214,9 @@ m_ser1al_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise
|
||||
Otherwise, the field 'field_name[index]' will be filled.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_variant_start, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
(void) max; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
@@ -231,9 +233,9 @@ m_ser1al_json_write_variant_start(m_serial_local_t local, m_serial_write_t seria
|
||||
|
||||
/* End Writing a variant into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _write_variant_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
M_UNUSED_CONTEXT();
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE *)serial->data[0].p;
|
||||
int n = fprintf(f, "}");
|
||||
@@ -287,25 +289,13 @@ typedef m_serial_write_t m_serial_json_write_t;
|
||||
/*************************** FILE / READ / JSON ********************************/
|
||||
/********************************************************************************/
|
||||
|
||||
/* Helper function to skip a space */
|
||||
M_INLINE int
|
||||
m_ser1al_json_read_skip (FILE *f)
|
||||
{
|
||||
int c;
|
||||
do {
|
||||
// c is an int, and is the value of the character read as unsigned char
|
||||
c = fgetc(f);
|
||||
} while (c != EOF && isspace(c));
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Read from the stream 'serial' a boolean.
|
||||
Set '*b' with the boolean value if succeeds
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_read_boolean(m_serial_read_t serial, bool *b){
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int c = m_ser1al_json_read_skip(f);
|
||||
int c = m_core_fgetc_nospace(f);
|
||||
if (c == 't') {
|
||||
*b = true;
|
||||
c = fgetc(f);
|
||||
@@ -354,12 +344,12 @@ m_ser1al_json_read_float(m_serial_read_t serial, long double *r, const size_t si
|
||||
/* Read from the stream 'serial' a string.
|
||||
Set 's' with the string if succeeds
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_json_read_string(m_serial_read_t serial, struct m_string_s *s){
|
||||
M_P(m_serial_return_code_t, m_ser1al_json, _read_string, m_serial_read_t serial, struct m_string_s *s)
|
||||
{
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int n = m_core_fscanf(f, " "); // Skip any leading spaces.
|
||||
(void) n; // Unused return value.
|
||||
return m_string_in_str(s, f) ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
return m_string_in_str M_R(s, f) ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
}
|
||||
|
||||
/* Start reading from the stream 'serial' an array.
|
||||
@@ -473,7 +463,7 @@ m_ser1al_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, cons
|
||||
{
|
||||
(void) local; // argument not used
|
||||
FILE *f = (FILE*) serial->data[0].p;
|
||||
int c = m_ser1al_json_read_skip(f);
|
||||
int c = m_core_fgetc_nospace(f);
|
||||
if (c == EOF) return m_core_serial_fail();
|
||||
if (c == '}') return M_SERIAL_OK_DONE;
|
||||
if (c == ',') {
|
||||
@@ -597,51 +587,50 @@ typedef m_serial_read_t m_serial_json_read_t;
|
||||
|
||||
/* Write the boolean 'data' into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_boolean(m_serial_write_t serial, const bool data)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_boolean, m_serial_write_t serial, const bool data)
|
||||
{
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
int n = m_string_cat_printf(f, "%s", data ? "true" : "false");
|
||||
//TODO: Simplify code.
|
||||
int n = m_string_cat_printf M_R(f, "%s", data ? "true" : "false");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
}
|
||||
|
||||
/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_integer, m_serial_write_t serial, const long long data, const size_t size_of_type)
|
||||
{
|
||||
(void) size_of_type; // Ignored
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
int n = m_string_cat_printf(f, "%lld", data);
|
||||
int n = m_string_cat_printf M_R(f, "%lld", data);
|
||||
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
}
|
||||
|
||||
/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_float, m_serial_write_t serial, const long double data, const size_t size_of_type)
|
||||
{
|
||||
(void) size_of_type; // Ignored
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
int n = m_string_cat_printf(f, "%Lf", data);
|
||||
int n = m_string_cat_printf M_R(f, "%Lf", data);
|
||||
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
}
|
||||
|
||||
/* Write the null-terminated string 'data'into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_string(m_serial_write_t serial, const char data[], size_t length)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_string, m_serial_write_t serial, const char data[], size_t length)
|
||||
{
|
||||
M_ASSERT(length == (m_str1ng_size_t) length);
|
||||
M_ASSERT_SLOW(length == strlen(data) );
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
M_ASSERT(f != NULL && data != NULL);
|
||||
/* HACK: Build dummy string to reuse m_string_get_str */
|
||||
m_string_t v2;
|
||||
uintptr_t ptr = (uintptr_t) data;
|
||||
v2->u.heap.size = length;
|
||||
v2->u.heap.alloc = length + 1;
|
||||
v2->ptr = (char*)ptr;
|
||||
m_string_get_str(f, v2, true);
|
||||
v2->u.heap.size = (m_str1ng_size_t) length;
|
||||
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-2] = 1;
|
||||
v2->u.heap.alloc[sizeof(m_str1ng_size_t)-1] = 31;
|
||||
v2->u.heap.ptr = (char*)ptr;
|
||||
m_string_get_str M_R(f, v2, true);
|
||||
return M_SERIAL_OK_DONE;
|
||||
}
|
||||
|
||||
@@ -651,35 +640,32 @@ m_ser1al_str_json_write_string(m_serial_write_t serial, const char data[], size_
|
||||
Initialize 'local' so that it can be used to serialize the array
|
||||
(local is an unique serialization object of the array).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_array_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) number_of_elements; // Ignored
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, '[');
|
||||
m_string_push_back M_R(f, '[');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Write an array separator between elements of an array into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_array_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_array_next, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, ',');
|
||||
m_string_push_back M_R(f, ',');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* End the writing of an array into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_array_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_array_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, ']');
|
||||
m_string_push_back M_R(f, ']');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -689,46 +675,42 @@ m_ser1al_str_json_write_array_end(m_serial_local_t local, m_serial_write_t seria
|
||||
Initialize 'local' so that it can be used to serialize the map
|
||||
(local is an unique serialization object of the map).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_start, m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) number_of_elements; // Ignored
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, '{');
|
||||
m_string_push_back M_R(f, '{');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_map_value(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_value, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, ':');
|
||||
m_string_push_back M_R(f, ':');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Write a map separator between elements of a map into the serial stream 'serial' if needed.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_map_next(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_next, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, ',');
|
||||
m_string_push_back M_R(f, ',');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* End the writing of a map into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_map_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, '}');
|
||||
m_string_push_back M_R(f, '}');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -736,35 +718,32 @@ m_ser1al_str_json_write_map_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
Initialize 'local' so that it can serial the tuple
|
||||
(local is an unique serialization object of the tuple).
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_tuple_start, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, '{');
|
||||
m_string_push_back M_R(f, '{');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_tuple_id, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) max; // Ignored
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
int n = m_string_cat_printf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]);
|
||||
int n = m_string_cat_printf M_R(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]);
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
|
||||
}
|
||||
|
||||
/* End the write of a tuple into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_tuple_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, '}');
|
||||
m_string_push_back M_R(f, '}');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -773,8 +752,7 @@ m_ser1al_str_json_write_tuple_end(m_serial_local_t local, m_serial_write_t seria
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise
|
||||
Otherwise, the field 'field_name[index]' will be filled.
|
||||
Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_variant_start, m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
(void) max; // argument not used
|
||||
@@ -782,22 +760,21 @@ m_ser1al_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t s
|
||||
int n;
|
||||
if (index >= 0) {
|
||||
M_ASSERT (index < max);
|
||||
n = m_string_cat_printf(f, "{\"%s\":", field_name[index]);
|
||||
n = m_string_cat_printf M_R(f, "{\"%s\":", field_name[index]);
|
||||
return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail();
|
||||
} else {
|
||||
n = m_string_cat_printf(f, "{}");
|
||||
n = m_string_cat_printf M_R(f, "{}");
|
||||
return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
}
|
||||
}
|
||||
|
||||
/* End Writing a variant into the serial stream 'serial'.
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial)
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _write_variant_end, m_serial_local_t local, m_serial_write_t serial)
|
||||
{
|
||||
(void) local; // argument not used
|
||||
struct m_string_s *f = (struct m_string_s *)serial->data[0].p;
|
||||
m_string_push_back(f, '}');
|
||||
m_string_push_back M_R(f, '}');
|
||||
return M_SERIAL_OK_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -860,9 +837,8 @@ m_ser1al_str_json_getc(const char **p)
|
||||
M_INLINE void
|
||||
m_ser1al_str_json_skip (const char **p)
|
||||
{
|
||||
while (isspace (**p)) {
|
||||
(*p)++;
|
||||
}
|
||||
(void) m_core_str_nospace(p);
|
||||
(*p)--;
|
||||
}
|
||||
|
||||
/* Helper function to read a string with characters not present in reject */
|
||||
@@ -942,12 +918,12 @@ m_ser1al_str_json_read_float(m_serial_read_t serial, long double *r, const size_
|
||||
/* Read from the stream 'serial' a string.
|
||||
Set 's' with the string if succeeds
|
||||
Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */
|
||||
M_INLINE m_serial_return_code_t
|
||||
m_ser1al_str_json_read_string(m_serial_read_t serial, struct m_string_s *s){
|
||||
M_P(m_serial_return_code_t, m_ser1al_str_json, _read_string, m_serial_read_t serial, struct m_string_s *s)
|
||||
{
|
||||
const char **f = &serial->data[0].cstr;
|
||||
m_ser1al_str_json_skip(f);
|
||||
const char *e;
|
||||
bool b = m_string_parse_str(s, *f, &e);
|
||||
bool b = m_string_parse_str M_R(s, *f, &e);
|
||||
serial->data[0].cstr = e;
|
||||
return b ? M_SERIAL_OK_DONE : m_core_serial_fail();
|
||||
}
|
||||
|
||||
+1413
File diff suppressed because it is too large.
Load diff
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user