├── fuzz ├── o │ └── .gitignore ├── t │ ├── 00.t │ ├── 01.t │ ├── 20.t │ ├── 10.t │ └── 11.t ├── Makefile ├── fuzz.dict └── fuzz.cpp ├── doc ├── mapping0.g ├── value.dot ├── mapping1.g ├── mapping2.g ├── memtrack.png ├── rnd_assign.png ├── rnd_insert.png ├── rnd_lookup.png ├── rnd_remove.png ├── seq_assign.png ├── seq_insert.png ├── seq_lookup.png ├── seq_remove.png ├── shortseq.png ├── demo.g ├── node.dot ├── nodestru.dot ├── assign0.dot ├── remove0.dot ├── Makefile ├── value.svg ├── perf-lnx-1.csv ├── perf-lnx-2.csv ├── perf-lnx-3.csv ├── perf-win-1.csv ├── perf-win-2.csv ├── perf-win-3.csv ├── assign1.dot ├── assign2.dot ├── lookup.dot ├── remove1.dot ├── remove2.dot ├── graph.c ├── nodestru.svg ├── perf-analysis.ipynb ├── mapping0.svg ├── node.svg ├── mapping1.svg ├── mapping2.svg ├── assign0.svg ├── remove0.svg ├── remove2.svg ├── assign2.svg ├── demo.svg └── remove1.svg ├── .gitignore ├── .github └── workflows │ ├── fuzz.yml │ └── test.yml ├── perf ├── Makefile ├── run-perf-tests.py ├── wrap.cpp └── bench.cpp ├── test └── Makefile ├── License.txt ├── ivmap.h ├── iset.h └── tlib ├── testsuite.h └── testsuite.c /fuzz/o/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /doc/mapping0.g: -------------------------------------------------------------------------------- 1 | 0xA0000056 = 0x56 2 | d 3 | -------------------------------------------------------------------------------- /doc/value.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "y" 3 | } 4 | -------------------------------------------------------------------------------- /fuzz/t/00.t: -------------------------------------------------------------------------------- 1 | A0000056=56 2 | A0000056 3 | A0000056 l 4 | -------------------------------------------------------------------------------- /doc/mapping1.g: -------------------------------------------------------------------------------- 1 | 0xA0000056 = 0x56 2 | 0xA0000057 = 0x57 3 | d 4 | -------------------------------------------------------------------------------- /fuzz/t/01.t: -------------------------------------------------------------------------------- 1 | A0000056=56 2 | A0000056r 3 | A0000056 4 | A0000056 l 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.obj 3 | *.pdb 4 | *.ilk 5 | *.exe 6 | *.o 7 | *.out 8 | -------------------------------------------------------------------------------- /doc/mapping2.g: -------------------------------------------------------------------------------- 1 | 0xA0000056 = 0x56 2 | 0xA0000057 = 0x57 3 | 0xA0008009 = 0x8009 4 | d 5 | -------------------------------------------------------------------------------- /doc/memtrack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/memtrack.png -------------------------------------------------------------------------------- /doc/rnd_assign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/rnd_assign.png -------------------------------------------------------------------------------- /doc/rnd_insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/rnd_insert.png -------------------------------------------------------------------------------- /doc/rnd_lookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/rnd_lookup.png -------------------------------------------------------------------------------- /doc/rnd_remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/rnd_remove.png -------------------------------------------------------------------------------- /doc/seq_assign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/seq_assign.png -------------------------------------------------------------------------------- /doc/seq_insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/seq_insert.png -------------------------------------------------------------------------------- /doc/seq_lookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/seq_lookup.png -------------------------------------------------------------------------------- /doc/seq_remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/seq_remove.png -------------------------------------------------------------------------------- /doc/shortseq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/imap/HEAD/doc/shortseq.png -------------------------------------------------------------------------------- /fuzz/t/20.t: -------------------------------------------------------------------------------- 1 | A0000056=56 2 | A0000057=57 3 | A0008009=8009 4 | A0008059=8059 5 | A0008069=8069 6 | 90008059 l 7 | B0008059 l 8 | -------------------------------------------------------------------------------- /doc/demo.g: -------------------------------------------------------------------------------- 1 | 0xA0000056 = 0x56 2 | 0xA0000057 = 0x57 3 | 0xA0008009 = 0x8009 4 | 0xA0008059 = 0x8059 5 | 0xA0008069 = 0x8069 6 | d 7 | -------------------------------------------------------------------------------- /fuzz/t/10.t: -------------------------------------------------------------------------------- 1 | A0000056=56 2 | A0000057=57 3 | A0008009=8009 4 | A0008059=8059 5 | A0008069=8069 6 | A0000056 7 | A0000057 8 | A0008009 9 | A0008059 10 | A0008069 11 | A0000056 l 12 | A0000057 l 13 | A0008009 l 14 | A0008059 l 15 | A0008069 l 16 | -------------------------------------------------------------------------------- /fuzz/Makefile: -------------------------------------------------------------------------------- 1 | fuzz: bin afl ; 2 | 3 | ifeq ($(OS),Windows_NT) 4 | 5 | else 6 | 7 | FUZZ=./fuzz.out 8 | bin: fuzz.out 9 | fuzz.out: ../imap.h fuzz.cpp 10 | afl-clang++ -I.. -Wall -Wstrict-aliasing=1 -O3 fuzz.cpp -lstdc++ -o $@ 11 | 12 | endif 13 | 14 | afl: bin 15 | afl-fuzz -i t -o o -x fuzz.dict $(FUZZ) 16 | -------------------------------------------------------------------------------- /fuzz/t/11.t: -------------------------------------------------------------------------------- 1 | A0000056=56 2 | A0000057=57 3 | A0008009=8009 4 | A0008059=8059 5 | A0008069=8069 6 | A0000056r 7 | A0000057r 8 | A0008009r 9 | A0008059r 10 | A0008069r 11 | A0000056 12 | A0000057 13 | A0008009 14 | A0008059 15 | A0008069 16 | A0000056 l 17 | A0000057 l 18 | A0008009 l 19 | A0008059 l 20 | A0008069 l 21 | -------------------------------------------------------------------------------- /fuzz/fuzz.dict: -------------------------------------------------------------------------------- 1 | "=" 2 | "r" 3 | "l" 4 | "0" 5 | "1" 6 | "2" 7 | "3" 8 | "4" 9 | "5" 10 | "6" 11 | "7" 12 | "8" 13 | "9" 14 | "A" 15 | "B" 16 | "C" 17 | "D" 18 | "E" 19 | "F" 20 | "0000" 21 | "1000" 22 | "2000" 23 | "3000" 24 | "4000" 25 | "5000" 26 | "6000" 27 | "7000" 28 | "8000" 29 | "9000" 30 | "A000" 31 | "B000" 32 | "C000" 33 | "D000" 34 | "E000" 35 | "F000" 36 | -------------------------------------------------------------------------------- /doc/node.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "node" [shape=plaintext label=< 3 | 4 | 5 | 6 |
hF...h1h0 / pos
0123456789ABCDEF
7 | >] 8 | } 9 | -------------------------------------------------------------------------------- /doc/nodestru.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "node" [shape=plaintext label=< 3 | 4 | 5 | 6 | 7 | 8 |
slot0pos
slot1h1
......
slotFhF
9 | >] 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: fuzz 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'fuzz*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | fuzz: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | - name: Install prerequisites 16 | run: | 17 | sudo apt install afl++ 18 | - name: Fuzz testing 19 | run: | 20 | sudo sh -c "echo core >/proc/sys/kernel/core_pattern" 21 | timeout 10m make -C fuzz || [ $? == 124 ] 22 | -------------------------------------------------------------------------------- /perf/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | 3 | bench: bench.exe 4 | .\bench.exe $(BENCH_CMDLINE) 5 | bench.exe: ../imap.h bench.cpp wrap.cpp 6 | cl -I.. -DIMAP_USE_SIMD -D_CRT_SECURE_NO_WARNINGS -W3 -GS- -sdl- -O2 -Oi -MT -GL- bench.cpp wrap.cpp ../tlib/testsuite.c -Fe$@ 7 | 8 | else 9 | 10 | bench: bench.out 11 | ./bench.out $(BENCH_CMDLINE) 12 | bench.out: ../imap.h bench.cpp wrap.cpp 13 | g++ -I.. -DIMAP_USE_SIMD -mavx2 -Wall -Wstrict-aliasing=1 -O3 -flto=none -std=c++17 -x c++ bench.cpp wrap.cpp -x c ../tlib/testsuite.c -o $@ 14 | 15 | endif 16 | -------------------------------------------------------------------------------- /doc/assign0.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"c0":n 5 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 6 | "40":"6":s->"56":n 7 | "40":"7":s->"57":n [style=dashed penwidth=2 color=red] 8 | "57" [style=dashed penwidth=2 color=red] 9 | "c0" [shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 10 | "c0":"9":s->"8009":n 11 | } 12 | -------------------------------------------------------------------------------- /doc/remove0.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"c0":n 5 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 6 | "40":"6":s->"56":n 7 | "40":"7":s->"57":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 8 | "57" [style=dashed color=gray] 9 | "c0" [shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 10 | "c0":"9":s->"8009":n 11 | } 12 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | graph: bin svg ; 2 | 3 | ifeq ($(OS),Windows_NT) 4 | 5 | GRAPH=.\graph.exe 6 | bin: graph.exe 7 | graph.exe: ../imap.h graph.c 8 | cl -I.. -D_CRT_SECURE_NO_WARNINGS -W3 -O2 -std:c11 -permissive- graph.c /Fe$@ 9 | 10 | else 11 | 12 | GRAPH=./graph.out 13 | bin: graph.out 14 | graph.out: ../imap.h graph.c 15 | gcc -I.. -Wall -Wstrict-aliasing=1 -O3 graph.c -o $@ 16 | 17 | endif 18 | 19 | svg: demo.svg node.svg nodestru.svg value.svg mapping0.svg mapping1.svg mapping2.svg lookup.svg assign0.svg assign1.svg assign2.svg remove0.svg remove1.svg remove2.svg 20 | 21 | %.svg: %.g 22 | $(GRAPH) < $< | dot -Tsvg -Granksep=1.0 -Gnodesep=0.5 -Nfontname=monospace -Elabelfontname=monospace -o $@ 23 | %.svg: %.dot 24 | dot -Tsvg -Granksep=1.0 -Gnodesep=0.5 -Nfontname=monospace -Elabelfontname=monospace $< -o $@ 25 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | 3 | test: test.exe 4 | .\test.exe 5 | testcxx: testcxx.exe 6 | .\testcxx.exe 7 | test.exe: ../imap.h test.c 8 | cl -I.. -D_CRT_SECURE_NO_WARNINGS -W3 -WX -O2 -std:c11 -permissive- -Tc test.c ../tlib/testsuite.c -Fe$@ 9 | testcxx.exe: ../imap.h test.c 10 | cl -I.. -D_CRT_SECURE_NO_WARNINGS -W3 -WX -O2 -std:c++14 -permissive- -Tp test.c ../tlib/testsuite.c -Fe$@ 11 | 12 | else 13 | 14 | test: test.out 15 | ./test.out 16 | testcxx: testcxx.out 17 | ./testcxx.out 18 | test.out: ../imap.h test.c 19 | gcc -I.. -Wall -Wstrict-aliasing=1 -Werror -O3 -x c test.c -x c ../tlib/testsuite.c -o $@ 20 | testcxx.out: ../imap.h test.c 21 | g++ -I.. -Wall -Wstrict-aliasing=1 -Werror -O3 -x c++ test.c -x c ../tlib/testsuite.c -o $@ 22 | 23 | valgrind: test.out 24 | valgrind --leak-check=yes --error-exitcode=1 ./test.out 25 | 26 | endif 27 | -------------------------------------------------------------------------------- /doc/value.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | y 15 | 16 | y 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /doc/perf-lnx-1.csv: -------------------------------------------------------------------------------- 1 | imap_seq_insert_test,,0.18 2 | imbv_seq_insert_test,,0.20 3 | stdu_seq_insert_test,,0.31 4 | stdm_seq_insert_test,,4.31 5 | imap_seq_assign_test,,0.16 6 | imbv_seq_assign_test,,0.16 7 | stdu_seq_assign_test,,0.03 8 | stdm_seq_assign_test,,2.54 9 | imap_seq_lookup_test,,0.12 10 | imbv_seq_lookup_test,,0.12 11 | stdu_seq_lookup_test,,0.09 12 | stdm_seq_lookup_test,,2.49 13 | imap_seq_remove_test,,0.20 14 | imbv_seq_remove_test,,0.22 15 | stdu_seq_remove_test,,0.09 16 | stdm_seq_remove_test,,2.04 17 | imap_rnd_insert_test,,0.99 18 | stdu_rnd_insert_test,,0.48 19 | stdm_rnd_insert_test,,13.03 20 | imap_rnd_assign_test,,0.90 21 | stdu_rnd_assign_test,,0.14 22 | stdm_rnd_assign_test,,15.05 23 | imap_rnd_lookup_test,,0.70 24 | stdu_rnd_lookup_test,,0.10 25 | stdm_rnd_lookup_test,,14.91 26 | imap_rnd_remove_test,,1.29 27 | stdu_rnd_remove_test,,0.62 28 | stdm_rnd_remove_test,,13.94 29 | imap_shortseq_test,,0.65 30 | stdu_shortseq_test,,0.25 31 | stdm_shortseq_test,,0.51 32 | imap_memtrack_test,42666880/67108864,0.30 33 | imbv_memtrack_test,122666880/134217728,0.18 34 | stdu_memtrack_test,336941512,0.29 35 | stdm_memtrack_test,480000000,4.35 36 | -------------------------------------------------------------------------------- /doc/perf-lnx-2.csv: -------------------------------------------------------------------------------- 1 | imap_seq_insert_test,,0.17 2 | imbv_seq_insert_test,,0.20 3 | stdu_seq_insert_test,,0.28 4 | stdm_seq_insert_test,,4.36 5 | imap_seq_assign_test,,0.16 6 | imbv_seq_assign_test,,0.17 7 | stdu_seq_assign_test,,0.03 8 | stdm_seq_assign_test,,2.46 9 | imap_seq_lookup_test,,0.12 10 | imbv_seq_lookup_test,,0.12 11 | stdu_seq_lookup_test,,0.09 12 | stdm_seq_lookup_test,,2.55 13 | imap_seq_remove_test,,0.21 14 | imbv_seq_remove_test,,0.22 15 | stdu_seq_remove_test,,0.10 16 | stdm_seq_remove_test,,2.05 17 | imap_rnd_insert_test,,1.00 18 | stdu_rnd_insert_test,,0.48 19 | stdm_rnd_insert_test,,13.11 20 | imap_rnd_assign_test,,0.92 21 | stdu_rnd_assign_test,,0.14 22 | stdm_rnd_assign_test,,15.11 23 | imap_rnd_lookup_test,,0.71 24 | stdu_rnd_lookup_test,,0.10 25 | stdm_rnd_lookup_test,,14.92 26 | imap_rnd_remove_test,,1.29 27 | stdu_rnd_remove_test,,0.62 28 | stdm_rnd_remove_test,,13.89 29 | imap_shortseq_test,,0.65 30 | stdu_shortseq_test,,0.24 31 | stdm_shortseq_test,,0.51 32 | imap_memtrack_test,42666880/67108864,0.30 33 | imbv_memtrack_test,122666880/134217728,0.18 34 | stdu_memtrack_test,336941512,0.29 35 | stdm_memtrack_test,480000000,4.25 36 | -------------------------------------------------------------------------------- /doc/perf-lnx-3.csv: -------------------------------------------------------------------------------- 1 | imap_seq_insert_test,,0.17 2 | imbv_seq_insert_test,,0.20 3 | stdu_seq_insert_test,,0.28 4 | stdm_seq_insert_test,,4.32 5 | imap_seq_assign_test,,0.16 6 | imbv_seq_assign_test,,0.17 7 | stdu_seq_assign_test,,0.03 8 | stdm_seq_assign_test,,2.47 9 | imap_seq_lookup_test,,0.12 10 | imbv_seq_lookup_test,,0.12 11 | stdu_seq_lookup_test,,0.09 12 | stdm_seq_lookup_test,,2.56 13 | imap_seq_remove_test,,0.21 14 | imbv_seq_remove_test,,0.22 15 | stdu_seq_remove_test,,0.10 16 | stdm_seq_remove_test,,2.05 17 | imap_rnd_insert_test,,1.00 18 | stdu_rnd_insert_test,,0.48 19 | stdm_rnd_insert_test,,13.18 20 | imap_rnd_assign_test,,0.92 21 | stdu_rnd_assign_test,,0.14 22 | stdm_rnd_assign_test,,15.12 23 | imap_rnd_lookup_test,,0.71 24 | stdu_rnd_lookup_test,,0.10 25 | stdm_rnd_lookup_test,,14.92 26 | imap_rnd_remove_test,,1.28 27 | stdu_rnd_remove_test,,0.63 28 | stdm_rnd_remove_test,,13.91 29 | imap_shortseq_test,,0.64 30 | stdu_shortseq_test,,0.25 31 | stdm_shortseq_test,,0.51 32 | imap_memtrack_test,42666880/67108864,0.30 33 | imbv_memtrack_test,122666880/134217728,0.18 34 | stdu_memtrack_test,336941512,0.29 35 | stdm_memtrack_test,480000000,4.23 36 | -------------------------------------------------------------------------------- /doc/perf-win-1.csv: -------------------------------------------------------------------------------- 1 | imap_seq_insert_test,,0.21 2 | imbv_seq_insert_test,,0.23 3 | stdu_seq_insert_test,,3.21 4 | stdm_seq_insert_test,,1.65 5 | imap_seq_assign_test,,0.18 6 | imbv_seq_assign_test,,0.20 7 | stdu_seq_assign_test,,0.40 8 | stdm_seq_assign_test,,0.81 9 | imap_seq_lookup_test,,0.17 10 | imbv_seq_lookup_test,,0.15 11 | stdu_seq_lookup_test,,0.34 12 | stdm_seq_lookup_test,,0.83 13 | imap_seq_remove_test,,0.25 14 | imbv_seq_remove_test,,0.28 15 | stdu_seq_remove_test,,1.23 16 | stdm_seq_remove_test,,1.34 17 | imap_rnd_insert_test,,1.07 18 | stdu_rnd_insert_test,,1.84 19 | stdm_rnd_insert_test,,14.17 20 | imap_rnd_assign_test,,1.00 21 | stdu_rnd_assign_test,,0.50 22 | stdm_rnd_assign_test,,14.25 23 | imap_rnd_lookup_test,,0.71 24 | stdu_rnd_lookup_test,,0.40 25 | stdm_rnd_lookup_test,,14.08 26 | imap_rnd_remove_test,,1.04 27 | stdu_rnd_remove_test,,1.18 28 | stdm_rnd_remove_test,,18.59 29 | imap_shortseq_test,,0.40 30 | stdu_shortseq_test,,0.90 31 | stdm_shortseq_test,,1.25 32 | imap_memtrack_test,42666880/67108864,0.21 33 | imbv_memtrack_test,122666880/134217728,0.25 34 | stdu_memtrack_test,588435488,4.15 35 | stdm_memtrack_test,480000048,2.14 36 | -------------------------------------------------------------------------------- /doc/perf-win-2.csv: -------------------------------------------------------------------------------- 1 | imap_seq_insert_test,,0.20 2 | imbv_seq_insert_test,,0.25 3 | stdu_seq_insert_test,,3.20 4 | stdm_seq_insert_test,,1.67 5 | imap_seq_assign_test,,0.18 6 | imbv_seq_assign_test,,0.20 7 | stdu_seq_assign_test,,0.40 8 | stdm_seq_assign_test,,0.86 9 | imap_seq_lookup_test,,0.15 10 | imbv_seq_lookup_test,,0.17 11 | stdu_seq_lookup_test,,0.32 12 | stdm_seq_lookup_test,,0.81 13 | imap_seq_remove_test,,0.23 14 | imbv_seq_remove_test,,0.28 15 | stdu_seq_remove_test,,1.20 16 | stdm_seq_remove_test,,1.31 17 | imap_rnd_insert_test,,1.03 18 | stdu_rnd_insert_test,,1.81 19 | stdm_rnd_insert_test,,14.15 20 | imap_rnd_assign_test,,0.98 21 | stdu_rnd_assign_test,,0.48 22 | stdm_rnd_assign_test,,14.18 23 | imap_rnd_lookup_test,,0.70 24 | stdu_rnd_lookup_test,,0.40 25 | stdm_rnd_lookup_test,,14.23 26 | imap_rnd_remove_test,,1.07 27 | stdu_rnd_remove_test,,1.18 28 | stdm_rnd_remove_test,,18.56 29 | imap_shortseq_test,,0.40 30 | stdu_shortseq_test,,0.90 31 | stdm_shortseq_test,,1.25 32 | imap_memtrack_test,42666880/67108864,0.21 33 | imbv_memtrack_test,122666880/134217728,0.23 34 | stdu_memtrack_test,588435488,4.17 35 | stdm_memtrack_test,480000048,2.17 36 | -------------------------------------------------------------------------------- /doc/perf-win-3.csv: -------------------------------------------------------------------------------- 1 | imap_seq_insert_test,,0.23 2 | imbv_seq_insert_test,,0.25 3 | stdu_seq_insert_test,,3.18 4 | stdm_seq_insert_test,,1.62 5 | imap_seq_assign_test,,0.20 6 | imbv_seq_assign_test,,0.21 7 | stdu_seq_assign_test,,0.40 8 | stdm_seq_assign_test,,0.81 9 | imap_seq_lookup_test,,0.17 10 | imbv_seq_lookup_test,,0.15 11 | stdu_seq_lookup_test,,0.34 12 | stdm_seq_lookup_test,,0.81 13 | imap_seq_remove_test,,0.25 14 | imbv_seq_remove_test,,0.26 15 | stdu_seq_remove_test,,1.20 16 | stdm_seq_remove_test,,1.32 17 | imap_rnd_insert_test,,1.03 18 | stdu_rnd_insert_test,,1.81 19 | stdm_rnd_insert_test,,14.10 20 | imap_rnd_assign_test,,1.00 21 | stdu_rnd_assign_test,,0.48 22 | stdm_rnd_assign_test,,14.23 23 | imap_rnd_lookup_test,,0.71 24 | stdu_rnd_lookup_test,,0.40 25 | stdm_rnd_lookup_test,,14.20 26 | imap_rnd_remove_test,,1.06 27 | stdu_rnd_remove_test,,1.18 28 | stdm_rnd_remove_test,,18.63 29 | imap_shortseq_test,,0.40 30 | stdu_shortseq_test,,0.90 31 | stdm_shortseq_test,,1.29 32 | imap_memtrack_test,42666880/67108864,0.23 33 | imbv_memtrack_test,122666880/134217728,0.25 34 | stdu_memtrack_test,588435488,4.18 35 | stdm_memtrack_test,480000048,2.14 36 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Bill Zissimopoulos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc/assign1.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"100":n 5 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 6 | "40":"6":s->"56":n 7 | "40":"7":s->"57":n 8 | "100" [shape=record label="{00000000a0008000 / 1|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 9 | "100":"0":s->"c0":n 10 | "100":"5":s->"140":n 11 | "100":"6":s->"180":n [style=dashed penwidth=2 color=red] 12 | "c0" [shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 13 | "c0":"9":s->"8009":n 14 | "140" [shape=record label="{00000000a0008050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 15 | "140":"9":s->"8059":n 16 | "180" [style=dashed penwidth=2 color=red shape=record label="{00000000a0008060 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 17 | "180":"9":s->"8069":n [style=dashed penwidth=2 color=red] 18 | "8069" [style=dashed penwidth=2 color=red] 19 | } 20 | -------------------------------------------------------------------------------- /doc/assign2.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"100":n [style=dashed penwidth=2 color=red] 5 | "80":"8":s->"c0":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 6 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 7 | "40":"6":s->"56":n 8 | "40":"7":s->"57":n 9 | "100" [style=dashed penwidth=2 color=red shape=record label="{00000000a0008000 / 1|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 10 | "100":"0":s->"c0":n [style=dashed penwidth=2 color=red] 11 | "100":"5":s->"140":n [style=dashed penwidth=2 color=red] 12 | "c0" [shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 13 | "c0":"9":s->"8009":n 14 | "140" [style=dashed penwidth=2 color=red shape=record label="{00000000a0008050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 15 | "140":"9":s->"8059":n [style=dashed penwidth=2 color=red] 16 | "8059" [style=dashed penwidth=2 color=red] 17 | } 18 | -------------------------------------------------------------------------------- /doc/lookup.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [penwidth=2 color=red shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"100":n [penwidth=2 color=red] 5 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 6 | "40":"6":s->"56":n 7 | "40":"7":s->"57":n 8 | "100" [penwidth=2 color=red shape=record label="{00000000a0008000 / 1|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 9 | "100":"0":s->"c0":n [penwidth=2 color=red] 10 | "100":"5":s->"140":n 11 | "100":"6":s->"180":n 12 | "c0" [penwidth=2 color=red shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 13 | "c0":"9":s->"8009":n [penwidth=2 color=red] 14 | "8009" [penwidth=2 color=red] 15 | "140" [shape=record label="{00000000a0008050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 16 | "140":"9":s->"8059":n 17 | "180" [shape=record label="{00000000a0008060 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 18 | "180":"9":s->"8069":n 19 | } 20 | -------------------------------------------------------------------------------- /doc/remove1.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"100":n 5 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 6 | "40":"6":s->"56":n 7 | "40":"7":s->"57":n 8 | "100" [shape=record label="{00000000a0008000 / 1|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 9 | "100":"0":s->"c0":n 10 | "100":"5":s->"140":n 11 | "100":"6":s->"180":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 12 | "c0" [shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 13 | "c0":"9":s->"8009":n 14 | "140" [shape=record label="{00000000a0008050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 15 | "140":"9":s->"8059":n 16 | "180" [style=dashed color=gray shape=record label="{00000000a0008060 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 17 | "180":"9":s->"8069":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 18 | "8069" [style=dashed color=gray] 19 | } 20 | -------------------------------------------------------------------------------- /perf/run-perf-tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os, platform, re, subprocess, sys 4 | 5 | def run_bench(args): 6 | bench = "bench.exe" if "Windows" == platform.system() else "bench.out" 7 | with subprocess.Popen( 8 | [os.path.join(os.path.dirname(sys.argv[0]), bench)] + args, 9 | text=True, encoding="utf-8", 10 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as pipe: 11 | while True: 12 | line = pipe.stdout.readline() 13 | if not line: 14 | break 15 | sys.stdout.write(line); 16 | yield line 17 | exitcode = pipe.wait() 18 | if exitcode: 19 | raise subprocess.CalledProcessError(exitcode, pipe.args) 20 | 21 | def bench2csv(args, filename): 22 | with open(filename, "wb") as file: 23 | for line in run_bench(args): 24 | m = re.match(r"(\w+)\.\.\.\.+ (.*)OK ([\d.]+)s", line) 25 | if m: 26 | name, info, time = m.group(1), m.group(2).strip(), m.group(3) 27 | file.write(("%s,%s,%s\n" % (name, info, time)).encode('utf-8')) 28 | 29 | abbrev = { 30 | "Windows": "win", 31 | "Linux": "lnx", 32 | } 33 | for i in range(3): 34 | bench2csv(["+*"], "perf-%s-%s.csv" % (abbrev[platform.system()], i + 1)) 35 | -------------------------------------------------------------------------------- /doc/remove2.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "80" [shape=record label="{00000000a0000000 / 3|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 3 | "80":"0":s->"40":n 4 | "80":"8":s->"c0":n [style=dashed penwidth=2 color="red"] 5 | "80":"8":s->"100":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 6 | "40" [shape=record label="{00000000a0000050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 7 | "40":"6":s->"56":n 8 | "40":"7":s->"57":n 9 | "100" [style=dashed color=gray shape=record label="{00000000a0008000 / 1|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 10 | "100":"0":s->"c0":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 11 | "100":"5":s->"140":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 12 | "c0" [shape=record label="{00000000a0008000 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 13 | "c0":"9":s->"8009":n 14 | "140" [style=dashed color=gray shape=record label="{00000000a0008050 / 0|{<0>0|<1>1|<2>2|<3>3|<4>4|<5>5|<6>6|<7>7|<8>8|<9>9|A|B|C|D|E|F}}"] 15 | "140":"9":s->"8059":n [style=dashed color="gray" taillabel="x" labeldistance=0 labelangle=0] 16 | "8059" [style=dashed color=gray] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | test: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [windows-2022, ubuntu-22.04] 13 | include: 14 | - os: windows-2022 15 | shell: msys2 {0} 16 | - os: ubuntu-22.04 17 | shell: bash 18 | runs-on: ${{ matrix.os }} 19 | defaults: 20 | run: 21 | shell: ${{ matrix.shell }} 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | - name: Enable MSVC (Windows) 26 | if: runner.os == 'Windows' 27 | uses: ilammy/msvc-dev-cmd@v1 28 | - name: Install prerequisites (Windows) 29 | if: runner.os == 'Windows' 30 | uses: msys2/setup-msys2@v2 31 | with: 32 | path-type: inherit 33 | msystem: mingw64 34 | install: >- 35 | base-devel 36 | - name: Install prerequisites (Linux) 37 | if: runner.os == 'Linux' 38 | run: | 39 | sudo apt install valgrind 40 | - name: Functional testing 41 | run: | 42 | make -C test 43 | make -C test testcxx 44 | - name: Valgrind testing (Linux) 45 | if: runner.os == 'Linux' 46 | run: | 47 | make -C test valgrind 48 | - name: Performance testing 49 | run: | 50 | make -C perf 51 | -------------------------------------------------------------------------------- /doc/graph.c: -------------------------------------------------------------------------------- 1 | /* 2 | * graph.c 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #include 14 | 15 | #define IMAP_DUMP_NODE(...) (imap_dump_node_gv(__VA_ARGS__)) 16 | #include "imap.h" 17 | 18 | int main(int argc, char **argv) 19 | { 20 | imap_node_t *tree; 21 | imap_slot_t *slot; 22 | unsigned long long x, y; 23 | char line[80]; 24 | char cmd; 25 | 26 | tree = imap_ensure(0, +1); 27 | 28 | while (fgets(line, sizeof line, stdin)) 29 | { 30 | if (2 == sscanf(line, "%llx = %llx", &x, &y)) 31 | { 32 | /* assign */ 33 | tree = imap_ensure(tree, +1); 34 | slot = imap_assign(tree, (imap_u64_t)x); 35 | imap_setval(tree, slot, (imap_u64_t)y); 36 | } 37 | else if (2 == sscanf(line, "%llx %c", &x, &cmd) && '=' == cmd) 38 | { 39 | /* assign */ 40 | tree = imap_ensure(tree, +1); 41 | slot = imap_assign(tree, (imap_u64_t)x); 42 | } 43 | else if (2 == sscanf(line, "%llx %c", &x, &cmd) && 'r' == cmd) 44 | { 45 | /* remove */ 46 | imap_remove(tree, (imap_u64_t)x); 47 | } 48 | else if (1 == sscanf(line, "%c", &cmd) && 'd' == cmd) 49 | { 50 | /* dump */ 51 | fprintf(stdout, "digraph {\n"); 52 | imap_dump(tree, (imap_dumpfn_t *)fprintf, stdout); 53 | fprintf(stdout, "}\n"); 54 | } 55 | } 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /perf/wrap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * wrap.cpp 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #include 14 | #include 15 | #include "imap.h" 16 | 17 | static imap_u64_t seed = 0; 18 | void test_srand(imap_u64_t s) 19 | { 20 | seed = s; 21 | } 22 | imap_u64_t test_rand(void) 23 | { 24 | /* constants used by Knuth and MUSL */ 25 | seed = seed * 6364136223846793005ULL + 1; 26 | return seed; 27 | } 28 | 29 | void test_imap_insert(imap_node_t *&tree, imap_u64_t x, imap_u64_t y) 30 | { 31 | tree = imap_ensure(tree, +1); 32 | auto slot = imap_assign(tree, x); 33 | imap_setval(tree, slot, y); 34 | } 35 | 36 | void test_imap_assign(imap_node_t *&tree, imap_u64_t x, imap_u64_t y) 37 | { 38 | auto slot = imap_assign(tree, x); 39 | imap_setval(tree, slot, y); 40 | } 41 | 42 | void test_imap_remove(imap_node_t *&tree, imap_u64_t x) 43 | { 44 | imap_remove(tree, x); 45 | } 46 | 47 | imap_u64_t test_imap_lookup(imap_node_t *&tree, imap_u64_t x) 48 | { 49 | auto slot = imap_lookup(tree, x); 50 | return imap_getval(tree, slot); 51 | } 52 | 53 | void test_stdu_insert(std::unordered_map &stdu, imap_u64_t x, imap_u64_t y) 54 | { 55 | stdu.emplace(x, y); 56 | } 57 | 58 | void test_stdu_assign(std::unordered_map &stdu, imap_u64_t x, imap_u64_t y) 59 | { 60 | stdu.insert_or_assign(x, y); 61 | } 62 | 63 | void test_stdu_remove(std::unordered_map &stdu, imap_u64_t x) 64 | { 65 | stdu.erase(x); 66 | } 67 | 68 | imap_u64_t test_stdu_lookup(std::unordered_map &stdu, imap_u64_t x) 69 | { 70 | return stdu.at(x); 71 | } 72 | 73 | void test_stdm_insert(std::map &stdm, imap_u64_t x, imap_u64_t y) 74 | { 75 | stdm.emplace(x, y); 76 | } 77 | 78 | void test_stdm_assign(std::map &stdm, imap_u64_t x, imap_u64_t y) 79 | { 80 | stdm.insert_or_assign(x, y); 81 | } 82 | 83 | void test_stdm_remove(std::map &stdm, imap_u64_t x) 84 | { 85 | stdm.erase(x); 86 | } 87 | 88 | imap_u64_t test_stdm_lookup(std::map &stdm, imap_u64_t x) 89 | { 90 | return stdm.at(x); 91 | } 92 | -------------------------------------------------------------------------------- /doc/nodestru.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | node 15 | 16 | slot 17 | 0 18 | 19 | pos 20 | 21 | slot 22 | 1 23 | 24 | h 25 | 1 26 | 27 | ... 28 | 29 | ... 30 | 31 | slot 32 | F 33 | 34 | h 35 | F 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /fuzz/fuzz.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * fuzz.cpp 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #undef NDEBUG 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | int main(int argc, char **argv) 20 | { 21 | const bool print = false; 22 | imap_node_t *tree; 23 | imap_slot_t *slot; 24 | imap_iter_t iter; 25 | imap_pair_t pair; 26 | std::map stdm; 27 | unsigned long long x, y; 28 | char line[80]; 29 | char cmd; 30 | 31 | tree = imap_ensure(0, +1); 32 | 33 | while (fgets(line, sizeof line, stdin)) 34 | { 35 | if (2 == sscanf(line, "%llx = %llx", &x, &y)) 36 | { 37 | /* assign */ 38 | if (print) printf("%llX=%llX\n", x, y); 39 | tree = imap_ensure(tree, +1); 40 | slot = imap_assign(tree, (imap_u64_t)x); 41 | imap_setval(tree, slot, (imap_u64_t)y); 42 | stdm[x] = y; 43 | } 44 | else if (2 == sscanf(line, "%llx %c", &x, &cmd) && 'r' == cmd) 45 | { 46 | /* remove */ 47 | if (print) printf("%llXr\n", x); 48 | imap_remove(tree, (imap_u64_t)x); 49 | stdm.erase(x); 50 | } 51 | else if (2 == sscanf(line, "%llx %c", &x, &cmd) && 'l' == cmd) 52 | { 53 | /* locate */ 54 | if (print) printf("%llXl\n", x); 55 | pair = imap_locate(tree, &iter, x); 56 | if (pair.slot) 57 | { 58 | auto i = stdm.lower_bound(x); 59 | assert(i != stdm.end()); 60 | assert(pair.x == i->first); 61 | assert(imap_getval(tree, pair.slot) == i->second); 62 | pair = imap_iterate(tree, &iter, 0); 63 | i++; 64 | if (pair.slot) 65 | { 66 | assert(i != stdm.end()); 67 | assert(pair.x == i->first); 68 | assert(imap_getval(tree, pair.slot) == i->second); 69 | pair = imap_iterate(tree, &iter, x); 70 | } 71 | else 72 | assert(i == stdm.end()); 73 | } 74 | else 75 | { 76 | auto i = stdm.lower_bound(x); 77 | assert(i == stdm.end()); 78 | } 79 | } 80 | else if (1 == sscanf(line, "%llx", &x)) 81 | { 82 | /* lookup */ 83 | if (print) printf("%llX\n", x); 84 | slot = imap_lookup(tree, x); 85 | if (slot) 86 | { 87 | assert(imap_getval(tree, slot) == stdm[x]); 88 | } 89 | else 90 | { 91 | assert(0 == stdm.count(x)); 92 | } 93 | } 94 | } 95 | 96 | imap_free(tree); 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /ivmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ivmap.h 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #ifndef IVMAP__GUARD__ 14 | #define IVMAP__GUARD__ 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | typedef imap_u32_t ivmap_u32_t; 23 | typedef imap_u64_t ivmap_u64_t; 24 | typedef imap_node_t ivmap_node_t; 25 | typedef imap_iter_t ivmap_iter_t; 26 | typedef struct ivmap_pair ivmap_pair_t; 27 | 28 | struct ivmap_pair 29 | { 30 | ivmap_u64_t x0, x1; 31 | ivmap_u64_t *y; 32 | }; 33 | 34 | static inline 35 | ivmap_node_t *ivmap_ensure(ivmap_node_t *tree, ivmap_u32_t n) 36 | { 37 | return imap_ensure128(tree, n); 38 | } 39 | 40 | static inline 41 | void ivmap_free(ivmap_node_t *tree) 42 | { 43 | imap_free(tree); 44 | } 45 | 46 | static inline 47 | ivmap_u64_t *ivmap_lookup(ivmap_node_t *tree, ivmap_u64_t x) 48 | { 49 | imap_u128_t *p128; 50 | imap_pair_t pair = imap_succ(tree, x); 51 | return pair.slot && (p128 = imap_addrof128(tree, pair.slot), p128->v[0] <= x) ? &p128->v[1] : 0; 52 | } 53 | 54 | static inline 55 | ivmap_u64_t *ivmap_insert(ivmap_node_t *tree, ivmap_u64_t x0, ivmap_u64_t x1) 56 | { 57 | imap_u128_t *p128; 58 | imap_pair_t pair0 = imap_succ(tree, x0); 59 | imap_pair_t pair1 = imap_succ(tree, x1); 60 | if (pair0.slot != pair1.slot || ( 61 | pair0.slot && (p128 = imap_addrof128(tree, pair0.slot), p128->v[0] < x1))) 62 | return 0; 63 | imap_slot_t *slot = imap_assign(tree, x1); 64 | imap_u128_t v128 = { { x0, 0 } }; 65 | imap_setval128(tree, slot, v128); 66 | p128 = imap_addrof128(tree, slot); 67 | return &p128->v[1]; 68 | } 69 | 70 | static inline 71 | void ivmap_remove(ivmap_node_t *tree, ivmap_u64_t x) 72 | { 73 | imap_u128_t *p128; 74 | imap_pair_t pair = imap_succ(tree, x); 75 | if (pair.slot && (p128 = imap_addrof128(tree, pair.slot), p128->v[0] <= x)) 76 | imap_remove(tree, pair.x); 77 | } 78 | 79 | static inline 80 | ivmap_pair_t ivmap_locate(ivmap_node_t *tree, ivmap_iter_t *iter, ivmap_u64_t x) 81 | { 82 | ivmap_pair_t result = { 0 }; 83 | imap_u128_t *p128; 84 | imap_pair_t pair = imap_locate(tree, iter, x + 1); 85 | if (pair.slot) 86 | { 87 | p128 = imap_addrof128(tree, pair.slot); 88 | result.x0 = p128->v[0]; 89 | result.x1 = pair.x; 90 | result.y = &p128->v[1]; 91 | } 92 | return result; 93 | } 94 | 95 | static inline 96 | ivmap_pair_t ivmap_iterate(ivmap_node_t *tree, ivmap_iter_t *iter, int restart) 97 | { 98 | ivmap_pair_t result = { 0 }; 99 | imap_u128_t *p128; 100 | imap_pair_t pair = imap_iterate(tree, iter, restart); 101 | if (pair.slot) 102 | { 103 | p128 = imap_addrof128(tree, pair.slot); 104 | result.x0 = p128->v[0]; 105 | result.x1 = pair.x; 106 | result.y = &p128->v[1]; 107 | } 108 | return result; 109 | } 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | #endif // IVMAP__GUARD__ 116 | -------------------------------------------------------------------------------- /doc/perf-analysis.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import glob\n", 10 | "import matplotlib.pyplot as plt\n", 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "\n", 14 | "def csv2df(pattern):\n", 15 | " df = None\n", 16 | " for f in sorted(glob.iglob(pattern)):\n", 17 | " df0 = pd.read_csv(f, header=None, names=[\"test\", \"info\", \"time\"])\n", 18 | " if df is None:\n", 19 | " df = df0\n", 20 | " else:\n", 21 | " df[\"time\"] = np.minimum(df[\"time\"], df0[\"time\"])\n", 22 | " df[[\"ds\", \"name\"]] = df[\"test\"].str.extract(r\"([^_]+)_(.*)_test\")\n", 23 | " return df[[\"ds\", \"name\", \"info\", \"time\"]]\n", 24 | "\n", 25 | "tdf = None\n", 26 | "mdf = None\n", 27 | "for s in [\"win\", \"lnx\"]:\n", 28 | " df = csv2df(\"*-%s-*.csv\" % s)\n", 29 | " tdf0 = df.loc[df[\"name\"] != \"memtrack\"][[\"ds\", \"name\", \"time\"]].rename(columns={\"time\": s})\n", 30 | " tdf0 = tdf0.set_index(['ds', 'name'])\n", 31 | " tdf0[s] = tdf0.groupby('name')[s].transform(lambda x: x / x.loc['imap'])\n", 32 | " tdf0 = tdf0.reset_index()\n", 33 | " if tdf is None:\n", 34 | " tdf = tdf0\n", 35 | " else:\n", 36 | " tdf = tdf.merge(tdf0, how=\"left\")\n", 37 | " mdf0 = df.loc[df[\"name\"] == \"memtrack\"][[\"ds\", \"name\", \"info\"]].rename(columns={\"info\": s})\n", 38 | " mdf0[s] = mdf0[s].apply(lambda x: int(x.split(\"/\")[1]) if \"/\" in x else int(x))\n", 39 | " if mdf is None:\n", 40 | " mdf = mdf0\n", 41 | " else:\n", 42 | " mdf = mdf.merge(mdf0, how=\"left\")" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "for t, gdf in tdf.groupby(\"name\", sort=False):\n", 52 | " gdf = gdf.set_index(\"ds\")\n", 53 | " plt.figure(figsize=(6,3), dpi=100, facecolor=\"white\")\n", 54 | " plt.title(t)\n", 55 | " gdf.plot.barh(ax=plt.gca()).invert_yaxis()\n", 56 | " plt.gca().set_xlim(0,22)\n", 57 | " plt.gca().set(ylabel=None)\n", 58 | " for container in plt.gca().containers:\n", 59 | " plt.gca().bar_label(container, fmt=\"%0.2f\", padding=4.0, fontsize=\"xx-small\")\n", 60 | " plt.savefig(t + \".png\")\n", 61 | " #plt.show()\n", 62 | " plt.close()" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 3, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "for t, gdf in mdf.groupby(\"name\", sort=False):\n", 72 | " gdf = gdf.set_index(\"ds\")\n", 73 | " plt.figure(figsize=(6,3), dpi=100, facecolor=\"white\")\n", 74 | " plt.title(t)\n", 75 | " gdf.plot.barh(ax=plt.gca()).invert_yaxis()\n", 76 | " plt.gca().set(ylabel=None)\n", 77 | " for container in plt.gca().containers:\n", 78 | " plt.gca().bar_label(container, fmt=\"%0.0f\", padding=4.0, fontsize=\"xx-small\")\n", 79 | " plt.savefig(t + \".png\")\n", 80 | " #plt.show()\n", 81 | " plt.close()" 82 | ] 83 | } 84 | ], 85 | "metadata": { 86 | "kernelspec": { 87 | "display_name": "base", 88 | "language": "python", 89 | "name": "python3" 90 | }, 91 | "language_info": { 92 | "codemirror_mode": { 93 | "name": "ipython", 94 | "version": 3 95 | }, 96 | "file_extension": ".py", 97 | "mimetype": "text/x-python", 98 | "name": "python", 99 | "nbconvert_exporter": "python", 100 | "pygments_lexer": "ipython3", 101 | "version": "3.8.12" 102 | }, 103 | "orig_nbformat": 4, 104 | "vscode": { 105 | "interpreter": { 106 | "hash": "5ee1342b8c83c7fd94b721cc9ad14b7a9882330f075a3387b03555d396d0a96d" 107 | } 108 | } 109 | }, 110 | "nbformat": 4, 111 | "nbformat_minor": 2 112 | } 113 | -------------------------------------------------------------------------------- /iset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * iset.h 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #ifndef ISET__GUARD__ 14 | #define ISET__GUARD__ 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | typedef imap_u32_t iset_u32_t; 23 | typedef imap_u64_t iset_u64_t; 24 | typedef imap_node_t iset_node_t; 25 | typedef struct iset_iter iset_iter_t; 26 | typedef struct iset_pair iset_pair_t; 27 | 28 | struct iset_iter 29 | { 30 | imap_iter_t iter; 31 | iset_u32_t y; 32 | iset_u64_t x; 33 | }; 34 | struct iset_pair 35 | { 36 | iset_u64_t x; 37 | iset_u32_t elemof; 38 | }; 39 | 40 | #if defined(_MSC_VER) 41 | static inline 42 | iset_u32_t iset__bsf__(iset_u32_t x) 43 | { 44 | return _BitScanForward((unsigned long *)&x, x), (unsigned long)x; 45 | } 46 | #elif defined(__GNUC__) 47 | static inline 48 | iset_u32_t iset__bsf__(iset_u32_t x) 49 | { 50 | return __builtin_ffs(x) - 1; 51 | } 52 | #endif 53 | 54 | static inline 55 | iset_node_t *iset_ensure(iset_node_t *tree, iset_u32_t n) 56 | { 57 | return imap_ensure0(tree, n); 58 | } 59 | 60 | static inline 61 | void iset_free(iset_node_t *tree) 62 | { 63 | imap_free(tree); 64 | } 65 | 66 | static inline 67 | iset_u32_t iset_lookup(iset_node_t *tree, iset_u64_t x) 68 | { 69 | imap_u64_t q = x / imap__slot_sbits__; 70 | imap_u32_t r = x % imap__slot_sbits__; 71 | imap_slot_t *slot = imap_lookup(tree, q); 72 | return slot ? (imap_getval0(tree, slot) & (1 << r)) : 0; 73 | } 74 | 75 | static inline 76 | void iset_assign(iset_node_t *tree, iset_u64_t x) 77 | { 78 | imap_u64_t q = x / imap__slot_sbits__; 79 | imap_u32_t r = x % imap__slot_sbits__; 80 | imap_slot_t *slot = imap_assign(tree, q); 81 | imap_setval0(tree, slot, imap_getval0(tree, slot) | (1 << r)); 82 | } 83 | 84 | static inline 85 | void iset_remove(iset_node_t *tree, iset_u64_t x) 86 | { 87 | imap_u64_t q = x / imap__slot_sbits__; 88 | imap_u32_t r = x % imap__slot_sbits__; 89 | imap_slot_t *slot = imap_lookup(tree, q); 90 | if (slot) 91 | { 92 | imap_u32_t v0 = imap_getval0(tree, slot) & ~(1 << r); 93 | if (v0) 94 | imap_setval0(tree, slot, v0); 95 | else 96 | imap_remove(tree, q); 97 | } 98 | } 99 | 100 | static inline 101 | iset_pair_t iset_locate(iset_node_t *tree, iset_iter_t *iter, iset_u64_t x) 102 | { 103 | iset_pair_t result = { 0 }; 104 | imap_u64_t q = x / imap__slot_sbits__; 105 | imap_u32_t r = x % imap__slot_sbits__; 106 | imap_pair_t pair; 107 | imap_u32_t dirn; 108 | iter->x = iter->y = 0; 109 | pair = imap_locate(tree, &iter->iter, q); 110 | if (pair.slot) 111 | { 112 | iter->y = imap_getval0(tree, pair.slot) & ~((1 << r) - 1); 113 | if (iter->y) 114 | { 115 | iter->x = q * imap__slot_sbits__; 116 | dirn = iset__bsf__(iter->y); 117 | iter->y &= ~(1 << dirn); 118 | result.x = iter->x + dirn; 119 | result.elemof = 1; 120 | } 121 | } 122 | return result; 123 | } 124 | 125 | static inline 126 | iset_pair_t iset_iterate(iset_node_t *tree, iset_iter_t *iter, int restart) 127 | { 128 | iset_pair_t result = { 0 }; 129 | imap_pair_t pair; 130 | imap_u32_t dirn; 131 | if (restart) 132 | iter->x = iter->y = 0; 133 | else if (iter->y) 134 | goto fill; 135 | pair = imap_iterate(tree, &iter->iter, restart); 136 | if (pair.slot) 137 | { 138 | iter->y = imap_getval0(tree, pair.slot); 139 | iter->x = pair.x * imap__slot_sbits__; 140 | fill: 141 | dirn = iset__bsf__(iter->y); 142 | iter->y &= ~(1 << dirn); 143 | result.x = iter->x + dirn; 144 | result.elemof = 1; 145 | } 146 | return result; 147 | } 148 | 149 | #ifdef __cplusplus 150 | } 151 | #endif 152 | 153 | #endif // ISET__GUARD__ 154 | -------------------------------------------------------------------------------- /doc/mapping0.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | N40 15 | 16 | 00000000a0000050 / 0 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 56 53 | 54 | 56 55 | 56 | 57 | 58 | N40:s->56:n 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /doc/node.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | node 15 | 16 | h 17 | F 18 | ...h 19 | 1 20 | h 21 | 0 22 | / pos 23 | 24 | 0 25 | 26 | 1 27 | 28 | 2 29 | 30 | 3 31 | 32 | 4 33 | 34 | 5 35 | 36 | 6 37 | 38 | 7 39 | 40 | 8 41 | 42 | 9 43 | 44 | A 45 | 46 | B 47 | 48 | C 49 | 50 | D 51 | 52 | E 53 | 54 | F 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /tlib/testsuite.h: -------------------------------------------------------------------------------- 1 | /* 2 | * tlib/testsuite.h 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #ifndef TLIB_TESTSUITE_H_INCLUDED 14 | #define TLIB_TESTSUITE_H_INCLUDED 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include 21 | 22 | /** 23 | * Assert macro 24 | * 25 | * This macro works similarly to the Standard C assert macro, except for the following differences: 26 | * 27 | *
    28 | *
  • The macro always checks the specified expression regardless of the NDEBUG macro.
  • 29 | *
  • The macro aborts the execution of the current test, but not necessarily the execution of the 30 | * whole testsuite.
  • 31 | *
32 | */ 33 | #define ASSERT(expr)\ 34 | (!(expr) ? (tlib__assert(__func__, __FILE__, __LINE__, #expr), abort()) : (void)0) 35 | 36 | /** 37 | * Register a test suite for execution. 38 | * 39 | * Test suites are simple functions with prototype void testsuite(). When executed 40 | * they register individual test cases for execution. 41 | */ 42 | #define TESTSUITE(fn)\ 43 | do\ 44 | {\ 45 | void fn(void);\ 46 | tlib_add_test_suite(#fn, fn);\ 47 | } while (0) 48 | 49 | /** 50 | * Register a test case for execution. 51 | * 52 | * Test cases are simple functions with prototype void testcase(). 53 | */ 54 | #define TEST(fn)\ 55 | do\ 56 | {\ 57 | void fn(void);\ 58 | tlib_add_test(#fn, fn);\ 59 | } while (0) 60 | 61 | /** 62 | * Register an optional test case for execution. 63 | * 64 | * Test cases are simple functions with prototype void testcase(). 65 | * Optional tests are not executed by default. 66 | */ 67 | #define TEST_OPT(fn)\ 68 | do\ 69 | {\ 70 | void fn(void);\ 71 | tlib_add_test_opt(#fn, fn);\ 72 | } while (0) 73 | 74 | void tlib_add_test_suite(const char *name, void (*fn)(void)); 75 | void tlib_add_test(const char *name, void (*fn)(void)); 76 | void tlib_add_test_opt(const char *name, void (*fn)(void)); 77 | 78 | /** 79 | * Register a test hook to be run before and after every test. 80 | * 81 | * Test hooks are functions with prototype 82 | * void testhook(const char *name, void (*fn)(void), int v). 83 | * The parameter v specifies that a test is about to be executed (v is +1) 84 | * or it was just executed (v is -1). 85 | */ 86 | #define TESTHOOK(fn)\ 87 | do\ 88 | {\ 89 | void fn(const char *name, void (*fn)(void), int v);\ 90 | tlib_add_hook(fn);\ 91 | } while (0) 92 | 93 | void tlib_add_hook(void (*fn)(const char *name, void (*fn)(void), int v)); 94 | 95 | /** 96 | * Printf function. 97 | * 98 | * Use this to produce output in the appropriate tlib file stream. This function uses the local 99 | * printf facilities and understands the same format strings. 100 | */ 101 | #if defined(__GNUC__) 102 | void tlib_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 103 | #else 104 | void tlib_printf(const char *fmt, ...); 105 | #endif 106 | /** 107 | * Run tests. 108 | * 109 | * This function will first execute all registered test suites, thus giving them the chance to 110 | * register any test cases. It will then execute all registered test cases according to the 111 | * command line arguments passed in argv. The command line syntax is a follows: 112 | * 113 | * Usage: testprog [--list][[--tap][--no-abort][--repeat-forever] [[+-]TESTNAME...] 114 | * 115 | *
    116 | *
  • --list - list tests only
  • 117 | *
  • --tap - produce output in TAP format
  • 118 | *
  • --no-abort - do not abort all tests when an ASSERT fails 119 | * (only the current test is aborted)
  • 120 | *
  • --repeat-forever - repeat tests forever
  • 121 | *
122 | * 123 | * By default all test cases are executed unless specific test cases are named. By default optional 124 | * test cases are not executed. To execute a specific test case specify its TESTNAME; if it is an 125 | * optional test case specify +TESTNAME. To exclude a test case specify -TESTNAME. 126 | * 127 | * TESTNAME may also contain a single asterisk at the end; for example, mytest* will match all test 128 | * cases that have names starting with "mytest". 129 | * 130 | * @param argc 131 | * Argument count. 132 | * @param argv 133 | * Argument vector. 134 | */ 135 | void tlib_run_tests(int argc, char *argv[]); 136 | 137 | void tlib__assert(const char *func, const char *file, int line, const char *expr); 138 | 139 | #ifdef __cplusplus 140 | } 141 | #endif 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /doc/mapping1.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | N40 15 | 16 | 00000000a0000050 / 0 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 56 53 | 54 | 56 55 | 56 | 57 | 58 | N40:s->56:n 59 | 60 | 61 | 62 | 63 | 64 | 57 65 | 66 | 57 67 | 68 | 69 | 70 | N40:s->57:n 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /tlib/testsuite.c: -------------------------------------------------------------------------------- 1 | /* 2 | * tlib/testsuite.c 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | struct test 23 | { 24 | char name[64]; 25 | void (*fn)(void); 26 | int optional; 27 | struct test *next; 28 | }; 29 | static struct test test_suite_sentinel = { .next = &test_suite_sentinel }; 30 | static struct test *test_suite_tail = &test_suite_sentinel; 31 | static struct test test_sentinel = { .next = &test_sentinel }; 32 | static struct test *test_tail = &test_sentinel; 33 | static void add_test_to_list(const char *name, void (*fn)(void), int optional, struct test **tail) 34 | { 35 | struct test *test = calloc(1, sizeof *test); 36 | strncpy(test->name, name, sizeof test->name - 1); 37 | test->name[sizeof test->name - 1] = '\0'; 38 | test->fn = fn; 39 | test->optional = optional; 40 | test->next = (*tail)->next; 41 | (*tail)->next = test; 42 | (*tail) = test; 43 | } 44 | void tlib_add_test_suite(const char *name, void (*fn)(void)) 45 | { 46 | add_test_to_list(name, fn, 0, &test_suite_tail); 47 | } 48 | void tlib_add_test(const char *name, void (*fn)(void)) 49 | { 50 | add_test_to_list(name, fn, 0, &test_tail); 51 | } 52 | void tlib_add_test_opt(const char *name, void (*fn)(void)) 53 | { 54 | add_test_to_list(name, fn, 1, &test_tail); 55 | } 56 | 57 | struct hook 58 | { 59 | void (*fn)(const char *name, void (*fn)(void), int v); 60 | struct hook *next; 61 | }; 62 | static struct hook hook_sentinel = { .next = &hook_sentinel }; 63 | static struct hook *hook_tail = &hook_sentinel; 64 | static void add_hook_to_list(void (*fn)(const char *name, void (*fn)(void), int v), struct hook **tail) 65 | { 66 | struct hook *hook = calloc(1, sizeof *hook); 67 | hook->fn = fn; 68 | hook->next = (*tail)->next; 69 | (*tail)->next = hook; 70 | (*tail) = hook; 71 | } 72 | void tlib_add_hook(void (*fn)(const char *name, void (*fn)(void), int v)) 73 | { 74 | add_hook_to_list(fn, &hook_tail); 75 | } 76 | 77 | static FILE *tlib_out, *tlib_err; 78 | static jmp_buf test_jmp_buf, *test_jmp; 79 | static char assert_buf[256]; 80 | static void test_printf(const char *fmt, ...); 81 | static inline unsigned long long get_time(void) 82 | { 83 | #if defined(_WIN32) 84 | #pragma comment(lib, "winmm.lib") 85 | unsigned long __stdcall timeGetTime(void); 86 | return timeGetTime(); 87 | #elif defined(__linux__) 88 | int clock_gettime(int, struct timespec *); 89 | struct timespec ts; 90 | clock_gettime(7/*CLOCK_BOOTTIME*/, &ts); 91 | return (unsigned long long)ts.tv_sec * 1000ULL + (unsigned long long)ts.tv_nsec / 1000000ULL; 92 | #else 93 | return (unsigned long long)time(0) * 1000ULL; 94 | #endif 95 | } 96 | static unsigned long long run_test(struct test *test) 97 | { 98 | for (struct hook *hook = hook_tail->next->next; 0 != hook->fn; hook = hook->next) 99 | hook->fn(test->name, test->fn, +1); 100 | unsigned long long t0 = get_time(); 101 | test->fn(); 102 | unsigned long long t1 = get_time(); 103 | for (struct hook *hook = hook_tail->next->next; 0 != hook->fn; hook = hook->next) 104 | hook->fn(test->name, test->fn, -1); 105 | return t1 - t0; 106 | } 107 | static void do_test_default(struct test *test, int testno) 108 | { 109 | if (0 != test) 110 | { 111 | snprintf(assert_buf, sizeof assert_buf, "KO\n "); 112 | char dispname[39 + 1]; 113 | size_t displen = strlen(test->name); 114 | if (displen > sizeof dispname - 1) 115 | displen = sizeof dispname - 1; 116 | memcpy(dispname, test->name, displen); 117 | memset(dispname + displen, '.', sizeof dispname - 1 - displen); 118 | dispname[sizeof dispname - 1] = '\0'; 119 | test_printf("%s ", dispname); 120 | unsigned long long d = run_test(test); 121 | test_printf("OK %u.%02us\n", (unsigned)(d / 1000), (unsigned)((d % 1000) / 10)); 122 | } 123 | else 124 | test_printf("--- COMPLETE ---\n"); 125 | } 126 | static void do_test_list(struct test *test, int testno) 127 | { 128 | if (0 != test) 129 | test_printf("%s\n", test->name); 130 | } 131 | static void do_test_tap(struct test *test, int testno) 132 | { 133 | if (0 != test) 134 | { 135 | snprintf(assert_buf, sizeof assert_buf, "not ok %d %s\n# ", testno + 1, test->name); 136 | run_test(test); 137 | test_printf("ok %d %s\n", testno + 1, test->name); 138 | } 139 | else 140 | test_printf("1..%d\n", testno); 141 | } 142 | static void test_printf(const char *fmt, ...) 143 | { 144 | va_list ap; 145 | va_start(ap, fmt); 146 | FILE *f = tlib_out ? tlib_out : stdout; 147 | vfprintf(f, fmt, ap); 148 | fflush(f); 149 | va_end(ap); 150 | } 151 | void tlib_printf(const char *fmt, ...) 152 | { 153 | va_list ap; 154 | va_start(ap, fmt); 155 | FILE *f = tlib_err ? tlib_err : stderr; 156 | vfprintf(f, fmt, ap); 157 | fflush(f); 158 | va_end(ap); 159 | } 160 | void tlib_run_tests(int argc, char *argv[]) 161 | { 162 | argc--; argv++; 163 | void (*do_test)(struct test *, int) = do_test_default; 164 | int match_any = 1, no_abort = 0; 165 | unsigned long repeat = 1; 166 | for (char **ap = argv, **aendp = ap + argc; aendp > ap; ap++) 167 | { 168 | const char *a = *ap; 169 | if ('-' == a[0]) 170 | { 171 | if (0 == strcmp("--list", a)) 172 | do_test = do_test_list; 173 | else if (0 == strcmp("--tap", a)) 174 | do_test = do_test_tap; 175 | else if (0 == strcmp("--no-abort", a)) 176 | no_abort = 1; 177 | else if (0 == strcmp("--repeat-forever", a)) 178 | repeat = ULONG_MAX; 179 | else if ('-' == a[1]) 180 | { 181 | fprintf(stderr, "tlib_run_tests: unknown option %s\n", a); 182 | exit(2); 183 | } 184 | } 185 | else 186 | match_any = 0; 187 | } 188 | for (struct test *test = test_suite_tail->next->next; 0 != test->fn; test = test->next) 189 | test->fn(); 190 | while (repeat--) 191 | { 192 | int testno = 0; 193 | for (struct test *test = test_tail->next->next; 0 != test->fn; test = test->next) 194 | { 195 | int match_arg = match_any && !test->optional; 196 | for (char **ap = argv, **aendp = ap + argc; aendp > ap; aendp--) 197 | { 198 | const char *a = aendp[-1]; 199 | int sign = a[0]; 200 | if ('+' == sign) 201 | a++; 202 | else if ('-' == sign) 203 | { 204 | if ('-' == a[1]) 205 | continue; 206 | a++; 207 | } 208 | size_t l = strlen(a); 209 | if (0 == (0 < l && '*' == a[l - 1] ? 210 | strncmp(test->name, a, l - 1) : strcmp(test->name, a))) 211 | { 212 | if ('+' == sign) 213 | match_arg = 1; 214 | else if ('-' == sign) 215 | match_arg = 0; 216 | else 217 | match_arg = !test->optional; 218 | break; 219 | } 220 | } 221 | if (!match_arg) 222 | continue; 223 | assert_buf[0] = '\0'; 224 | if (no_abort) 225 | { 226 | test_jmp = &test_jmp_buf; 227 | if (0 == setjmp(*test_jmp)) 228 | do_test(test, testno); 229 | test_jmp = 0; 230 | } 231 | else 232 | do_test(test, testno); 233 | testno++; 234 | } 235 | do_test(0, testno); 236 | } 237 | } 238 | void tlib__assert(const char *func, const char *file, int line, const char *expr) 239 | { 240 | #if defined(_WIN32) 241 | const char *p = strrchr(file, '\\'); 242 | #else 243 | const char *p = strrchr(file, '/'); 244 | #endif 245 | file = 0 != p ? p + 1 : file; 246 | if (0 == func) 247 | test_printf("%sASSERT(%s) failed at: %s:%d\n", assert_buf, expr, file, line); 248 | else 249 | test_printf("%sASSERT(%s) failed at %s:%d:%s\n", assert_buf, expr, file, line, func); 250 | if (0 != test_jmp) 251 | longjmp(*test_jmp, 1); 252 | } 253 | -------------------------------------------------------------------------------- /perf/bench.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * bench.cpp 3 | * 4 | * Copyright 2023 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of imap. 8 | * 9 | * It is licensed under the MIT license. The full license text can be found 10 | * in the License.txt file at the root of this project. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "imap.h" 19 | 20 | void test_srand(imap_u64_t s); 21 | imap_u64_t test_rand(void); 22 | void test_imap_insert(imap_node_t *&tree, imap_u64_t x, imap_u64_t y); 23 | void test_imap_assign(imap_node_t *&tree, imap_u64_t x, imap_u64_t y); 24 | void test_imap_remove(imap_node_t *&tree, imap_u64_t x); 25 | imap_u64_t test_imap_lookup(imap_node_t *&tree, imap_u64_t x); 26 | void test_stdu_insert(std::unordered_map &stdu, imap_u64_t x, imap_u64_t y); 27 | void test_stdu_assign(std::unordered_map &stdu, imap_u64_t x, imap_u64_t y); 28 | void test_stdu_remove(std::unordered_map &stdu, imap_u64_t x); 29 | imap_u64_t test_stdu_lookup(std::unordered_map &stdu, imap_u64_t x); 30 | void test_stdm_insert(std::map &stdm, imap_u64_t x, imap_u64_t y); 31 | void test_stdm_assign(std::map &stdm, imap_u64_t x, imap_u64_t y); 32 | void test_stdm_remove(std::map &stdm, imap_u64_t x); 33 | imap_u64_t test_stdm_lookup(std::map &stdm, imap_u64_t x); 34 | 35 | static const unsigned N = 10000000; 36 | static imap_node_t *tree = imap_ensure(0, +1); 37 | static imap_node_t *trbv = imap_ensure(0, +1); 38 | static std::unordered_map stdu; 39 | static std::map stdm; 40 | 41 | static imap_u32_t *init_random_array() 42 | { 43 | test_srand(time(0)); 44 | imap_u32_t *array = (imap_u32_t *)malloc(N * sizeof(imap_u32_t)); 45 | for (unsigned i = 0; N > i; i++) 46 | array[i] = i; 47 | for (unsigned i = 0; N > i; i++) 48 | { 49 | imap_u32_t r = test_rand() % N; 50 | imap_u32_t t = array[i]; 51 | array[i] = array[r]; 52 | array[r] = t; 53 | } 54 | return array; 55 | } 56 | static imap_u32_t *test_array = init_random_array(); 57 | 58 | static void imap_seq_insert_test(void) 59 | { 60 | for (unsigned i = 0; N > i; i++) 61 | test_imap_insert(tree, i, i); 62 | } 63 | 64 | static void imap_seq_assign_test(void) 65 | { 66 | for (unsigned i = 0; N > i; i++) 67 | test_imap_assign(tree, i, i); 68 | } 69 | 70 | static void imap_seq_lookup_test(void) 71 | { 72 | for (unsigned i = 0; N > i; i++) 73 | test_imap_lookup(tree, i); 74 | } 75 | 76 | static void imap_seq_remove_test(void) 77 | { 78 | for (unsigned i = 0; N > i; i++) 79 | test_imap_remove(tree, i); 80 | } 81 | 82 | static void imbv_seq_insert_test(void) 83 | { 84 | for (unsigned i = 0; N > i; i++) 85 | test_imap_insert(trbv, i, 0x8000000000000000ull | i); 86 | } 87 | 88 | static void imbv_seq_assign_test(void) 89 | { 90 | for (unsigned i = 0; N > i; i++) 91 | test_imap_assign(trbv, i, 0x8000000000000000ull | i); 92 | } 93 | 94 | static void imbv_seq_lookup_test(void) 95 | { 96 | for (unsigned i = 0; N > i; i++) 97 | test_imap_lookup(trbv, i); 98 | } 99 | 100 | static void imbv_seq_remove_test(void) 101 | { 102 | for (unsigned i = 0; N > i; i++) 103 | test_imap_remove(trbv, i); 104 | } 105 | 106 | static void imap_rnd_insert_test(void) 107 | { 108 | for (unsigned i = 0; N > i; i++) 109 | test_imap_insert(tree, test_array[i], test_array[i]); 110 | } 111 | 112 | static void imap_rnd_assign_test(void) 113 | { 114 | for (unsigned i = 0; N > i; i++) 115 | test_imap_assign(tree, test_array[i], test_array[i]); 116 | } 117 | 118 | static void imap_rnd_lookup_test(void) 119 | { 120 | for (unsigned i = 0; N > i; i++) 121 | test_imap_lookup(tree, test_array[i]); 122 | } 123 | 124 | static void imap_rnd_remove_test(void) 125 | { 126 | for (unsigned i = 0; N > i; i++) 127 | test_imap_remove(tree, test_array[i]); 128 | } 129 | 130 | static void imap_shortseq_test(void) 131 | { 132 | for (unsigned i = 0; N / 100 > i; i++) 133 | { 134 | for (unsigned j = 0; 100 > j; j++) 135 | test_imap_insert(tree, j, j); 136 | for (unsigned j = 0; 100 > j; j++) 137 | test_imap_assign(tree, j, j); 138 | for (unsigned j = 0; 100 > j; j++) 139 | test_imap_lookup(tree, j); 140 | for (unsigned j = 0; 100 > j; j++) 141 | test_imap_remove(tree, j); 142 | } 143 | } 144 | 145 | static void stdu_seq_insert_test(void) 146 | { 147 | for (unsigned i = 0; N > i; i++) 148 | test_stdu_insert(stdu, i, i); 149 | } 150 | 151 | static void stdu_seq_assign_test(void) 152 | { 153 | for (unsigned i = 0; N > i; i++) 154 | test_stdu_assign(stdu, i, i); 155 | } 156 | 157 | static void stdu_seq_lookup_test(void) 158 | { 159 | for (unsigned i = 0; N > i; i++) 160 | test_stdu_lookup(stdu, i); 161 | } 162 | 163 | static void stdu_seq_remove_test(void) 164 | { 165 | for (unsigned i = 0; N > i; i++) 166 | test_stdu_remove(stdu, i); 167 | } 168 | 169 | static void stdu_rnd_insert_test(void) 170 | { 171 | for (unsigned i = 0; N > i; i++) 172 | test_stdu_insert(stdu, test_array[i], test_array[i]); 173 | } 174 | 175 | static void stdu_rnd_assign_test(void) 176 | { 177 | for (unsigned i = 0; N > i; i++) 178 | test_stdu_assign(stdu, test_array[i], test_array[i]); 179 | } 180 | 181 | static void stdu_rnd_lookup_test(void) 182 | { 183 | for (unsigned i = 0; N > i; i++) 184 | test_stdu_lookup(stdu, test_array[i]); 185 | } 186 | 187 | static void stdu_rnd_remove_test(void) 188 | { 189 | for (unsigned i = 0; N > i; i++) 190 | test_stdu_remove(stdu, test_array[i]); 191 | } 192 | 193 | static void stdu_shortseq_test(void) 194 | { 195 | for (unsigned i = 0; N / 100 > i; i++) 196 | { 197 | for (unsigned j = 0; 100 > j; j++) 198 | test_stdu_insert(stdu, j, j); 199 | for (unsigned j = 0; 100 > j; j++) 200 | test_stdu_assign(stdu, j, j); 201 | for (unsigned j = 0; 100 > j; j++) 202 | test_stdu_lookup(stdu, j); 203 | for (unsigned j = 0; 100 > j; j++) 204 | test_stdu_remove(stdu, j); 205 | } 206 | } 207 | 208 | static void stdm_seq_insert_test(void) 209 | { 210 | for (unsigned i = 0; N > i; i++) 211 | test_stdm_insert(stdm, i, i); 212 | } 213 | 214 | static void stdm_seq_assign_test(void) 215 | { 216 | for (unsigned i = 0; N > i; i++) 217 | test_stdm_assign(stdm, i, i); 218 | } 219 | 220 | static void stdm_seq_lookup_test(void) 221 | { 222 | for (unsigned i = 0; N > i; i++) 223 | test_stdm_lookup(stdm, i); 224 | } 225 | 226 | static void stdm_seq_remove_test(void) 227 | { 228 | for (unsigned i = 0; N > i; i++) 229 | test_stdm_remove(stdm, i); 230 | } 231 | 232 | static void stdm_rnd_insert_test(void) 233 | { 234 | for (unsigned i = 0; N > i; i++) 235 | test_stdm_insert(stdm, test_array[i], test_array[i]); 236 | } 237 | 238 | static void stdm_rnd_assign_test(void) 239 | { 240 | for (unsigned i = 0; N > i; i++) 241 | test_stdm_assign(stdm, test_array[i], test_array[i]); 242 | } 243 | 244 | static void stdm_rnd_lookup_test(void) 245 | { 246 | for (unsigned i = 0; N > i; i++) 247 | test_stdm_lookup(stdm, test_array[i]); 248 | } 249 | 250 | static void stdm_rnd_remove_test(void) 251 | { 252 | for (unsigned i = 0; N > i; i++) 253 | test_stdm_remove(stdm, test_array[i]); 254 | } 255 | 256 | static void stdm_shortseq_test(void) 257 | { 258 | for (unsigned i = 0; N / 100 > i; i++) 259 | { 260 | for (unsigned j = 0; 100 > j; j++) 261 | test_stdm_insert(stdm, j, j); 262 | for (unsigned j = 0; 100 > j; j++) 263 | test_stdm_assign(stdm, j, j); 264 | for (unsigned j = 0; 100 > j; j++) 265 | test_stdm_lookup(stdm, j); 266 | for (unsigned j = 0; 100 > j; j++) 267 | test_stdm_remove(stdm, j); 268 | } 269 | } 270 | 271 | static size_t memtrack_total = 0; 272 | template 273 | class memtrack_allocator 274 | { 275 | public: 276 | using value_type = T; 277 | memtrack_allocator() 278 | { 279 | } 280 | template 281 | memtrack_allocator(const memtrack_allocator &other) 282 | { 283 | } 284 | T *allocate(std::size_t size) 285 | { 286 | memtrack_total += sizeof(T) * size; 287 | return (T *)malloc(sizeof(T) * size); 288 | } 289 | void deallocate(T *p, std::size_t size) 290 | { 291 | memtrack_total -= sizeof(T) * size; 292 | free(p); 293 | } 294 | }; 295 | 296 | static void imap_memtrack_test(void) 297 | { 298 | imap_node_t *t = imap_ensure(0, +1); 299 | 300 | for (unsigned i = 0; N > i; i++) 301 | test_imap_insert(t, i, i); 302 | 303 | tlib_printf("%u/%u ", t->vec32[imap__tree_mark__], t->vec32[imap__tree_size__]); 304 | 305 | imap_free(t); 306 | } 307 | 308 | static void imbv_memtrack_test(void) 309 | { 310 | imap_node_t *t = imap_ensure(0, +1); 311 | 312 | for (unsigned i = 0; N > i; i++) 313 | test_imap_insert(t, i, 0x8000000000000000ull | i); 314 | 315 | tlib_printf("%u/%u ", t->vec32[imap__tree_mark__], t->vec32[imap__tree_size__]); 316 | 317 | imap_free(t); 318 | } 319 | 320 | static void stdu_memtrack_test(void) 321 | { 322 | IMAP_ASSERT(memtrack_total == 0); 323 | 324 | std::unordered_map< 325 | imap_u64_t, 326 | imap_u64_t, 327 | std::hash, 328 | std::equal_to, 329 | memtrack_allocator>> u; 330 | 331 | for (unsigned i = 0; N > i; i++) 332 | u.emplace(i, i); 333 | 334 | tlib_printf("%llu ", (unsigned long long)memtrack_total); 335 | } 336 | 337 | static void stdm_memtrack_test(void) 338 | { 339 | IMAP_ASSERT(memtrack_total == 0); 340 | 341 | std::map< 342 | imap_u64_t, 343 | imap_u64_t, 344 | std::less, 345 | memtrack_allocator>> m; 346 | 347 | for (unsigned i = 0; N > i; i++) 348 | m.emplace(i, i); 349 | 350 | tlib_printf("%llu ", (unsigned long long)memtrack_total); 351 | } 352 | 353 | void perf_tests(void) 354 | { 355 | TEST(imap_seq_insert_test); 356 | TEST_OPT(imbv_seq_insert_test); 357 | TEST(stdu_seq_insert_test); 358 | TEST_OPT(stdm_seq_insert_test); 359 | TEST(imap_seq_assign_test); 360 | TEST_OPT(imbv_seq_assign_test); 361 | TEST(stdu_seq_assign_test); 362 | TEST_OPT(stdm_seq_assign_test); 363 | TEST(imap_seq_lookup_test); 364 | TEST_OPT(imbv_seq_lookup_test); 365 | TEST(stdu_seq_lookup_test); 366 | TEST_OPT(stdm_seq_lookup_test); 367 | TEST(imap_seq_remove_test); 368 | TEST_OPT(imbv_seq_remove_test); 369 | TEST(stdu_seq_remove_test); 370 | TEST_OPT(stdm_seq_remove_test); 371 | TEST(imap_rnd_insert_test); 372 | TEST(stdu_rnd_insert_test); 373 | TEST_OPT(stdm_rnd_insert_test); 374 | TEST(imap_rnd_assign_test); 375 | TEST(stdu_rnd_assign_test); 376 | TEST_OPT(stdm_rnd_assign_test); 377 | TEST(imap_rnd_lookup_test); 378 | TEST(stdu_rnd_lookup_test); 379 | TEST_OPT(stdm_rnd_lookup_test); 380 | TEST(imap_rnd_remove_test); 381 | TEST(stdu_rnd_remove_test); 382 | TEST_OPT(stdm_rnd_remove_test); 383 | TEST(imap_shortseq_test); 384 | TEST(stdu_shortseq_test); 385 | TEST_OPT(stdm_shortseq_test); 386 | TEST(imap_memtrack_test); 387 | TEST_OPT(imbv_memtrack_test); 388 | TEST(stdu_memtrack_test); 389 | TEST_OPT(stdm_memtrack_test); 390 | } 391 | 392 | int main(int argc, char **argv) 393 | { 394 | TESTSUITE(perf_tests); 395 | 396 | tlib_run_tests(argc, argv); 397 | return 0; 398 | } 399 | -------------------------------------------------------------------------------- /doc/mapping2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | N80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | N40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | N80:s->N40:n 91 | 92 | 93 | 94 | 95 | 96 | Nc0 97 | 98 | 00000000a0008000 / 0 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | N80:s->Nc0:n 135 | 136 | 137 | 138 | 139 | 140 | 56 141 | 142 | 56 143 | 144 | 145 | 146 | N40:s->56:n 147 | 148 | 149 | 150 | 151 | 152 | 57 153 | 154 | 57 155 | 156 | 157 | 158 | N40:s->57:n 159 | 160 | 161 | 162 | 163 | 164 | 8009 165 | 166 | 8009 167 | 168 | 169 | 170 | Nc0:s->8009:n 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /doc/assign0.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | 80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | 80:s->40:n 91 | 92 | 93 | 94 | 95 | 96 | c0 97 | 98 | 00000000a0008000 / 0 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | 80:s->c0:n 135 | 136 | 137 | 138 | 139 | 140 | 56 141 | 142 | 56 143 | 144 | 145 | 146 | 40:s->56:n 147 | 148 | 149 | 150 | 151 | 152 | 57 153 | 154 | 57 155 | 156 | 157 | 158 | 40:s->57:n 159 | 160 | 161 | 162 | 163 | 164 | 8009 165 | 166 | 8009 167 | 168 | 169 | 170 | c0:s->8009:n 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /doc/remove0.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | 80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | 80:s->40:n 91 | 92 | 93 | 94 | 95 | 96 | c0 97 | 98 | 00000000a0008000 / 0 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | 80:s->c0:n 135 | 136 | 137 | 138 | 139 | 140 | 56 141 | 142 | 56 143 | 144 | 145 | 146 | 40:s->56:n 147 | 148 | 149 | 150 | 151 | 152 | 57 153 | 154 | 57 155 | 156 | 157 | 158 | 40:s->57:n 159 | 160 | 161 | x 162 | 163 | 164 | 165 | 8009 166 | 167 | 8009 168 | 169 | 170 | 171 | c0:s->8009:n 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /doc/remove2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | 80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | 80:s->40:n 91 | 92 | 93 | 94 | 95 | 96 | c0 97 | 98 | 00000000a0008000 / 0 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | 80:s->c0:n 135 | 136 | 137 | 138 | 139 | 140 | 100 141 | 142 | 00000000a0008000 / 1 143 | 144 | 0 145 | 146 | 1 147 | 148 | 2 149 | 150 | 3 151 | 152 | 4 153 | 154 | 5 155 | 156 | 6 157 | 158 | 7 159 | 160 | 8 161 | 162 | 9 163 | 164 | A 165 | 166 | B 167 | 168 | C 169 | 170 | D 171 | 172 | E 173 | 174 | F 175 | 176 | 177 | 178 | 80:s->100:n 179 | 180 | 181 | x 182 | 183 | 184 | 185 | 56 186 | 187 | 56 188 | 189 | 190 | 191 | 40:s->56:n 192 | 193 | 194 | 195 | 196 | 197 | 57 198 | 199 | 57 200 | 201 | 202 | 203 | 40:s->57:n 204 | 205 | 206 | 207 | 208 | 209 | 8009 210 | 211 | 8009 212 | 213 | 214 | 215 | c0:s->8009:n 216 | 217 | 218 | 219 | 220 | 221 | 100:s->c0:n 222 | 223 | 224 | x 225 | 226 | 227 | 228 | 140 229 | 230 | 00000000a0008050 / 0 231 | 232 | 0 233 | 234 | 1 235 | 236 | 2 237 | 238 | 3 239 | 240 | 4 241 | 242 | 5 243 | 244 | 6 245 | 246 | 7 247 | 248 | 8 249 | 250 | 9 251 | 252 | A 253 | 254 | B 255 | 256 | C 257 | 258 | D 259 | 260 | E 261 | 262 | F 263 | 264 | 265 | 266 | 100:s->140:n 267 | 268 | 269 | x 270 | 271 | 272 | 273 | 8059 274 | 275 | 8059 276 | 277 | 278 | 279 | 140:s->8059:n 280 | 281 | 282 | x 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /doc/assign2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | 80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | 80:s->40:n 91 | 92 | 93 | 94 | 95 | 96 | 100 97 | 98 | 00000000a0008000 / 1 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | 80:s->100:n 135 | 136 | 137 | 138 | 139 | 140 | c0 141 | 142 | 00000000a0008000 / 0 143 | 144 | 0 145 | 146 | 1 147 | 148 | 2 149 | 150 | 3 151 | 152 | 4 153 | 154 | 5 155 | 156 | 6 157 | 158 | 7 159 | 160 | 8 161 | 162 | 9 163 | 164 | A 165 | 166 | B 167 | 168 | C 169 | 170 | D 171 | 172 | E 173 | 174 | F 175 | 176 | 177 | 178 | 80:s->c0:n 179 | 180 | 181 | x 182 | 183 | 184 | 185 | 56 186 | 187 | 56 188 | 189 | 190 | 191 | 40:s->56:n 192 | 193 | 194 | 195 | 196 | 197 | 57 198 | 199 | 57 200 | 201 | 202 | 203 | 40:s->57:n 204 | 205 | 206 | 207 | 208 | 209 | 100:s->c0:n 210 | 211 | 212 | 213 | 214 | 215 | 140 216 | 217 | 00000000a0008050 / 0 218 | 219 | 0 220 | 221 | 1 222 | 223 | 2 224 | 225 | 3 226 | 227 | 4 228 | 229 | 5 230 | 231 | 6 232 | 233 | 7 234 | 235 | 8 236 | 237 | 9 238 | 239 | A 240 | 241 | B 242 | 243 | C 244 | 245 | D 246 | 247 | E 248 | 249 | F 250 | 251 | 252 | 253 | 100:s->140:n 254 | 255 | 256 | 257 | 258 | 259 | 8009 260 | 261 | 8009 262 | 263 | 264 | 265 | c0:s->8009:n 266 | 267 | 268 | 269 | 270 | 271 | 8059 272 | 273 | 8059 274 | 275 | 276 | 277 | 140:s->8059:n 278 | 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /doc/demo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | N80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | N40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | N80:s->N40:n 91 | 92 | 93 | 94 | 95 | 96 | N100 97 | 98 | 00000000a0008000 / 1 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | N80:s->N100:n 135 | 136 | 137 | 138 | 139 | 140 | 56 141 | 142 | 56 143 | 144 | 145 | 146 | N40:s->56:n 147 | 148 | 149 | 150 | 151 | 152 | 57 153 | 154 | 57 155 | 156 | 157 | 158 | N40:s->57:n 159 | 160 | 161 | 162 | 163 | 164 | Nc0 165 | 166 | 00000000a0008000 / 0 167 | 168 | 0 169 | 170 | 1 171 | 172 | 2 173 | 174 | 3 175 | 176 | 4 177 | 178 | 5 179 | 180 | 6 181 | 182 | 7 183 | 184 | 8 185 | 186 | 9 187 | 188 | A 189 | 190 | B 191 | 192 | C 193 | 194 | D 195 | 196 | E 197 | 198 | F 199 | 200 | 201 | 202 | N100:s->Nc0:n 203 | 204 | 205 | 206 | 207 | 208 | N140 209 | 210 | 00000000a0008050 / 0 211 | 212 | 0 213 | 214 | 1 215 | 216 | 2 217 | 218 | 3 219 | 220 | 4 221 | 222 | 5 223 | 224 | 6 225 | 226 | 7 227 | 228 | 8 229 | 230 | 9 231 | 232 | A 233 | 234 | B 235 | 236 | C 237 | 238 | D 239 | 240 | E 241 | 242 | F 243 | 244 | 245 | 246 | N100:s->N140:n 247 | 248 | 249 | 250 | 251 | 252 | N180 253 | 254 | 00000000a0008060 / 0 255 | 256 | 0 257 | 258 | 1 259 | 260 | 2 261 | 262 | 3 263 | 264 | 4 265 | 266 | 5 267 | 268 | 6 269 | 270 | 7 271 | 272 | 8 273 | 274 | 9 275 | 276 | A 277 | 278 | B 279 | 280 | C 281 | 282 | D 283 | 284 | E 285 | 286 | F 287 | 288 | 289 | 290 | N100:s->N180:n 291 | 292 | 293 | 294 | 295 | 296 | 8009 297 | 298 | 8009 299 | 300 | 301 | 302 | Nc0:s->8009:n 303 | 304 | 305 | 306 | 307 | 308 | 8059 309 | 310 | 8059 311 | 312 | 313 | 314 | N140:s->8059:n 315 | 316 | 317 | 318 | 319 | 320 | 8069 321 | 322 | 8069 323 | 324 | 325 | 326 | N180:s->8069:n 327 | 328 | 329 | 330 | 331 | 332 | -------------------------------------------------------------------------------- /doc/remove1.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | 80 15 | 16 | 00000000a0000000 / 3 17 | 18 | 0 19 | 20 | 1 21 | 22 | 2 23 | 24 | 3 25 | 26 | 4 27 | 28 | 5 29 | 30 | 6 31 | 32 | 7 33 | 34 | 8 35 | 36 | 9 37 | 38 | A 39 | 40 | B 41 | 42 | C 43 | 44 | D 45 | 46 | E 47 | 48 | F 49 | 50 | 51 | 52 | 40 53 | 54 | 00000000a0000050 / 0 55 | 56 | 0 57 | 58 | 1 59 | 60 | 2 61 | 62 | 3 63 | 64 | 4 65 | 66 | 5 67 | 68 | 6 69 | 70 | 7 71 | 72 | 8 73 | 74 | 9 75 | 76 | A 77 | 78 | B 79 | 80 | C 81 | 82 | D 83 | 84 | E 85 | 86 | F 87 | 88 | 89 | 90 | 80:s->40:n 91 | 92 | 93 | 94 | 95 | 96 | 100 97 | 98 | 00000000a0008000 / 1 99 | 100 | 0 101 | 102 | 1 103 | 104 | 2 105 | 106 | 3 107 | 108 | 4 109 | 110 | 5 111 | 112 | 6 113 | 114 | 7 115 | 116 | 8 117 | 118 | 9 119 | 120 | A 121 | 122 | B 123 | 124 | C 125 | 126 | D 127 | 128 | E 129 | 130 | F 131 | 132 | 133 | 134 | 80:s->100:n 135 | 136 | 137 | 138 | 139 | 140 | 56 141 | 142 | 56 143 | 144 | 145 | 146 | 40:s->56:n 147 | 148 | 149 | 150 | 151 | 152 | 57 153 | 154 | 57 155 | 156 | 157 | 158 | 40:s->57:n 159 | 160 | 161 | 162 | 163 | 164 | c0 165 | 166 | 00000000a0008000 / 0 167 | 168 | 0 169 | 170 | 1 171 | 172 | 2 173 | 174 | 3 175 | 176 | 4 177 | 178 | 5 179 | 180 | 6 181 | 182 | 7 183 | 184 | 8 185 | 186 | 9 187 | 188 | A 189 | 190 | B 191 | 192 | C 193 | 194 | D 195 | 196 | E 197 | 198 | F 199 | 200 | 201 | 202 | 100:s->c0:n 203 | 204 | 205 | 206 | 207 | 208 | 140 209 | 210 | 00000000a0008050 / 0 211 | 212 | 0 213 | 214 | 1 215 | 216 | 2 217 | 218 | 3 219 | 220 | 4 221 | 222 | 5 223 | 224 | 6 225 | 226 | 7 227 | 228 | 8 229 | 230 | 9 231 | 232 | A 233 | 234 | B 235 | 236 | C 237 | 238 | D 239 | 240 | E 241 | 242 | F 243 | 244 | 245 | 246 | 100:s->140:n 247 | 248 | 249 | 250 | 251 | 252 | 180 253 | 254 | 00000000a0008060 / 0 255 | 256 | 0 257 | 258 | 1 259 | 260 | 2 261 | 262 | 3 263 | 264 | 4 265 | 266 | 5 267 | 268 | 6 269 | 270 | 7 271 | 272 | 8 273 | 274 | 9 275 | 276 | A 277 | 278 | B 279 | 280 | C 281 | 282 | D 283 | 284 | E 285 | 286 | F 287 | 288 | 289 | 290 | 100:s->180:n 291 | 292 | 293 | x 294 | 295 | 296 | 297 | 8009 298 | 299 | 8009 300 | 301 | 302 | 303 | c0:s->8009:n 304 | 305 | 306 | 307 | 308 | 309 | 8059 310 | 311 | 8059 312 | 313 | 314 | 315 | 140:s->8059:n 316 | 317 | 318 | 319 | 320 | 321 | 8069 322 | 323 | 8069 324 | 325 | 326 | 327 | 180:s->8069:n 328 | 329 | 330 | x 331 | 332 | 333 | 334 | --------------------------------------------------------------------------------