aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2020-12-01 13:33:34 +0100
committerToni Uhlig <matzeton@googlemail.com>2020-12-01 13:33:34 +0100
commitc8bf38e5fb717d40635a2a89b22ed71b0de4266b (patch)
tree63751b2f5497c6f99e1c6a78f23a8e6e5c49833f /tests
Squashed 'dependencies/uthash/' content from commit 8e67ced
git-subtree-dir: dependencies/uthash git-subtree-split: 8e67ced1d1c5bd8141c542a22630e6de78aa6b90
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile120
-rw-r--r--tests/README127
-rwxr-xr-xtests/all_funcs13
-rw-r--r--tests/bloom_perf.c82
-rwxr-xr-xtests/bloom_perf.sh17
-rwxr-xr-xtests/do_tests21
-rwxr-xr-xtests/do_tests.cygwin22
-rw-r--r--tests/do_tests.mingw20
-rw-r--r--tests/do_tests_win32.cmd16
-rw-r--r--tests/emit_keys.c48
-rw-r--r--tests/example.c149
-rw-r--r--tests/hashscan.c678
-rw-r--r--tests/keystat.c255
-rwxr-xr-xtests/keystats40
-rw-r--r--tests/lru_cache/Makefile21
-rw-r--r--tests/lru_cache/cache.c221
-rw-r--r--tests/lru_cache/cache.h31
-rw-r--r--tests/lru_cache/main.c191
-rwxr-xr-xtests/simkeys.pl28
-rw-r--r--tests/sleep_test.c32
-rw-r--r--tests/tdiff.cpp34
-rw-r--r--tests/test1.ans10
-rw-r--r--tests/test1.c31
-rw-r--r--tests/test10.ans4
-rw-r--r--tests/test10.c51
-rw-r--r--tests/test11.ans51
-rw-r--r--tests/test11.c57
-rw-r--r--tests/test11.dat51
-rw-r--r--tests/test12.ans20
-rw-r--r--tests/test12.c40
-rw-r--r--tests/test13.ans5
-rw-r--r--tests/test13.c50
-rw-r--r--tests/test14.ans1
-rw-r--r--tests/test14.c54
-rw-r--r--tests/test14.dat1219
-rw-r--r--tests/test15.ans1
-rw-r--r--tests/test15.c40
-rw-r--r--tests/test16.ans1
-rw-r--r--tests/test16.c53
-rw-r--r--tests/test17.ans134
-rw-r--r--tests/test17.c63
-rw-r--r--tests/test18.ans20
-rw-r--r--tests/test18.c37
-rw-r--r--tests/test19.ans1012
-rw-r--r--tests/test19.c66
-rw-r--r--tests/test2.ans5
-rw-r--r--tests/test2.c38
-rw-r--r--tests/test20.ans1
-rw-r--r--tests/test20.c34
-rw-r--r--tests/test21.ans1
-rw-r--r--tests/test21.c44
-rw-r--r--tests/test22.ans1
-rw-r--r--tests/test22.c68
-rw-r--r--tests/test23.ans6
-rw-r--r--tests/test23.c69
-rw-r--r--tests/test24.ans1
-rw-r--r--tests/test24.c29
-rw-r--r--tests/test25.ans31
-rw-r--r--tests/test25.c138
-rw-r--r--tests/test26.ans53
-rw-r--r--tests/test26.c61
-rw-r--r--tests/test27.ans28
-rw-r--r--tests/test27.c130
-rw-r--r--tests/test28.ans34
-rw-r--r--tests/test28.c153
-rw-r--r--tests/test29.ans103
-rw-r--r--tests/test29.c57
-rw-r--r--tests/test3.ans5
-rw-r--r--tests/test3.c43
-rw-r--r--tests/test30.ans51
-rw-r--r--tests/test30.c50
-rw-r--r--tests/test31.ans51
-rw-r--r--tests/test31.c50
-rw-r--r--tests/test32.ans51
-rw-r--r--tests/test32.c43
-rw-r--r--tests/test33.ans51
-rw-r--r--tests/test33.c50
-rw-r--r--tests/test34.ans51
-rw-r--r--tests/test34.c43
-rw-r--r--tests/test35.ans30
-rw-r--r--tests/test35.c37
-rw-r--r--tests/test36.ans25
-rw-r--r--tests/test36.c60
-rw-r--r--tests/test37.ans20
-rw-r--r--tests/test37.c56
-rw-r--r--tests/test38.ans1
-rw-r--r--tests/test38.c31
-rw-r--r--tests/test39.ans7
-rw-r--r--tests/test39.c34
-rw-r--r--tests/test4.ans10
-rw-r--r--tests/test4.c33
-rw-r--r--tests/test40.ans1
-rw-r--r--tests/test40.c40
-rw-r--r--tests/test41.ans6
-rw-r--r--tests/test41.c72
-rw-r--r--tests/test42.ans14
-rw-r--r--tests/test42.c90
-rw-r--r--tests/test43.ans88
-rw-r--r--tests/test43.c130
-rw-r--r--tests/test44.ans9
-rw-r--r--tests/test44.c66
-rw-r--r--tests/test45.ans3
-rw-r--r--tests/test45.c36
-rw-r--r--tests/test46.ans11
-rw-r--r--tests/test46.c84
-rw-r--r--tests/test47.ans8
-rw-r--r--tests/test47.c29
-rw-r--r--tests/test48.ans10
-rw-r--r--tests/test48.c23
-rw-r--r--tests/test49.ans2
-rw-r--r--tests/test49.c23
-rw-r--r--tests/test5.ans5
-rw-r--r--tests/test5.c40
-rw-r--r--tests/test50.ans2
-rw-r--r--tests/test50.c23
-rw-r--r--tests/test51.ans2
-rw-r--r--tests/test51.c32
-rw-r--r--tests/test52.ans2
-rw-r--r--tests/test52.c48
-rw-r--r--tests/test53.ans1
-rw-r--r--tests/test53.c14
-rw-r--r--tests/test54.ans2
-rw-r--r--tests/test54.c24
-rw-r--r--tests/test55.ans2
-rw-r--r--tests/test55.c19
-rw-r--r--tests/test56.ans65
-rw-r--r--tests/test56.c98
-rw-r--r--tests/test57.ans1
-rw-r--r--tests/test57.c32
-rw-r--r--tests/test58.ans18
-rw-r--r--tests/test58.c60
-rw-r--r--tests/test59.ans1
-rw-r--r--tests/test59.c57
-rw-r--r--tests/test6.ans0
-rw-r--r--tests/test6.c121
-rw-r--r--tests/test60.ans1
-rw-r--r--tests/test60.c51
-rw-r--r--tests/test61.ans16
-rw-r--r--tests/test61.c53
-rw-r--r--tests/test62.ans0
-rw-r--r--tests/test62.c80
-rw-r--r--tests/test63.ans7
-rw-r--r--tests/test63.c67
-rw-r--r--tests/test64.ans7
-rw-r--r--tests/test64.c67
-rw-r--r--tests/test65.ans4
-rw-r--r--tests/test65.c65
-rw-r--r--tests/test65.dat51
-rw-r--r--tests/test66.ans20
-rw-r--r--tests/test66.c43
-rw-r--r--tests/test67.ans20
-rw-r--r--tests/test67.c30
-rw-r--r--tests/test68.ans10
-rw-r--r--tests/test68.c87
-rw-r--r--tests/test69.ans11
-rw-r--r--tests/test69.c92
-rw-r--r--tests/test7.ans0
-rw-r--r--tests/test7.c44
-rw-r--r--tests/test70.ans10
-rw-r--r--tests/test70.c87
-rw-r--r--tests/test71.ans11
-rw-r--r--tests/test71.c92
-rw-r--r--tests/test72.ans10
-rw-r--r--tests/test72.c87
-rw-r--r--tests/test73.ans11
-rw-r--r--tests/test73.c92
-rw-r--r--tests/test74.ans6
-rw-r--r--tests/test74.c40
-rw-r--r--tests/test75.ans6
-rw-r--r--tests/test75.c40
-rw-r--r--tests/test76.ans6
-rw-r--r--tests/test76.c54
-rw-r--r--tests/test77.ans13
-rw-r--r--tests/test77.c74
-rw-r--r--tests/test78.ans28
-rw-r--r--tests/test78.c130
-rw-r--r--tests/test79.ans8
-rw-r--r--tests/test79.c71
-rw-r--r--tests/test8.ans15
-rw-r--r--tests/test8.c40
-rw-r--r--tests/test80.ans6
-rw-r--r--tests/test80.c29
-rw-r--r--tests/test81.ans6
-rw-r--r--tests/test81.c29
-rw-r--r--tests/test82.ans9
-rw-r--r--tests/test82.c42
-rw-r--r--tests/test83.ans41
-rw-r--r--tests/test83.c61
-rw-r--r--tests/test84.ans41
-rw-r--r--tests/test84.c71
-rw-r--r--tests/test85.ans2
-rw-r--r--tests/test85.c35
-rw-r--r--tests/test86.ans79
-rw-r--r--tests/test86.c296
-rw-r--r--tests/test87.ans78
-rw-r--r--tests/test87.c116
-rw-r--r--tests/test88.ans30
-rw-r--r--tests/test88.c68
-rw-r--r--tests/test89.ans5
-rw-r--r--tests/test89.c65
-rw-r--r--tests/test9.ans500
-rw-r--r--tests/test9.c37
-rw-r--r--tests/test90.ans2
-rw-r--r--tests/test90.c53
-rw-r--r--tests/test91.ans6
-rw-r--r--tests/test91.c54
-rw-r--r--tests/test92.ans1
-rw-r--r--tests/test92.c245
-rw-r--r--tests/test93.ans1
-rw-r--r--tests/test93.c119
-rw-r--r--tests/test94.ans13
-rw-r--r--tests/test94.c93
-rw-r--r--tests/test95.ans0
-rw-r--r--tests/test95.c66
-rw-r--r--tests/threads/Makefile31
-rw-r--r--tests/threads/README2
-rwxr-xr-xtests/threads/do_tests22
-rw-r--r--tests/threads/test1.c116
-rw-r--r--tests/threads/test2.c75
219 files changed, 12921 insertions, 0 deletions
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 000000000..fcb751984
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,120 @@
+#CC=clang
+HASHDIR = ../src
+UTILS = emit_keys
+PROGS = test1 test2 test3 test4 test5 test6 test7 test8 test9 \
+ test10 test11 test12 test13 test14 test15 test16 test17 \
+ test18 test19 test20 test21 test22 test23 test24 test25 \
+ test26 test27 test28 test29 test30 test31 test32 test33 \
+ test34 test35 test36 test37 test38 test39 test40 test41 \
+ test42 test43 test44 test45 test46 test47 test48 test49 \
+ test50 test51 test52 test53 test54 test55 test56 test57 \
+ test58 test59 test60 test61 test62 test63 test64 test65 \
+ test66 test67 test68 test69 test70 test71 test72 test73 \
+ test74 test75 test76 test77 test78 test79 test80 test81 \
+ test82 test83 test84 test85 test86 test87 test88 test89 \
+ test90 test91 test92 test93 test94 test95
+CFLAGS += -I$(HASHDIR)
+#CFLAGS += -DHASH_BLOOM=16
+#CFLAGS += -O2
+CFLAGS += -g
+#CFLAGS += -Wstrict-aliasing=2
+CFLAGS += -Wall
+#CFLAGS += -Wextra
+#CFLAGS += -std=c89
+CFLAGS += ${EXTRA_CFLAGS}
+
+ifeq ($(HASH_DEBUG),1)
+CFLAGS += -DHASH_DEBUG=1
+endif
+
+ifeq ($(HASH_PEDANTIC),1)
+CFLAGS += -pedantic
+endif
+
+TEST_TARGET=run_tests
+TESTS=./do_tests
+
+# detect Cygwin
+ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "cygwin")),)
+ TESTS=./do_tests.cygwin
+endif
+
+# detect MinGW
+ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "mingw")),)
+ TEST_TARGET=run_tests_mingw
+ TESTS=./do_tests.mingw
+endif
+
+#detect Linux (platform specific utilities)
+ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "linux")),)
+ PLAT_UTILS = hashscan sleep_test
+endif
+
+#detect FreeBSD (platform specific utilities)
+ifeq ($(strip $(shell uname -s)), FreeBSD)
+ ifeq ($(shell if [ `sysctl -n kern.osreldate` -ge 0801000 ]; then echo "ok"; fi), ok)
+ PLAT_UTILS = hashscan sleep_test
+ endif
+endif
+
+all: $(PROGS) $(UTILS) $(PLAT_UTILS) keystat $(TEST_TARGET)
+
+tests_only: $(PROGS) $(TEST_TARGET)
+
+GITIGN = .gitignore
+MKGITIGN = [ -f "$(GITIGN)" ] || echo "$(GITIGN)" > $(GITIGN); grep -q '^\$@$$' $(GITIGN) || echo "$@" >> $(GITIGN)
+
+debug:
+ $(MAKE) all HASH_DEBUG=1
+
+pedantic:
+ $(MAKE) all HASH_PEDANTIC=1
+
+cplusplus:
+ CC="$(CXX) -x c++" $(MAKE) all
+
+thorough:
+ $(MAKE) clean && $(MAKE) all EXTRA_CFLAGS='-pedantic'
+ $(MAKE) clean && $(MAKE) all EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16'
+ $(MAKE) clean && $(MAKE) tests_only EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16 -DHASH_DEBUG -DNO_DECLTYPE'
+ $(MAKE) clean && CC="$(CXX) -x c++" $(MAKE) all EXTRA_CFLAGS='-pedantic'
+ $(MAKE) clean && CC="$(CXX) -x c++" $(MAKE) all EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16'
+ $(MAKE) clean && CC="$(CXX) -x c++" $(MAKE) tests_only EXTRA_CFLAGS='-pedantic -DHASH_BLOOM=16 -DHASH_DEBUG -DNO_DECLTYPE'
+
+example: example.c $(HASHDIR)/uthash.h
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
+
+$(PROGS) $(UTILS) : $(HASHDIR)/uthash.h
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
+ @$(MKGITIGN)
+
+hashscan : $(HASHDIR)/uthash.h
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
+ @$(MKGITIGN)
+
+sleep_test : $(HASHDIR)/uthash.h
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_BLOOM=16 $(LDFLAGS) -o $@ $(@).c
+ @$(MKGITIGN)
+
+keystat : $(HASHDIR)/uthash.h
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_BER $(LDFLAGS) -o keystat.BER keystat.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_FNV $(LDFLAGS) -o keystat.FNV keystat.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_JEN $(LDFLAGS) -o keystat.JEN keystat.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_OAT $(LDFLAGS) -o keystat.OAT keystat.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_SAX $(LDFLAGS) -o keystat.SAX keystat.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_SFH $(LDFLAGS) -o keystat.SFH keystat.c
+
+run_tests: $(PROGS)
+ perl $(TESTS)
+
+run_tests_mingw: $(PROGS)
+ /bin/bash do_tests.mingw
+
+astyle:
+ astyle -n --style=kr --indent-switches --add-brackets *.c
+
+.PHONY: clean astyle
+
+clean:
+ rm -f $(UTILS) $(PLAT_UTILS) $(PROGS) test*.out keystat.??? example hashscan sleep_test *.exe $(GITIGN)
+ rm -rf *.dSYM
diff --git a/tests/README b/tests/README
new file mode 100644
index 000000000..fc25b9b41
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,127 @@
+Automated tests for uthash
+==============================================================================
+Run "make" in this directory to build the tests and run them.
+
+test1: make 10-item hash, iterate and print each one
+test2: make 10-item hash, lookup items with even keys, print
+test3: make 10-item hash, delete items with even keys, print others
+test4: 10 structs have dual hash handles, separate keys
+test5: 10 structs have dual hash handles, lookup evens by alt key
+test6: test alt malloc macros (and alt memcmp macro)
+test7: test alt malloc macros with 1000 structs so bucket expansion occurs
+test8: test num_items counter in UT_hash_handle
+test9: test "find" after bucket expansion
+test10: dual-hash handle test, bucket expansion on one and not the other
+test11: read dat file of names into hash, sort them and print
+test12: create hash with string keys, add 10 items, lookup each item
+test13: make 10-item hash, delete items with even keys, reverse print others
+test14: read dat file of names into hash, read file again and lookup each one
+test15: build string-keyed hash of 3 items, lookup one item (c.f. test40.c)
+test16: hash on aggregate key, iterate, lookup, using generalized macros
+test17: sort, add more items, sort again
+test18: test pathological HASH_DEL(a,a) scenario (single head,deletee variable)
+test19: sort two hash tables with shared elements using HASH_SRT
+test20: test a 5-byte "binary" key
+test21: test a structure key (userguide)
+test22: test multi-field key using flexible array member (userguide utf32)
+test23: test whether delete in iteration works
+test24: make 10-item hash and confirm item count (HASH_COUNT)
+test25: CDL / DL / LL tests
+test26: test the linked list sort macros in utlist.h
+test27: LL_APPEND, SORT
+test28: CDL / DL / LL tests
+test29: DL_APPEND, SORT
+test30: CDL_PREPEND, SORT
+test31: CDL_PREPEND, SORT
+test32: DL_PREPEND
+test33: LL_PREPEND
+test34: CDL_PREPEND
+test35: CDL_PREPEND
+test36: HASH_SELECT
+test37: HASH_CLEAR
+test38: find-or-add test on integer keys in short loop
+test39: HASH_ADD_KEYPTR then HASH_FIND using array element as key pointer
+test40: HASH_ADD_KEYPTR on string keys; pointer equivalent to test15.c
+test41: test LL_FOREACH_SAFE,DL_FOREACH_SAFE,CDL_FOREACH_SAFE
+test42: test LL_SEARCH, LL_SEARCH_SCALAR, and DL and CDL counterparts
+test43: test utarray with intpair objects
+test44: test utarray with int objects
+test45: test utarray with int objects
+test46: test utarray with char* objects
+test47: test utstring
+test48: test utarray of int
+test49: test utarray of str
+test50: test utarray of long
+test51: test utarray of intpair
+test52: test utarray of intchar
+test53: test utstring
+test54: test utstring
+test55: test utstring
+test56: test uthash, utlist and utstring together for #define conflicts etc
+test57: test uthash HASH_ADD_PTR and HASH_FIND_PTR
+test58: test HASH_ITER macro
+test59: sample of multi-level hash
+test60: sample of multi-level hash that also does HASH_DEL and free
+test61: test utarray_find
+test62: test macros used in safe unaligned reads on non-Intel type platforms
+test63: LL_CONCAT test
+test64: DL_CONCAT test
+test65: LRU cache example courtesy of jehiah.cz with modifications
+test66: test example where output variable to HASH_FIND needs extra parens
+test67: test utarray_prev
+test68: test DL_REPLACE_ELEM (Zoltán Lajos Kis)
+test69: test DL_PREPEND_ELEM (Zoltán Lajos Kis)
+test70: test LL_REPLACE_ELEM (Zoltán Lajos Kis)
+test71: test LL_PREPEND_ELEM (Zoltán Lajos Kis)
+test72: test CDL_REPLACE_ELEM (Zoltán Lajos Kis)
+test73: test CDL_PREPEND_ELEM (Zoltán Lajos Kis)
+test74: test utstring with utstring_find (Joe Wei)
+test75: test utstring with utstring_findR (Joe Wei)
+test76: test utstring with _utstring_find (Joe Wei)
+test77: test utstring with _utstring_findR (Joe Wei)
+test78: test utlist "2" family with flexible Prev/Next naming eg. DL_DELETE2
+test79: test HASH_REPLACE
+test80: test utarray_insert past end of array
+test81: test utarray_insert past end of array
+test82: test utarray_inserta past end of array
+test83: test HASH_REPLACE_STR with char[] key
+test84: test HASH_REPLACE_STR with char* key
+test85: test HASH_OVERHEAD on null and non null hash
+test86: test *_APPEND_ELEM / *_PREPEND_ELEM (Thilo Schulz)
+test87: test HASH_ADD_INORDER() macro (Thilo Schulz)
+test88: test alt memcmp and strlen macros
+test89: test code from the tinydtls project
+test90: regression-test HASH_ADD_KEYPTR_INORDER (IronBug)
+test91: test LL_INSERT_INORDER etc.
+
+Other Make targets
+================================================================================
+pedantic: makes the tests with extra CFLAGS for pedantic compiling
+cplusplus: compiles all the C tests using the C++ compiler to test compatibility
+debug: makes the tests with debugging symbols and no optimization
+example: builds the 'example' program from the user guide
+================================================================================
+
+Testing a specific hash function
+--------------------------------
+Set EXTRA_CFLAGS with this Makefile to use a specific hash function:
+ EXTRA_CFLAGS=-DHASH_FUNCTION=HASH_BER make
+
+Other files
+================================================================================
+keystats: key statistics analyzer. See the uthash User Guide.
+emit_keys: reads a data file of unique strings, emits as keys w/HASH_EMIT_KEYS=1
+all_funcs: a script which executes the test suite with every hash function
+win32tests:builds and runs the test suite under Microsoft Visual Studio
+
+LINUX/FREEBSD
+-------------
+hashscan: tool to examine a running process and get info on its hash tables
+test_sleep:used as a subject for inspection by hashscan
+
+Manual performance testing
+================================================================================
+ # test performance characteristics on keys that are English dictionary words
+ emit_keys /usr/share/dict/words > words.keys
+ ./keystats words.keys
+
diff --git a/tests/all_funcs b/tests/all_funcs
new file mode 100755
index 000000000..f61a410c6
--- /dev/null
+++ b/tests/all_funcs
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+function proceed {
+ read -p "proceed ? [n] " response
+ if [ "$response" != "y" ]; then exit -1; fi
+}
+
+make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_BER'; proceed
+make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_FNV'; proceed
+make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_JEN'; proceed
+make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_OAT'; proceed
+make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_SAX'; proceed
+make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_SFH'; proceed
diff --git a/tests/bloom_perf.c b/tests/bloom_perf.c
new file mode 100644
index 000000000..6a9abf19f
--- /dev/null
+++ b/tests/bloom_perf.c
@@ -0,0 +1,82 @@
+#include <stdlib.h> /* malloc */
+#include <sys/time.h> /* gettimeofday */
+#include <errno.h> /* perror */
+#include <stdio.h> /* printf */
+#include "uthash.h"
+
+#define BUFLEN 20
+#if 0
+#undef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets)
+#endif
+
+typedef struct name_rec {
+ char boy_name[BUFLEN];
+ UT_hash_handle hh;
+} name_rec;
+
+int main(int argc,char *argv[])
+{
+ name_rec *name, *names=NULL;
+ char linebuf[BUFLEN];
+ FILE *file;
+ int i=0,j,nloops=3,loopnum=0,miss;
+ struct timeval tv1,tv2;
+ long elapsed_usec;
+ if (argc > 1) {
+ nloops = atoi(argv[1]);
+ }
+
+ if ( (file = fopen( "test14.dat", "r" )) == NULL ) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ i++;
+ if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) {
+ exit(-1);
+ }
+ strcpy(name->boy_name, linebuf);
+ HASH_ADD_STR(names,boy_name,name);
+ }
+
+again:
+ if (fseek(file,0,SEEK_SET) == -1) {
+ fprintf(stderr,"fseek failed: %s\n", strerror(errno));
+ }
+ j=0;
+
+ if (gettimeofday(&tv1,NULL) == -1) {
+ perror("gettimeofday: ");
+ }
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ /* if we do 10 loops, the first has a 0% miss rate,
+ * the second has a 10% miss rate, etc */
+ miss = ((rand()*1.0/RAND_MAX) < (loopnum*1.0/nloops)) ? 1 : 0;
+ /* generate a miss if we want one */
+ if (miss) {
+ linebuf[0]++;
+ if (linebuf[1] != '\0') {
+ linebuf[1]++;
+ }
+ }
+ HASH_FIND_STR(names,linebuf,name);
+ if (name) {
+ j++;
+ }
+ }
+ if (gettimeofday(&tv2,NULL) == -1) {
+ perror("gettimeofday: ");
+ }
+ elapsed_usec = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
+ printf("lookup on %d of %d (%.2f%%) names succeeded (%.2f usec)\n", j, i,
+ j*100.0/i, (double)(elapsed_usec));
+ if (++loopnum < nloops) {
+ goto again;
+ }
+ fclose(file);
+
+ return 0;
+}
+
diff --git a/tests/bloom_perf.sh b/tests/bloom_perf.sh
new file mode 100755
index 000000000..0a04f230a
--- /dev/null
+++ b/tests/bloom_perf.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+BITS="16"
+
+cc -I../src -O3 -Wall -m64 bloom_perf.c -o bloom_perf.none
+for bits in $BITS
+do
+cc -I../src -DHASH_BLOOM=$bits -O3 -Wall -m64 bloom_perf.c -o bloom_perf.$bits
+done
+
+for bits in none $BITS
+do
+echo
+echo "using $bits-bit filter:"
+./bloom_perf.$bits 10
+done
+
diff --git a/tests/do_tests b/tests/do_tests
new file mode 100755
index 000000000..574403f2d
--- /dev/null
+++ b/tests/do_tests
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "test*[0-9]") {
+ push @tests, $_ if -e "$_.ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out`;
+ `diff $test.out $test.ans`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
+exit $num_failed;
diff --git a/tests/do_tests.cygwin b/tests/do_tests.cygwin
new file mode 100755
index 000000000..0c01ebee0
--- /dev/null
+++ b/tests/do_tests.cygwin
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "test*[0-9].exe") {
+ push @tests, "$_" if -e substr($_, 0, - 4).".ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out`;
+ my $ansfile = substr($test, 0, - 4).".ans";
+ `diff $test.out $ansfile`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
+exit $num_failed;
diff --git a/tests/do_tests.mingw b/tests/do_tests.mingw
new file mode 100644
index 000000000..4dcf49834
--- /dev/null
+++ b/tests/do_tests.mingw
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+echo "MinGW test script starting"
+
+for f in test*.exe
+do
+ t=`echo $f | sed s/.exe//`
+ "./$f" > "$t.out"
+ diff -qb "$t.out" "$t.ans"
+ if [ $? -eq 1 ]
+ then
+ echo "$f failed"
+ else
+ true # can't have empty else
+ #echo "$f passed"
+ fi
+done
+
+echo
+echo "All tests complete"
diff --git a/tests/do_tests_win32.cmd b/tests/do_tests_win32.cmd
new file mode 100644
index 000000000..be8930170
--- /dev/null
+++ b/tests/do_tests_win32.cmd
@@ -0,0 +1,16 @@
+:: this compiles and runs the test suite under Visual Studio 2008
+::@echo off
+call "C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat" > vc.out
+::call "C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat" > vc.out
+set "COMPILE=cl.exe /I ..\src /EHsc /nologo"
+echo compiling...
+%COMPILE% tdiff.cpp > compile.out
+::for %%f in (test*.c) do %COMPILE% /Tp %%f >> compile.out
+for %%f in (test*.c) do %COMPILE% /Tc %%f >> compile.out
+echo running tests...
+for %%f in (test*.exe) do %%f > %%~nf.out
+echo scanning for failures...
+for %%f in (test*.out) do tdiff %%f %%~nf.ans
+echo tests completed
+::for %%f in (test*.out test*.obj test*.exe vc.out compile.out tdiff.obj tdiff.exe) do del %%f
+pause
diff --git a/tests/emit_keys.c b/tests/emit_keys.c
new file mode 100644
index 000000000..0b98689eb
--- /dev/null
+++ b/tests/emit_keys.c
@@ -0,0 +1,48 @@
+#include <stdlib.h> /* malloc */
+#include <errno.h> /* perror */
+#include <stdio.h> /* printf */
+#include <unistd.h> /* write */
+
+/* this define must precede uthash.h */
+#define HASH_EMIT_KEYS 1
+#include "uthash.h"
+
+#define BUFLEN 30
+
+typedef struct name_rec {
+ char boy_name[BUFLEN];
+ UT_hash_handle hh;
+} name_rec;
+
+int main(int argc,char *argv[])
+{
+ name_rec *name, *names=NULL;
+ char linebuf[BUFLEN];
+ FILE *file;
+ int i=0;
+
+ if (argc != 2) {
+ fprintf(stderr,"usage: %s file\n", argv[0]);
+ exit(-1);
+ }
+
+ if ( (file = fopen( argv[1], "r" )) == NULL ) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (name_rec*)malloc(sizeof(name_rec));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->boy_name, linebuf);
+ HASH_ADD_STR(names,boy_name,name);
+ i++;
+ }
+
+ fprintf(stderr,"%d keys emitted.\n", i);
+ fclose(file);
+ return 0;
+}
+
diff --git a/tests/example.c b/tests/example.c
new file mode 100644
index 000000000..a85f422d4
--- /dev/null
+++ b/tests/example.c
@@ -0,0 +1,149 @@
+#include <stdio.h> /* gets */
+#include <stdlib.h> /* atoi, malloc */
+#include <string.h> /* strcpy */
+#include "uthash.h"
+
+struct my_struct {
+ int id; /* key */
+ char name[10];
+ UT_hash_handle hh; /* makes this structure hashable */
+};
+
+struct my_struct *users = NULL;
+
+void add_user(int user_id, char *name)
+{
+ struct my_struct *s;
+
+ HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */
+ if (s==NULL) {
+ s = (struct my_struct*)malloc(sizeof(struct my_struct));
+ s->id = user_id;
+ HASH_ADD_INT( users, id, s ); /* id: name of key field */
+ }
+ strcpy(s->name, name);
+}
+
+struct my_struct *find_user(int user_id)
+{
+ struct my_struct *s;
+
+ HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */
+ return s;
+}
+
+void delete_user(struct my_struct *user)
+{
+ HASH_DEL( users, user); /* user: pointer to deletee */
+ free(user);
+}
+
+void delete_all()
+{
+ struct my_struct *current_user, *tmp;
+
+ HASH_ITER(hh, users, current_user, tmp) {
+ HASH_DEL(users,current_user); /* delete it (users advances to next) */
+ free(current_user); /* free it */
+ }
+}
+
+void print_users()
+{
+ struct my_struct *s;
+
+ for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) {
+ printf("user id %d: name %s\n", s->id, s->name);
+ }
+}
+
+int name_sort(struct my_struct *a, struct my_struct *b)
+{
+ return strcmp(a->name,b->name);
+}
+
+int id_sort(struct my_struct *a, struct my_struct *b)
+{
+ return (a->id - b->id);
+}
+
+void sort_by_name()
+{
+ HASH_SORT(users, name_sort);
+}
+
+void sort_by_id()
+{
+ HASH_SORT(users, id_sort);
+}
+
+int main()
+{
+ char in[10];
+ int id=1, running=1;
+ struct my_struct *s;
+ unsigned num_users;
+
+ while (running) {
+ printf(" 1. add user\n");
+ printf(" 2. add/rename user by id\n");
+ printf(" 3. find user\n");
+ printf(" 4. delete user\n");
+ printf(" 5. delete all users\n");
+ printf(" 6. sort items by name\n");
+ printf(" 7. sort items by id\n");
+ printf(" 8. print users\n");
+ printf(" 9. count users\n");
+ printf("10. quit\n");
+ gets(in);
+ switch(atoi(in)) {
+ case 1:
+ printf("name?\n");
+ add_user(id++, gets(in));
+ break;
+ case 2:
+ printf("id?\n");
+ gets(in);
+ id = atoi(in);
+ printf("name?\n");
+ add_user(id, gets(in));
+ break;
+ case 3:
+ printf("id?\n");
+ s = find_user(atoi(gets(in)));
+ printf("user: %s\n", s ? s->name : "unknown");
+ break;
+ case 4:
+ printf("id?\n");
+ s = find_user(atoi(gets(in)));
+ if (s) {
+ delete_user(s);
+ } else {
+ printf("id unknown\n");
+ }
+ break;
+ case 5:
+ delete_all();
+ break;
+ case 6:
+ sort_by_name();
+ break;
+ case 7:
+ sort_by_id();
+ break;
+ case 8:
+ print_users();
+ break;
+ case 9:
+ num_users=HASH_COUNT(users);
+ printf("there are %u users\n", num_users);
+ break;
+ case 10:
+ running=0;
+ break;
+ }
+ }
+
+ delete_all(); /* free any structures */
+ return 0;
+}
diff --git a/tests/hashscan.c b/tests/hashscan.c
new file mode 100644
index 000000000..c487d9cee
--- /dev/null
+++ b/tests/hashscan.c
@@ -0,0 +1,678 @@
+/*
+Copyright (c) 2005-2018, Troy D. Hanson http://troydhanson.github.com/uthash/
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER
+OR 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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/types.h> /* on OSX, must come before ptrace.h */
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <assert.h>
+
+#ifdef __FreeBSD__
+#include <sys/param.h> /* MAXPATHLEN */
+#include <vm/vm.h> /* VM_PROT_* flags */
+#endif
+
+#if defined(PT_ATTACH) && !defined(PTRACE_ATTACH)
+#define PTRACE_ATTACH PT_ATTACH
+#define PTRACE_DETACH PT_DETACH
+#endif
+
+/* need this defined so offsetof can give us bloom offsets in UT_hash_table */
+#define HASH_BLOOM 16
+#include "uthash.h"
+
+#ifdef __FreeBSD__
+typedef struct {
+ void *start;
+ void *end;
+} vma_t;
+#else
+typedef struct {
+ off_t start;
+ off_t end;
+ char perms[4]; /* rwxp */
+ char device[5]; /* fd:01 or 00:00 */
+} vma_t;
+#endif
+
+const uint32_t sig = HASH_SIGNATURE;
+int verbose=0;
+int getkeys=0;
+
+#define vv(...) do {if (verbose>0) printf(__VA_ARGS__);} while(0)
+#define vvv(...) do {if (verbose>1) printf(__VA_ARGS__);} while(0)
+
+/* these id's are arbitrary, only meaningful within this file */
+#define JEN 1
+#define BER 2
+#define SFH 3
+#define SAX 4
+#define FNV 5
+#define OAT 6
+#define NUM_HASH_FUNCS 7 /* includes id 0, the non-function */
+const char *hash_fcns[] = {"???","JEN","BER","SFH","SAX","FNV","OAT"};
+
+/* given a peer key/len/hashv, reverse engineer its hash function */
+static int infer_hash_function(char *key, size_t keylen, uint32_t hashv)
+{
+ uint32_t ohashv;
+ /* BER SAX FNV OAT JEN SFH */
+ HASH_JEN(key,keylen,ohashv);
+ if (ohashv == hashv) {
+ return JEN;
+ }
+ HASH_BER(key,keylen,ohashv);
+ if (ohashv == hashv) {
+ return BER;
+ }
+ HASH_SFH(key,keylen,ohashv);
+ if (ohashv == hashv) {
+ return SFH;
+ }
+ HASH_SAX(key,keylen,ohashv);
+ if (ohashv == hashv) {
+ return SAX;
+ }
+ HASH_FNV(key,keylen,ohashv);
+ if (ohashv == hashv) {
+ return FNV;
+ }
+ HASH_OAT(key,keylen,ohashv);
+ if (ohashv == hashv) {
+ return OAT;
+ }
+ return 0;
+}
+
+/* read peer's memory from addr for len bytes, store into our dst */
+#ifdef __FreeBSD__
+static int read_mem(void *dst, pid_t pid, void *start, size_t len)
+{
+ struct ptrace_io_desc io_desc;
+ int ret;
+
+ io_desc.piod_op = PIOD_READ_D;
+ io_desc.piod_offs = start;
+ io_desc.piod_addr = dst;
+ io_desc.piod_len = len;
+
+ ret = ptrace(PT_IO, pid, (void *) &io_desc, 0);
+
+ if (ret) {
+ vv("read_mem: ptrace failed: %s\n", strerror(errno));
+ return -1;
+ } else if (io_desc.piod_len != len) {
+ vv("read_mem: short read!\n");
+ return -1;
+ }
+
+ return 0;
+}
+#else
+static int read_mem(void *dst, int fd, off_t start, size_t len)
+{
+ int rc;
+ size_t bytes_read=0;
+ if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
+ fprintf(stderr, "lseek failed: %s\n", strerror(errno));
+ return -1;
+ }
+ while ( len && ((rc=read(fd, (char*)dst+bytes_read, len)) > 0)) {
+ len -= rc;
+ bytes_read += rc;
+ }
+ if (rc==-1) {
+ vv("read_mem failed (%s)\n",strerror(errno));
+ }
+ if ((len != 0 && rc >= 0)) {
+ vv("INTERNAL ERROR\n");
+ }
+ return (rc == -1) ? -1 : 0;
+}
+#endif
+
+/* later compensate for possible presence of bloom filter */
+static char *tbl_from_sig_addr(char *sig)
+{
+ return (sig - offsetof(UT_hash_table,signature));
+}
+
+#define HS_BIT_TEST(v,i) (v[i/8] & (1U << (i%8)))
+static void found(int fd, char* peer_sig, pid_t pid)
+{
+ UT_hash_table *tbl=NULL;
+ UT_hash_bucket *bkts=NULL;
+ UT_hash_handle hh;
+ size_t i, bloom_len, bloom_bitlen, bloom_on_bits=0,bloom_off_bits=0;
+ char *peer_tbl, *peer_bloom_sig, *peer_bloom_nbits, *peer_bloombv_ptr,
+ *peer_bloombv, *peer_bkts, *peer_hh, *key=NULL;
+ const char *peer_key;
+ const char *hash_fcn = NULL;
+ unsigned char *bloombv=NULL;
+ static int fileno=0;
+ char keyfile[50];
+ unsigned char bloom_nbits=0;
+ int keyfd=-1, mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
+ hash_fcn_hits[NUM_HASH_FUNCS], hash_fcn_winner;
+ unsigned max_chain=0;
+ uint32_t bloomsig;
+ int has_bloom_filter_fields = 0;
+
+ for(i=0; i < NUM_HASH_FUNCS; i++) {
+ hash_fcn_hits[i]=0;
+ }
+
+ if (getkeys) {
+ snprintf(keyfile, sizeof(keyfile), "/tmp/%u-%u.key", (unsigned)pid,fileno++);
+ if ( (keyfd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
+ fprintf(stderr, "can't open %s: %s\n", keyfile, strerror(errno));
+ exit(-1);
+ }
+ }
+
+ vv("found signature at peer %p\n", (void*)peer_sig);
+ peer_tbl = tbl_from_sig_addr(peer_sig);
+ vvv("reading table at peer %p\n", (void*)peer_tbl);
+
+ if ( (tbl = (UT_hash_table*)malloc(sizeof(UT_hash_table))) == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(-1);
+ }
+#ifdef __FreeBSD__
+ if (read_mem(tbl, pid, (void *)peer_tbl, sizeof(UT_hash_table)) != 0) {
+#else
+ if (read_mem(tbl, fd, (off_t)peer_tbl, sizeof(UT_hash_table)) != 0) {
+#endif
+ fprintf(stderr, "failed to read peer memory\n");
+ goto done;
+ }
+
+ /* got the table. how about the buckets */
+ peer_bkts = (char*)tbl->buckets;
+ vvv("reading %u buckets at peer %p\n", tbl->num_buckets, (void*)peer_bkts);
+ bkts = (UT_hash_bucket*)malloc(sizeof(UT_hash_bucket)*tbl->num_buckets);
+ if (bkts == NULL) {
+ fprintf(stderr, "out of memory\n");
+ goto done;
+ }
+#ifdef __FreeBSD__
+ if (read_mem(bkts, pid, (void *)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
+#else
+ if (read_mem(bkts, fd, (off_t)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
+#endif
+ fprintf(stderr, "failed to read peer memory\n");
+ goto done;
+ }
+
+ vvv("scanning %u peer buckets\n", tbl->num_buckets);
+ for(i=0; i < tbl->num_buckets; i++) {
+ vvv("bucket %u has %u items\n", (unsigned)i, (unsigned)(bkts[i].count));
+ if (bkts[i].count > max_chain) {
+ max_chain = bkts[i].count;
+ }
+ if (bkts[i].expand_mult) {
+ vvv(" bucket %u has expand_mult %u\n", (unsigned)i, (unsigned)(bkts[i].expand_mult));
+ }
+
+ vvv("scanning bucket %u chain:\n", (unsigned)i);
+ peer_hh = (char*)bkts[i].hh_head;
+ while(peer_hh) {
+#ifdef __FreeBSD__
+ if (read_mem(&hh, pid, (void *)peer_hh, sizeof(hh)) != 0) {
+#else
+ if (read_mem(&hh, fd, (off_t)peer_hh, sizeof(hh)) != 0) {
+#endif
+ fprintf(stderr, "failed to read peer memory\n");
+ goto done;
+ }
+ if ((char*)hh.tbl != peer_tbl) {
+ goto done;
+ }
+ peer_hh = (char*)hh.hh_next;
+ peer_key = (const char*)(hh.key);
+ /* malloc space to read the key, and read it */
+ if ( (key = (char*)malloc(sizeof(hh.keylen))) == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(-1);
+ }
+#ifdef __FreeBSD__
+ if (read_mem(key, pid, (void*)peer_key, hh.keylen) != 0) {
+#else
+ if (read_mem(key, fd, (off_t)peer_key, hh.keylen) != 0) {
+#endif
+ fprintf(stderr, "failed to read peer memory\n");
+ goto done;
+ }
+ hash_fcn_hits[infer_hash_function(key,hh.keylen,hh.hashv)]++;
+ /* write the key if requested */
+ if (getkeys) {
+ write(keyfd, &hh.keylen, sizeof(unsigned));
+ write(keyfd, key, hh.keylen);
+ }
+ free(key);
+ key=NULL;
+ }
+ }
+
+ /* does it have a bloom filter? */
+ peer_bloom_sig = peer_tbl + offsetof(UT_hash_table, bloom_sig);
+ peer_bloombv_ptr = peer_tbl + offsetof(UT_hash_table, bloom_bv);
+ peer_bloom_nbits = peer_tbl + offsetof(UT_hash_table, bloom_nbits);
+ vvv("looking for bloom signature at peer %p\n", (void*)peer_bloom_sig);
+#ifdef __FreeBSD__
+ if ((read_mem(&bloomsig, pid, (void *)peer_bloom_sig, sizeof(uint32_t)) == 0) &&
+ (bloomsig == HASH_BLOOM_SIGNATURE)) {
+#else
+ if ((read_mem(&bloomsig, fd, (off_t)peer_bloom_sig, sizeof(uint32_t)) == 0) &&
+ (bloomsig == HASH_BLOOM_SIGNATURE)) {
+#endif
+ vvv("bloom signature (%x) found\n",bloomsig);
+ /* bloom found. get at bv, nbits */
+#ifdef __FreeBSD__
+ if (read_mem(&bloom_nbits, pid, (void *)peer_bloom_nbits, sizeof(char)) == 0) {
+#else
+ if (read_mem(&bloom_nbits, fd, (off_t)peer_bloom_nbits, sizeof(char)) == 0) {
+#endif
+ /* scan bloom filter, calculate saturation */
+ bloom_bitlen = (1ULL << bloom_nbits);
+ bloom_len = (bloom_bitlen / 8) + ((bloom_bitlen % 8) ? 1 : 0);
+ vvv("bloom bitlen is %u, bloom_bytelen is %u\n", (unsigned)bloom_bitlen, (unsigned)bloom_len);
+ if ( (bloombv = (unsigned char*)malloc(bloom_len)) == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(-1);
+ }
+ /* read the address of the bitvector in the peer, then read the bv itself */
+#ifdef __FreeBSD__
+ if ((read_mem(&peer_bloombv, pid, (void *)peer_bloombv_ptr, sizeof(void*)) == 0) &&
+ (read_mem(bloombv, pid, (void *)peer_bloombv, bloom_len) == 0)) {
+#else
+ if ((read_mem(&peer_bloombv, fd, (off_t)peer_bloombv_ptr, sizeof(void*)) == 0) &&
+ (read_mem(bloombv, fd, (off_t)peer_bloombv, bloom_len) == 0)) {
+#endif
+ /* calculate saturation */
+ vvv("read peer bloom bitvector from %p (%u bytes)\n", (void*)peer_bloombv, (unsigned)bloom_len);
+ for(i=0; i < bloom_bitlen; i++) {
+ if (HS_BIT_TEST(bloombv,(unsigned)i)) {
+ /* vvv("bit %u set\n",(unsigned)i); */
+ bloom_on_bits++;
+ } else {
+ bloom_off_bits++;
+ }
+ }
+ has_bloom_filter_fields = 1;
+ vvv("there were %u on_bits among %u total bits\n", (unsigned)bloom_on_bits, (unsigned)bloom_bitlen);
+ }
+ }
+ }
+
+ /* choose apparent hash function */
+ hash_fcn_winner=0;
+ for(i=0; i<NUM_HASH_FUNCS; i++) {
+ if (hash_fcn_hits[i] > hash_fcn_hits[hash_fcn_winner]) {
+ hash_fcn_winner=i;
+ }
+ }
+ hash_fcn = hash_fcns[hash_fcn_winner];
+
+ /*
+ Address ideal items buckets mc fl bloom sat fcn keys saved to
+ ------------------ ----- -------- -------- -- -- ----- ----- --- -------------
+ 0x10aa4090 98% 10000000 32000000 10 ok BER /tmp/9110-0.key
+ 0x10abcdef 100% 10000000 32000000 9 NX 27 12% BER /tmp/9110-1.key
+ */
+ printf("Address ideal items buckets mc fl bloom sat fcn keys saved to\n");
+ printf("------------------ ----- -------- -------- -- -- ----- ----- --- -------------\n");
+ if (has_bloom_filter_fields) {
+ printf("%-18p %4.0f%% %8u %8u %2u %2s %5u %4.0f%c %3s %s\n",
+ (void*)peer_tbl,
+ (tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
+ tbl->num_items,
+ tbl->num_buckets,
+ max_chain,
+ tbl->noexpand ? "NX" : "ok",
+ bloom_nbits,
+ bloom_on_bits * 100.0 / bloom_bitlen, '%',
+ hash_fcn,
+ (getkeys ? keyfile : ""));
+ } else {
+ printf("%-18p %4.0f%% %8u %8u %2u %2s %5s %4s%c %3s %s\n",
+ (void*)peer_tbl,
+ (tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
+ tbl->num_items,
+ tbl->num_buckets,
+ max_chain,
+ tbl->noexpand ? "NX" : "ok",
+ "",
+ "", ' ',
+ hash_fcn,
+ (getkeys ? keyfile : ""));
+ }
+
+#if 0
+ printf("read peer tbl:\n");
+ printf("num_buckets: %u\n", tbl->num_buckets);
+ printf("num_items: %u\n", tbl->num_items);
+ printf("nonideal_items: %u (%.2f%%)\n", tbl->nonideal_items,
+ tbl->nonideal_items*100.0/tbl->num_items);
+ printf("expand: %s\n", tbl->noexpand ? "inhibited": "normal");
+ if (getkeys) {
+ printf("keys written to %s\n", keyfile);
+ }
+#endif
+
+done:
+ if (bkts) {
+ free(bkts);
+ }
+ if (tbl) {
+ free(tbl);
+ }
+ if (key) {
+ free(key);
+ }
+ if (keyfd != -1) {
+ close(keyfd);
+ }
+ if (bloombv) {
+ free(bloombv);
+ }
+}
+
+
+#ifdef __FreeBSD__
+static void sigscan(pid_t pid, void *start, void *end, uint32_t sig)
+{
+ struct ptrace_io_desc io_desc;
+ int page_size = getpagesize();
+ char *buf;
+ char *pos;
+
+ /* make sure page_size is a multiple of the signature size, code below assumes this */
+ assert(page_size % sizeof(sig) == 0);
+
+ buf = malloc(page_size);
+
+ if (buf == NULL) {
+ fprintf(stderr, "malloc failed in sigscan()\n");
+ return;
+ }
+
+ io_desc.piod_op = PIOD_READ_D;
+ io_desc.piod_offs = start;
+ io_desc.piod_addr = buf;
+ io_desc.piod_len = page_size;
+
+ /* read in one page after another and search sig */
+ while(!ptrace(PT_IO, pid, (void *) &io_desc, 0)) {
+ if (io_desc.piod_len != page_size) {
+ fprintf(stderr, "PT_IO returned less than page size in sigscan()\n");
+ return;
+ }
+
+ /* iterate over the the page using the signature size and look for the sig */
+ for (pos = buf; pos < (buf + page_size); pos += sizeof(sig)) {
+ if (*(uint32_t *) pos == sig) {
+ found(pid, (char *) io_desc.piod_offs + (pos - buf), pid);
+ }
+ }
+
+ /*
+ * 'end' is inclusive (the address of the last valid byte), so if the current offset
+ * plus a page is beyond 'end', we're already done. since all vm map entries consist
+ * of entire pages and 'end' is inclusive, current offset plus one page should point
+ * exactly one byte beyond 'end'. this is assert()ed below to be on the safe side.
+ */
+ if (io_desc.piod_offs + page_size > end) {
+ assert(io_desc.piod_offs + page_size == (end + 1));
+ break;
+ }
+
+ /* advance to the next page */
+ io_desc.piod_offs += page_size;
+ }
+}
+#else
+static void sigscan(int fd, off_t start, off_t end, uint32_t sig, pid_t pid)
+{
+ int rlen;
+ uint32_t u;
+ off_t at=0;
+
+ if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
+ fprintf(stderr, "lseek failed: %s\n", strerror(errno));
+ return;
+ }
+
+ while ( (rlen = read(fd,&u,sizeof(u))) == sizeof(u)) {
+ if (!memcmp(&u,&sig,sizeof(u))) {
+ found(fd, (char*)(start+at),pid);
+ }
+ at += sizeof(u);
+ if ((off_t)(at + sizeof(u)) > end-start) {
+ break;
+ }
+ }
+
+ if (rlen == -1) {
+ //fprintf(stderr,"read failed: %s\n", strerror(errno));
+ //exit(-1);
+ }
+}
+#endif
+
+
+#ifdef __FreeBSD__
+static int scan(pid_t pid)
+{
+ vma_t *vmas=NULL, vma;
+ unsigned i, num_vmas = 0;
+ int ret;
+ struct ptrace_vm_entry vm_entry;
+ char path[MAXPATHLEN];
+
+ vv("attaching to peer\n");
+ if (ptrace(PT_ATTACH,pid,NULL,0) == -1) {
+ fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ vv("waiting for peer to suspend temporarily\n");
+ if (waitpid(pid,NULL,0) != pid) {
+ fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
+ goto die;
+ }
+
+ /* read memory map using ptrace */
+ vv("listing peer virtual memory areas\n");
+ vm_entry.pve_entry = 0;
+ vm_entry.pve_path = path; /* not used but required to make vm_entry.pve_pathlen work */
+ while(1) {
+ /* set pve_pathlen every turn, it gets overwritten by ptrace */
+ vm_entry.pve_pathlen = MAXPATHLEN;
+ errno = 0;
+
+ ret = ptrace(PT_VM_ENTRY, pid, (void *) &vm_entry, 0);
+
+ if (ret) {
+ if (errno == ENOENT) {
+ /* we've reached the last entry */
+ break;
+ }
+ fprintf(stderr, "fetching vm map entry failed: %s (%i)\n", strerror(errno), errno);
+ goto die;
+ }
+
+ vvv("vmmap entry: start: %p, end: %p", (void *) vm_entry.pve_start, (void *) vm_entry.pve_end);
+
+ /* skip unreadable or vnode-backed entries */
+ if (!(vm_entry.pve_prot & VM_PROT_READ) || vm_entry.pve_pathlen > 0) {
+ vvv(" -> skipped (not readable or vnode-backed)\n");
+ vm_entry.pve_path[0] = 0;
+ continue;
+ }
+
+ /* useful entry, add to list */
+ vvv(" -> will be scanned\n");
+ vma.start = (void *)vm_entry.pve_start;
+ vma.end = (void *)vm_entry.pve_end;
+ vmas = (vma_t *) realloc(vmas, (num_vmas + 1) * sizeof(vma_t));
+ if (vmas == NULL) {
+ exit(-1);
+ }
+ vmas[num_vmas++] = vma;
+ }
+
+ vv("peer has %u virtual memory areas\n", num_vmas);
+
+ /* look for the hash signature */
+ vv("scanning peer memory for hash table signatures\n");
+ for(i=0; i<num_vmas; i++) {
+ vma = vmas[i];
+ sigscan(pid, vma.start, vma.end, sig);
+ }
+
+die:
+ vv("detaching and resuming peer\n");
+ if (ptrace(PT_DETACH, pid, NULL, 0) == -1) {
+ fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
+ }
+ return 0;
+}
+# else
+static int scan(pid_t pid)
+{
+ FILE *mapf;
+ char mapfile[30], memfile[30], line[100];
+ vma_t *vmas=NULL, vma;
+ unsigned i, num_vmas = 0;
+ int memfd;
+ void *pstart, *pend, *unused;
+
+ /* attach to the target process and wait for it to suspend */
+ vv("attaching to peer\n");
+ if (ptrace(PTRACE_ATTACH, pid, NULL, 0) == -1) {
+ fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
+ exit(-1);
+ }
+ vv("waiting for peer to suspend temporarily\n");
+ if (waitpid(pid,NULL,0) != pid) {
+ fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
+ goto die;
+ }
+
+ /* get ready to open its memory map. this gives us its valid memory areas */
+ snprintf(mapfile,sizeof(mapfile),"/proc/%u/maps",(unsigned)pid);
+ snprintf(memfile,sizeof(memfile),"/proc/%u/mem", (unsigned)pid);
+ vv("opening peer memory map [%s]\n", mapfile);
+ if ( (mapf = fopen(mapfile,"r")) == NULL) {
+ fprintf(stderr,"failed to open %s: %s\n", mapfile, strerror(errno));
+ goto die;
+ }
+ vv("listing peer virtual memory areas\n");
+ while(fgets(line,sizeof(line),mapf)) {
+ if (sscanf(line, "%p-%p %4c %p %5c", &pstart, &pend, vma.perms,
+ &unused, vma.device) == 5) {
+ vma.start = (off_t)pstart;
+ vma.end = (off_t)pend;
+ if (vma.perms[0] != 'r') {
+ continue; /* only readable vma's */
+ }
+ if (memcmp(vma.device,"fd",2)==0) {
+ continue; /* skip mapped files */
+ }
+ vmas = (vma_t*)realloc(vmas, (num_vmas+1) * sizeof(vma_t));
+ if (vmas == NULL) {
+ exit(-1);
+ }
+ vmas[num_vmas++] = vma;
+ }
+ }
+ vv("peer has %u virtual memory areas\n",num_vmas);
+ fclose(mapf);
+
+ /* ok, open up its memory and start looking around in there */
+ vv("opening peer memory\n");
+ if ( (memfd=open(memfile,O_RDONLY)) == -1) {
+ fprintf(stderr,"failed to open %s: %s\n", memfile, strerror(errno));
+ goto die;
+ }
+ /* look for the hash signature */
+ vv("scanning peer memory for hash table signatures\n");
+ for(i=0; i<num_vmas; i++) {
+ vma = vmas[i];
+ pstart = (void*)vma.start;
+ pend = (void*)vma.end;
+ /*fprintf(stderr,"scanning %p-%p %.4s %.5s\n", pstart, pend,
+ vma.perms, vma.device);*/
+ sigscan(memfd, vma.start, vma.end, sig, pid);
+ }
+
+ /* done. close memory and detach. this resumes the target process */
+ close(memfd);
+
+die:
+ vv("detaching and resuming peer\n");
+ if (ptrace(PTRACE_DETACH, pid, NULL, 0) == -1) {
+ fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
+ }
+ return 0;
+}
+#endif
+
+
+static int usage(const char *prog)
+{
+ fprintf(stderr,"usage: %s [-v] [-k] <pid>\n", prog);
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+
+ while ( (opt = getopt(argc, argv, "kv")) != -1) {
+ switch (opt) {
+ case 'v':
+ verbose++;
+ break;
+ case 'k':
+ getkeys++;
+ break;
+ default:
+ return usage(argv[0]);
+ }
+ }
+
+ if (optind < argc) {
+ pid_t pid = atoi(argv[optind++]);
+ return scan(pid);
+ } else {
+ return usage(argv[0]);
+ }
+}
diff --git a/tests/keystat.c b/tests/keystat.c
new file mode 100644
index 000000000..cb64ca66f
--- /dev/null
+++ b/tests/keystat.c
@@ -0,0 +1,255 @@
+#include <sys/types.h> /* for 'open' */
+#include <sys/stat.h> /* for 'open' */
+#include <fcntl.h> /* for 'open' */
+#include <stdlib.h> /* for 'malloc' */
+#include <stdio.h> /* for 'printf' */
+#include <unistd.h> /* for 'read' */
+#include <errno.h> /* for 'sterror' */
+#include <sys/time.h> /* for 'gettimeofday' */
+#include "uthash.h"
+
+#undef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(t) die()
+#define UNALIGNED_KEYS 0
+
+static void die()
+{
+ fprintf(stderr,"expansion inhibited\n");
+ exit(-1);
+}
+
+/* Windows doesn't have gettimeofday. While Cygwin and some
+ * versions of MinGW supply one, it is very coarse. This substitute
+ * gives much more accurate elapsed times under Windows. */
+#if (( defined __CYGWIN__ ) || ( defined __MINGW32__ ))
+#include <windows.h>
+static void win_gettimeofday(struct timeval* p, void* tz /* IGNORED */)
+{
+ LARGE_INTEGER q;
+ static long long freq;
+ static long long cyg_timer;
+ QueryPerformanceFrequency(&q);
+ freq = q.QuadPart;
+ QueryPerformanceCounter(&q);
+ cyg_timer = q.QuadPart;
+ p->tv_sec = (long)(cyg_timer / freq);
+ p->tv_usec = (long)(((cyg_timer % freq) * 1000000) / freq);
+}
+#define gettimeofday win_gettimeofday
+#define MODE (O_RDONLY|O_BINARY)
+#else
+#define MODE (O_RDONLY)
+#endif
+
+#ifndef timersub
+#define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+typedef struct stat_key {
+ char *key;
+ unsigned len;
+ UT_hash_handle hh, hh2;
+} stat_key;
+
+#define CHAIN_0 0
+#define CHAIN_5 1
+#define CHAIN_10 2
+#define CHAIN_20 3
+#define CHAIN_100 4
+#define CHAIN_MAX 5
+static void hash_chain_len_histogram(const UT_hash_table *tbl)
+{
+ unsigned i, bkt_hist[CHAIN_MAX+1];
+ double pct = 100.0/(double)tbl->num_buckets;
+ memset(bkt_hist,0,sizeof(bkt_hist));
+ for(i=0; i < tbl->num_buckets; i++) {
+ unsigned count = tbl->buckets[i].count;
+ if (count == 0U) {
+ bkt_hist[CHAIN_0]++;
+ } else if (count < 5U) {
+ bkt_hist[CHAIN_5]++;
+ } else if (count < 10U) {
+ bkt_hist[CHAIN_10]++;
+ } else if (count < 20U) {
+ bkt_hist[CHAIN_20]++;
+ } else if (count < 100U) {
+ bkt_hist[CHAIN_100]++;
+ } else {
+ bkt_hist[CHAIN_MAX]++;
+ }
+ }
+ fprintf(stderr, "Buckets with 0 items: %.1f%%\n", (double)bkt_hist[CHAIN_0 ]*pct);
+ fprintf(stderr, "Buckets with < 5 items: %.1f%%\n", (double)bkt_hist[CHAIN_5 ]*pct);
+ fprintf(stderr, "Buckets with < 10 items: %.1f%%\n", (double)bkt_hist[CHAIN_10]*pct);
+ fprintf(stderr, "Buckets with < 20 items: %.1f%%\n", (double)bkt_hist[CHAIN_20]*pct);
+ fprintf(stderr, "Buckets with < 100 items: %.1f%%\n", (double)bkt_hist[CHAIN_100]*pct);
+ fprintf(stderr, "Buckets with > 100 items: %.1f%%\n", (double)bkt_hist[CHAIN_MAX]*pct);
+}
+
+int main(int argc, char *argv[])
+{
+ int dups=0, rc, fd, done=0, err=0, want, i, padding=0, v=1, percent=100;
+ unsigned keylen, max_keylen=0, verbose=0;
+ const char *filename = "/dev/stdin";
+ char *dst;
+ stat_key *keyt, *keytmp, *keys=NULL, *keys2=NULL;
+ struct timeval start_tm, end_tm, elapsed_tm, elapsed_tm2, elapsed_tm3;
+
+ if ((argc >= 3) && (strcmp(argv[1],"-p") == 0)) {
+ percent = atoi(argv[2]);
+ v = 3;
+ }
+ if ((v < argc) && (strcmp(argv[v],"-v") == 0)) {
+ verbose=1;
+ v++;
+ }
+ if (v < argc) {
+ filename=argv[v];
+ }
+ fd=open(filename,MODE);
+
+ if ( fd == -1 ) {
+ fprintf(stderr,"open failed %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ for(i=0; done==0; i++) {
+
+ want = sizeof(int);
+ dst = (char*)&keylen;
+readmore1:
+ rc = read(fd,dst,want);
+ if (rc != want) {
+ if (rc == 0) {
+ done=1;
+ } else if (rc == -1) {
+ fprintf(stderr,"read failed: %s\n", strerror(errno));
+ err=1;
+ } else if (rc > 0) {
+ want -= rc;
+ dst += rc;
+ goto readmore1;
+ }
+ }
+
+ if (done || err) {
+ break;
+ }
+ if (keylen > max_keylen) {
+ max_keylen=keylen;
+ }
+
+ keyt = (stat_key*)malloc(sizeof(stat_key));
+ if (keyt == NULL) {
+ fprintf(stderr,"out of memory\n");
+ exit(-1);
+ }
+
+ /* read key */
+#ifdef UNALIGNED_KEYS
+ padding = i%8;
+#endif
+ keyt->key = (char*)malloc(padding+keylen);
+ if (keyt->key == NULL) {
+ fprintf(stderr,"out of memory\n");
+ exit(-1);
+ }
+ keyt->key += padding; /* forcibly alter the alignment of key */
+ keyt->len = keylen;
+
+ want = keylen;
+ dst = keyt->key;
+readmore2:
+ rc = read(fd,dst,want);
+ if (rc != want) {
+ if (rc == -1) {
+ fprintf(stderr,"read failed: %s\n", strerror(errno));
+ err=1;
+ } else if (rc == 0) {
+ fprintf(stderr,"incomplete file\n");
+ err=1;
+ } else if (rc >= 0) {
+ want -= rc;
+ dst += rc;
+ goto readmore2;
+ }
+ }
+ if (err != 0) {
+ break;
+ }
+ /* if percent was set to something less than 100%, skip some keys*/
+ if (((rand()*1.0) / RAND_MAX) > ((percent*1.0)/100)) {
+ free(keyt->key-padding);
+ free(keyt);
+ continue;
+ }
+
+ /* eliminate dups */
+ HASH_FIND(hh,keys,keyt->key,keylen,keytmp);
+ if (keytmp != NULL) {
+ dups++;
+ free(keyt->key - padding);
+ free(keyt);
+ } else {
+ HASH_ADD_KEYPTR(hh,keys,keyt->key,keylen,keyt);
+ }
+ }
+
+ if (verbose != 0) {
+ unsigned key_count = HASH_COUNT(keys);
+ fprintf(stderr,"max key length: %u\n", max_keylen);
+ fprintf(stderr,"number unique keys: %u\n", key_count);
+ fprintf(stderr,"keystats memory: %u\n",
+ (unsigned)((sizeof(stat_key)+max_keylen)*key_count));
+ hash_chain_len_histogram(keys->hh.tbl);
+ }
+
+ /* add all keys to a new hash, so we can measure add time w/o malloc */
+ gettimeofday(&start_tm,NULL);
+ for(keyt = keys; keyt != NULL; keyt=(stat_key*)keyt->hh.next) {
+ HASH_ADD_KEYPTR(hh2,keys2,keyt->key,keyt->len,keyt);
+ }
+ gettimeofday(&end_tm,NULL);
+ timersub(&end_tm, &start_tm, &elapsed_tm);
+
+ /* now look up all keys in the new hash, again measuring elapsed time */
+ gettimeofday(&start_tm,NULL);
+ for(keyt = keys; keyt != NULL; keyt=(stat_key*)keyt->hh.next) {
+ HASH_FIND(hh2,keys2,keyt->key,keyt->len,keytmp);
+ if (keytmp == NULL) {
+ fprintf(stderr,"internal error, key not found\n");
+ }
+ }
+ gettimeofday(&end_tm,NULL);
+ timersub(&end_tm, &start_tm, &elapsed_tm2);
+
+ /* now delete all items in the new hash, measuring elapsed time */
+ gettimeofday(&start_tm,NULL);
+ while (keys2 != NULL) {
+ keytmp = keys2;
+ HASH_DELETE(hh2,keys2,keytmp);
+ }
+ gettimeofday(&end_tm,NULL);
+ timersub(&end_tm, &start_tm, &elapsed_tm3);
+
+ if (err == 0) {
+ printf("%.3f,%u,%u,%d,%s,%ld,%ld,%ld\n",
+ 1-(1.0*keys->hh.tbl->nonideal_items/keys->hh.tbl->num_items),
+ keys->hh.tbl->num_items,
+ keys->hh.tbl->num_buckets,
+ dups,
+ (keys->hh.tbl->noexpand != 0U) ? "nx" : "ok",
+ (elapsed_tm.tv_sec * 1000000) + elapsed_tm.tv_usec,
+ (elapsed_tm2.tv_sec * 1000000) + elapsed_tm2.tv_usec,
+ (elapsed_tm3.tv_sec * 1000000) + elapsed_tm3.tv_usec );
+ }
+ return 0;
+}
diff --git a/tests/keystats b/tests/keystats
new file mode 100755
index 000000000..9d4076278
--- /dev/null
+++ b/tests/keystats
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+use strict;
+
+use FindBin;
+
+sub usage {
+ print "usage: keystats [-v] keyfile\n";
+ print "usage: keystats [-p <pct> [-v]] keyfile\n";
+ exit -1;
+}
+
+usage if ((@ARGV == 0) or ($ARGV[0] eq '-h'));
+
+my @exes = glob "$FindBin::Bin/keystat.???";
+my %stats;
+for my $exe (@exes) {
+ $stats{$exe} = `$exe @ARGV`;
+ delete $stats{$exe} if ($? != 0); # omit hash functions that fail to produce stats (nx)
+}
+
+print( "fcn ideal% #items #buckets dup% fl add_usec find_usec del-all usec\n");
+printf("--- ------ ---------- ---------- ----- -- ---------- ---------- ------------\n");
+for my $exe (sort statsort keys %stats) {
+ my ($ideal,$items,$bkts,$dups,$ok,$add,$find,$del) = split /,/, $stats{$exe};
+
+ # convert 0-1 values to percentages
+ $dups = $items ? (100.0 * $dups / $items) : 0.0;
+ $ideal = 100.0 * $ideal;
+
+ printf("%3s %5.1f%% %10d %10d %4.0f%% %2s %10d %10d %12d\n", substr($exe,-3,3),
+ $ideal,$items,$bkts,$dups,$ok,$add,$find,$del);
+}
+
+# sort on hash_q (desc) then by find_usec (asc)
+sub statsort {
+ my @a_stats = split /,/, $stats{$a};
+ my @b_stats = split /,/, $stats{$b};
+ return ($b_stats[0] <=> $a_stats[0]) || ($a_stats[-1] <=> $b_stats[-1]);
+}
diff --git a/tests/lru_cache/Makefile b/tests/lru_cache/Makefile
new file mode 100644
index 000000000..aa3a2cb89
--- /dev/null
+++ b/tests/lru_cache/Makefile
@@ -0,0 +1,21 @@
+CC=gcc
+
+CFLAGS+=-W -Werror -Wall -Wextra -std=c99 \
+ -D_FORTIFY_SOURCE=2 -fstack-protector -g \
+ -Wformat=2 -pedantic -pedantic-errors \
+ -D_GNU_SOURCE=1 -D_BSD_SOURCE=1 \
+ -I../../src
+
+LDFLAGS+=-pthread
+
+cache: main.o cache.o
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) main.o cache.o -o cache
+
+main.o: main.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c main.c -o main.o
+
+cache.o: cache.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c cache.c -o cache.o
+
+clean:
+ rm -f cache *.o
diff --git a/tests/lru_cache/cache.c b/tests/lru_cache/cache.c
new file mode 100644
index 000000000..632c55862
--- /dev/null
+++ b/tests/lru_cache/cache.c
@@ -0,0 +1,221 @@
+/*
+ * =====================================================================================
+ *
+ * Filename: cache.c
+ *
+ * Description: A simple cache
+ *
+ * Version: 1.0
+ * Created: 04/11/2013 02:31:02 PM
+ * Revision: none
+ * Compiler: gcc
+ *
+ * Author: Oliver Lorenz (ol), olli@olorenz.org
+ * Company: https://olorenz.org
+ * License: This is licensed under the same terms as uthash itself
+ *
+ * =====================================================================================
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include "cache.h"
+#include "uthash.h"
+
+/**
+ * A cache entry
+ */
+struct foo_cache_entry {
+ char *key; /**<The key */
+ void *data; /**<Payload */
+ UT_hash_handle hh; /**<Hash Handle for uthash */
+};
+#define KEY_MAX_LENGTH 32
+
+/**
+ * A cache object
+ */
+struct foo_cache {
+ size_t max_entries; /**<Amount of entries this cache object can hold */
+ pthread_rwlock_t cache_lock; /**<A lock for concurrent access */
+ struct foo_cache_entry *entries; /**<Head pointer for uthash */
+ void (*free_cb) (void *element);/**<Callback function to free cache entries */
+};
+
+/** Creates a new cache object
+
+ @param dst
+ Where the newly allocated cache object will be stored in
+
+ @param capacity
+ The maximum number of elements this cache object can hold
+
+ @return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise
+*/
+int foo_cache_create(struct foo_cache **dst, const size_t capacity,
+ void (*free_cb) (void *element))
+{
+ struct foo_cache *new = NULL;
+ int rv;
+
+ if (!dst)
+ return EINVAL;
+
+ if ((new = malloc(sizeof(*new))) == NULL)
+ return ENOMEM;
+
+ if ((rv = pthread_rwlock_init(&(new->cache_lock), NULL)) != 0)
+ goto err_out;
+
+ new->max_entries = capacity;
+ new->entries = NULL;
+ new->free_cb = free_cb;
+ *dst = new;
+ return 0;
+
+err_out:
+ if (new)
+ free(new);
+ return rv;
+}
+
+/** Frees an allocated cache object
+
+ @param cache
+ The cache object to free
+
+ @param keep_data
+ Whether to free contained data or just delete references to it
+
+ @return EINVAL if cache is NULL, 0 otherwise
+*/
+int foo_cache_delete(struct foo_cache *cache, int keep_data)
+{
+ struct foo_cache_entry *entry, *tmp;
+ int rv;
+
+ if (!cache)
+ return EINVAL;
+
+ rv = pthread_rwlock_wrlock(&(cache->cache_lock));
+ if (rv)
+ return rv;
+
+ if (keep_data) {
+ HASH_CLEAR(hh, cache->entries);
+ } else {
+ HASH_ITER(hh, cache->entries, entry, tmp) {
+ HASH_DEL(cache->entries, entry);
+ if (cache->free_cb)
+ cache->free_cb(entry->data);
+ free(entry);
+ }
+ }
+ (void)pthread_rwlock_unlock(&(cache->cache_lock));
+ (void)pthread_rwlock_destroy(&(cache->cache_lock));
+ free(cache);
+ cache = NULL;
+ return 0;
+}
+
+/** Checks if a given key is in the cache
+
+ @param cache
+ The cache object
+
+ @param key
+ The key to look-up
+
+ @param result
+ Where to store the result if key is found.
+
+ A warning: Even though result is just a pointer,
+ you have to call this function with a **ptr,
+ otherwise this will blow up in your face.
+
+ @return EINVAL if cache is NULL, 0 otherwise
+*/
+int foo_cache_lookup(struct foo_cache *cache, char *key, void *result)
+{
+ int rv;
+ struct foo_cache_entry *tmp = NULL;
+ char **dirty_hack = result;
+
+ if (!cache || !key || !result)
+ return EINVAL;
+
+ rv = pthread_rwlock_wrlock(&(cache->cache_lock));
+ if (rv)
+ return rv;
+
+ HASH_FIND_STR(cache->entries, key, tmp);
+ if (tmp) {
+ size_t key_len = strnlen(tmp->key, KEY_MAX_LENGTH);
+ HASH_DELETE(hh, cache->entries, tmp);
+ HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
+ *dirty_hack = tmp->data;
+ } else {
+ *dirty_hack = result = NULL;
+ }
+ rv = pthread_rwlock_unlock(&(cache->cache_lock));
+ return rv;
+}
+
+/** Inserts a given <key, value> pair into the cache
+
+ @param cache
+ The cache object
+
+ @param key
+ The key that identifies <value>
+
+ @param data
+ Data associated with <key>
+
+ @return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise
+*/
+int foo_cache_insert(struct foo_cache *cache, char *key, void *data)
+{
+ struct foo_cache_entry *entry = NULL;
+ struct foo_cache_entry *tmp_entry = NULL;
+ size_t key_len = 0;
+ int rv;
+
+ if (!cache || !data)
+ return EINVAL;
+
+ if ((entry = malloc(sizeof(*entry))) == NULL)
+ return ENOMEM;
+
+ if ((rv = pthread_rwlock_wrlock(&(cache->cache_lock))) != 0)
+ goto err_out;
+
+ entry->key = key;
+ entry->data = data;
+ key_len = strnlen(entry->key, KEY_MAX_LENGTH);
+ HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry);
+
+ if (HASH_COUNT(cache->entries) >= cache->max_entries) {
+ HASH_ITER(hh, cache->entries, entry, tmp_entry) {
+ HASH_DELETE(hh, cache->entries, entry);
+ if (cache->free_cb)
+ cache->free_cb(entry->data);
+ else
+ free(entry->data);
+ /* free(key->key) if data has been copied */
+ free(entry);
+ break;
+ }
+ }
+
+ rv = pthread_rwlock_unlock(&(cache->cache_lock));
+ return rv;
+
+err_out:
+ if (entry)
+ free(entry);
+ (void)pthread_rwlock_unlock(&(cache->cache_lock));
+ return rv;
+
+}
diff --git a/tests/lru_cache/cache.h b/tests/lru_cache/cache.h
new file mode 100644
index 000000000..350576d4f
--- /dev/null
+++ b/tests/lru_cache/cache.h
@@ -0,0 +1,31 @@
+/*
+ * =====================================================================================
+ *
+ * Filename: cache.h
+ *
+ * Description: A simple cache
+ *
+ * Version: 1.0
+ * Created: 04/11/2013 02:30:46 PM
+ * Revision: none
+ * Compiler: gcc
+ *
+ * Author: Oliver Lorenz (ol), olli@olorenz.org
+ * Company: https://olorenz.org
+ * License: This is licensed under the same terms as uthash itself
+ *
+ * =====================================================================================
+ */
+
+#ifndef _CACHE_
+#define _CACHE_
+
+struct foo_cache;
+
+extern int foo_cache_create(struct foo_cache **dst, const size_t capacity,
+ void (*free_cb) (void *element));
+extern int foo_cache_delete(struct foo_cache *cache, int keep_data);
+extern int foo_cache_lookup(struct foo_cache *cache, char *key, void *result);
+extern int foo_cache_insert(struct foo_cache *cache, char *key, void *data);
+
+#endif
diff --git a/tests/lru_cache/main.c b/tests/lru_cache/main.c
new file mode 100644
index 000000000..7f0eae2f2
--- /dev/null
+++ b/tests/lru_cache/main.c
@@ -0,0 +1,191 @@
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "cache.h"
+
+#define MAX_RANDOM_ENTRIES 32
+
+struct key_record {
+ char *key;
+ char *value;
+};
+
+int generate_random_entry(struct key_record **entry);
+int generate_random_string(char **dst, const size_t len);
+void free_random_entry(void *entry);
+
+void *producer(void *arg)
+{
+ struct foo_cache *cache = arg;
+ int i;
+
+ for (i = 0; i < MAX_RANDOM_ENTRIES; i++) {
+ struct key_record *entry = NULL;
+ if (generate_random_entry(&entry)) {
+ fprintf(stderr, "generate_random_entry() failed\n");
+ continue;
+ }
+#if defined(DEBUG)
+ printf("Random Entry:\n");
+ printf(" key: %s\n", entry->key);
+ printf(" Key: %s\n", entry->value);
+#else
+ printf("inserted %s (%d)\n", entry->key,
+ (int)strlen(entry->key));
+#endif
+ if (foo_cache_insert(cache, entry->key, entry)) {
+ fprintf(stderr, "foo_cache_insert() failed\n");
+ continue;
+ }
+ }
+
+ pthread_exit(NULL);
+}
+
+void *consumer(void *arg)
+{
+ struct foo_cache *cache = arg;
+ struct key_record *result = NULL;
+ char *buffer = malloc(64);
+ char key[33];
+ int stop = 0;
+
+ if (!buffer)
+ goto out;
+
+ /* give producer time to populate the cache */
+ sleep(2);
+ printf("\n\n");
+
+ do {
+ memset(key, 0, 64);
+ result = NULL;
+
+ printf("Enter key for lookup: ");
+ fgets(buffer, sizeof(key), stdin);
+ sscanf(buffer, "%s\n", key);
+ /* read '\n' from stdin */
+ getchar();
+
+ if (strncmp(key, "exit", 4) == 0) {
+ stop = 1;
+ continue;
+ }
+
+ printf("Got key %s (%d)\n", key, (int)strlen(key));
+
+ if (foo_cache_lookup(cache, key, &result)) {
+ fprintf(stderr, "Could not retrieve key %s\n", key);
+ continue;
+ }
+
+ if (!result) {
+ printf("MISS\n");
+ continue;
+ }
+
+ printf("HIT\n");
+ printf("key: %s\n", result->key);
+ printf("key : %s\n", result->value);
+ } while (!stop);
+
+out:
+ if (buffer)
+ free(buffer);
+ pthread_exit(NULL);
+}
+
+int main()
+{
+ int rv;
+ struct foo_cache *cache = NULL;
+ pthread_t workers[2];
+
+ rv = foo_cache_create(&cache, MAX_RANDOM_ENTRIES / 2,
+ free_random_entry);
+ if (rv) {
+ fprintf(stderr, "Could not create cache\n");
+ exit(1);
+ }
+
+ (void)pthread_create(&workers[0], NULL, producer, (void *)cache);
+ (void)pthread_create(&workers[1], NULL, consumer, (void *)cache);
+
+ pthread_join(workers[0], NULL);
+ pthread_join(workers[1], NULL);
+
+ (void)foo_cache_delete(cache, 0);
+ return 0;
+}
+
+int generate_random_entry(struct key_record **entry)
+{
+ struct key_record *new = NULL;
+ char *key = NULL;
+ char *value = NULL;
+ int rv;
+
+ if (!entry)
+ return EINVAL;
+
+ rv = generate_random_string(&key, 33);
+ if (rv)
+ return rv;
+
+ rv = generate_random_string(&value, 129);
+ if (rv)
+ return rv;
+
+ if ((new = malloc(sizeof(*new))) == NULL) {
+ free(key);
+ free(value);
+ return ENOMEM;
+ }
+
+ new->key = key;
+ new->value = value;
+
+ *entry = new;
+ return 0;
+}
+
+int generate_random_string(char **dst, const size_t len)
+{
+ static const char alphanum[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ size_t i;
+ char *s;
+
+ if (!dst || len == 0)
+ return EINVAL;
+
+ if ((s = malloc(len)) == NULL)
+ return ENOMEM;
+
+ for (i = 0; i < len - 1; i++) {
+ s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
+ }
+
+ s[len - 1] = '\0';
+ *dst = s;
+ return 0;
+}
+
+void free_random_entry(void *entry)
+{
+#if defined(DEBUG)
+ fprintf(stderr, "In %s: entry @ %p\n", __func__, entry);
+#endif
+ struct key_record *record = entry;
+ if (!record)
+ return;
+ if (record->key)
+ free(record->key);
+ if (record->value)
+ free(record->value);
+ free(record);
+ record = NULL;
+}
diff --git a/tests/simkeys.pl b/tests/simkeys.pl
new file mode 100755
index 000000000..acc5583b7
--- /dev/null
+++ b/tests/simkeys.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+# This program generates a simkey10.dat (100, 1000, etc) each
+# containing 100 random keys of length 10 (100, 1000, etc).
+# These files can then be fed into keystats to observe that
+# the time to add or find the keys is directly proportional to
+# keylength n [in other words, O(n)].
+#
+# The conclusion is that really long keys (e.g. 100k) are not
+# efficient. TDH 23Jan07
+
+use strict;
+use warnings;
+
+
+#for my $len (10,100,1000,10000,100000,1000000) {
+for my $len (100) {
+ open OUTFILE, ">simkeys$len.dat" or die "can't open: $!\n";
+ # we'll do 100 keys of $len
+ print "keylen $len\n";
+ for my $i (0..99) {
+ my $key = pack "I", $len;
+ $key .= pack "C", (int(rand(256))) for (1..$len);
+ print OUTFILE $key;
+ }
+ close OUTFILE;
+}
+
diff --git a/tests/sleep_test.c b/tests/sleep_test.c
new file mode 100644
index 000000000..60e60d1b6
--- /dev/null
+++ b/tests/sleep_test.c
@@ -0,0 +1,32 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+#include <unistd.h> /* getpid */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10000; i++) {
+ if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ printf("pid: %u\n", (unsigned)getpid());
+ /* printf("sig: %p\n", &users->hh.tbl->signature); */
+ /* printf("bbv: %p\n", &users->hh.tbl->bloom_bv); */
+ sleep(60*10);
+ return 0;
+}
diff --git a/tests/tdiff.cpp b/tests/tdiff.cpp
new file mode 100644
index 000000000..4be14fed6
--- /dev/null
+++ b/tests/tdiff.cpp
@@ -0,0 +1,34 @@
+// Windows does not have unix diff so this is a simple replacement
+#include <iostream>
+#include <fstream>
+using namespace std;
+int main(int argc, char *argv[] ) {
+ int rc=-1;
+ if (argc != 3) {
+ cout << "usage: " << argv[0] << " file1 file2\n";
+ return -1;
+ }
+ char *file1 = argv[1];
+ char *file2 = argv[2];
+ ifstream is1(file1, ios::in);
+ ifstream is2(file2, ios::in);
+ if (is1.fail()) {cerr << "failed to open " << file1 << "\n"; goto done;}
+ if (is2.fail()) {cerr << "failed to open " << file2 << "\n"; goto done;}
+ char d1[256], d2[256];
+ do {
+ is1.read(d1,sizeof(d1));
+ is2.read(d2,sizeof(d2));
+ if ((is1.gcount() != is2.gcount()) || memcmp(d1,d2,is1.gcount())) {
+ cout << file1 << " and " << file2 << " differ\n";
+ goto done;
+ }
+ } while (!is1.eof() && !is2.eof());
+
+ rc=0;
+
+ done:
+ is1.close();
+ is2.close();
+ return rc;
+}
+
diff --git a/tests/test1.ans b/tests/test1.ans
new file mode 100644
index 000000000..0b08c48c3
--- /dev/null
+++ b/tests/test1.ans
@@ -0,0 +1,10 @@
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
diff --git a/tests/test1.c b/tests/test1.c
new file mode 100644
index 000000000..5e457ab3b
--- /dev/null
+++ b/tests/test1.c
@@ -0,0 +1,31 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ return 0;
+}
diff --git a/tests/test10.ans b/tests/test10.ans
new file mode 100644
index 000000000..b1a27a844
--- /dev/null
+++ b/tests/test10.ans
@@ -0,0 +1,4 @@
+9 found in hh
+9 found in alth
+10 not found in hh
+10 found in alth
diff --git a/tests/test10.c b/tests/test10.c
new file mode 100644
index 000000000..64f8035e2
--- /dev/null
+++ b/tests/test10.c
@@ -0,0 +1,51 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+ UT_hash_handle alth;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL, *altusers=NULL;
+
+ /* create elements */
+ for(i=0; i<1000; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ if (i<10) {
+ HASH_ADD_INT(users,id,user);
+ }
+ HASH_ADD(alth,altusers,id,sizeof(int),user);
+ }
+
+ /*
+ printf("hh items: %d, alth items: %d\n",
+ users->hh.tbl->num_items, users->alth.tbl->num_items);
+ printf("hh buckets: %d, alth buckets: %d\n",
+ users->hh.tbl->num_buckets, users->alth.tbl->num_buckets);
+ */
+
+ i=9;
+ HASH_FIND_INT(users,&i,tmp);
+ printf("%d %s in hh\n", i, (tmp != NULL) ? "found" : "not found");
+ HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
+ printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
+
+ i=10;
+ HASH_FIND_INT(users,&i,tmp);
+ printf("%d %s in hh\n", i, (tmp != NULL) ? "found" : "not found");
+ HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
+ printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
+
+ return 0;
+}
diff --git a/tests/test11.ans b/tests/test11.ans
new file mode 100644
index 000000000..2b72e985a
--- /dev/null
+++ b/tests/test11.ans
@@ -0,0 +1,51 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
diff --git a/tests/test11.c b/tests/test11.c
new file mode 100644
index 000000000..1460141f7
--- /dev/null
+++ b/tests/test11.c
@@ -0,0 +1,57 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <errno.h> /* perror */
+#include <stdio.h> /* printf */
+
+#define BUFLEN 20
+
+#if 0
+/* Print a message if the hash's no-expand flag is set. */
+#undef uthash_noexpand_fyi
+#undef uthash_expand_fyi
+#define uthash_noexpand_fyi(tbl) printf("noexpand set\n");
+#define uthash_expand_fyi(tbl) printf("hash expanded\n");
+#endif
+
+typedef struct name_rec {
+ char boy_name[BUFLEN];
+ UT_hash_handle hh;
+} name_rec;
+
+static int namecmp(void *_a, void *_b)
+{
+ name_rec *a = (name_rec*)_a;
+ name_rec *b = (name_rec*)_b;
+ return strcmp(a->boy_name,b->boy_name);
+}
+
+int main()
+{
+ name_rec *name, *names=NULL;
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (name_rec*)malloc(sizeof(name_rec));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->boy_name, linebuf);
+ HASH_ADD_STR(names,boy_name,name);
+ }
+
+ fclose(file);
+ HASH_SORT(names,namecmp);
+ for(name=names; name!=NULL; name=(name_rec*)(name->hh.next)) {
+ printf("%s",name->boy_name);
+ }
+
+ return 0;
+}
+
diff --git a/tests/test11.dat b/tests/test11.dat
new file mode 100644
index 000000000..bb6051b75
--- /dev/null
+++ b/tests/test11.dat
@@ -0,0 +1,51 @@
+JOHN
+WILLIAM
+WALTER
+DOUGLAS
+GERALD
+FREDERICK
+WARREN
+SHANE
+LESTER
+RON
+HARVEY
+ADRIAN
+CODY
+NELSON
+CLIFTON
+WILLARD
+DOUG
+ORLANDO
+REX
+OMAR
+DAMON
+LOWELL
+IRVING
+CARROLL
+LAURENCE
+ROLANDO
+CARY
+XAVIER
+ISAIAH
+GUS
+JARVIS
+WINFRED
+RAYMUNDO
+LINCOLN
+CORNELL
+NIGEL
+NORMAND
+FRITZ
+DONN
+TRINIDAD
+ODIS
+DANNIE
+DARIO
+KENTON
+CHONG
+NEVILLE
+TONEY
+WARNER
+WES
+COLTON
+ARNOLDO
diff --git a/tests/test12.ans b/tests/test12.ans
new file mode 100644
index 000000000..727f397eb
--- /dev/null
+++ b/tests/test12.ans
@@ -0,0 +1,20 @@
+added bob (id 0)
+added jack (id 1)
+added gary (id 2)
+added ty (id 3)
+added bo (id 4)
+added phil (id 5)
+added art (id 6)
+added gil (id 7)
+added buck (id 8)
+added ted (id 9)
+found bob (id 0)
+found jack (id 1)
+found gary (id 2)
+found ty (id 3)
+found bo (id 4)
+found phil (id 5)
+found art (id 6)
+found gil (id 7)
+found buck (id 8)
+found ted (id 9)
diff --git a/tests/test12.c b/tests/test12.c
new file mode 100644
index 000000000..1ed0d3b34
--- /dev/null
+++ b/tests/test12.c
@@ -0,0 +1,40 @@
+#include "uthash.h"
+#include <stdio.h>
+#include <stdlib.h> /* malloc */
+
+typedef struct person_t {
+ char first_name[10];
+ int id;
+ UT_hash_handle hh;
+} person_t;
+
+int main()
+{
+ person_t *people=NULL, *person;
+ const char **name;
+ const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art",
+ "gil", "buck", "ted", NULL
+ };
+ int id=0;
+
+ for(name=names; *name != NULL; name++) {
+ person = (person_t*)malloc(sizeof(person_t));
+ if (person == NULL) {
+ exit(-1);
+ }
+ strcpy(person->first_name, *name);
+ person->id = id++;
+ HASH_ADD_STR(people,first_name,person);
+ printf("added %s (id %d)\n", person->first_name, person->id);
+ }
+
+ for(name=names; *name != NULL; name++) {
+ HASH_FIND_STR(people,*name,person);
+ if (person != NULL) {
+ printf("found %s (id %d)\n", person->first_name, person->id);
+ } else {
+ printf("failed to find %s\n", *name);
+ }
+ }
+ return 0;
+}
diff --git a/tests/test13.ans b/tests/test13.ans
new file mode 100644
index 000000000..baf3ff215
--- /dev/null
+++ b/tests/test13.ans
@@ -0,0 +1,5 @@
+id 9, following prev...
+id 7, following prev...
+id 5, following prev...
+id 3, following prev...
+id 1, following prev...
diff --git a/tests/test13.c b/tests/test13.c
new file mode 100644
index 000000000..eef7d029b
--- /dev/null
+++ b/tests/test13.c
@@ -0,0 +1,50 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ /* delete each even ID */
+ for(i=0; i<10; i+=2) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ HASH_DEL(users,tmp);
+ free(tmp);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+
+ i=9;
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ while (tmp != NULL) {
+ printf("id %d, following prev...\n", tmp->id);
+ tmp = (example_user_t*)tmp->hh.prev;
+ }
+ } else {
+ printf("user id %d not found\n", i);
+ }
+
+ return 0;
+}
diff --git a/tests/test14.ans b/tests/test14.ans
new file mode 100644
index 000000000..77aeaebd0
--- /dev/null
+++ b/tests/test14.ans
@@ -0,0 +1 @@
+lookup on 1219 of 1219 names succeeded
diff --git a/tests/test14.c b/tests/test14.c
new file mode 100644
index 000000000..cb2606716
--- /dev/null
+++ b/tests/test14.c
@@ -0,0 +1,54 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <errno.h> /* perror */
+#include <stdio.h> /* printf */
+
+#define BUFLEN 20
+#if 0
+#undef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets)
+#endif
+
+typedef struct name_rec {
+ char boy_name[BUFLEN];
+ UT_hash_handle hh;
+} name_rec;
+
+int main()
+{
+ name_rec *name, *names=NULL;
+ char linebuf[BUFLEN];
+ FILE *file;
+ int i=0,j=0;
+
+ file = fopen( "test14.dat", "r" );
+ if (file == NULL ) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ i++;
+ name = (name_rec*)malloc(sizeof(name_rec));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->boy_name, linebuf);
+ HASH_ADD_STR(names,boy_name,name);
+ }
+
+ fseek(file,0L,SEEK_SET);
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ HASH_FIND_STR(names,linebuf,name);
+ if (!name) {
+ printf("failed to find: %s", linebuf);
+ } else {
+ j++;
+ }
+ }
+ fclose(file);
+ printf("lookup on %d of %d names succeeded\n", j, i);
+ return 0;
+}
+
diff --git a/tests/test14.dat b/tests/test14.dat
new file mode 100644
index 000000000..ea0b4eac6
--- /dev/null
+++ b/tests/test14.dat
@@ -0,0 +1,1219 @@
+JAMES
+JOHN
+ROBERT
+MICHAEL
+WILLIAM
+DAVID
+RICHARD
+CHARLES
+JOSEPH
+THOMAS
+CHRISTOPHER
+DANIEL
+PAUL
+MARK
+DONALD
+GEORGE
+KENNETH
+STEVEN
+EDWARD
+BRIAN
+RONALD
+ANTHONY
+KEVIN
+JASON
+MATTHEW
+GARY
+TIMOTHY
+JOSE
+LARRY
+JEFFREY
+FRANK
+SCOTT
+ERIC
+STEPHEN
+ANDREW
+RAYMOND
+GREGORY
+JOSHUA
+JERRY
+DENNIS
+WALTER
+PATRICK
+PETER
+HAROLD
+DOUGLAS
+HENRY
+CARL
+ARTHUR
+RYAN
+ROGER
+JOE
+JUAN
+JACK
+ALBERT
+JONATHAN
+JUSTIN
+TERRY
+GERALD
+KEITH
+SAMUEL
+WILLIE
+RALPH
+LAWRENCE
+NICHOLAS
+ROY
+BENJAMIN
+BRUCE
+BRANDON
+ADAM
+HARRY
+FRED
+WAYNE
+BILLY
+STEVE
+LOUIS
+JEREMY
+AARON
+RANDY
+HOWARD
+EUGENE
+CARLOS
+RUSSELL
+BOBBY
+VICTOR
+MARTIN
+ERNEST
+PHILLIP
+TODD
+JESSE
+CRAIG
+ALAN
+SHAWN
+CLARENCE
+SEAN
+PHILIP
+CHRIS
+JOHNNY
+EARL
+JIMMY
+ANTONIO
+DANNY
+BRYAN
+TONY
+LUIS
+MIKE
+STANLEY
+LEONARD
+NATHAN
+DALE
+MANUEL
+RODNEY
+CURTIS
+NORMAN
+ALLEN
+MARVIN
+VINCENT
+GLENN
+JEFFERY
+TRAVIS
+JEFF
+CHAD
+JACOB
+LEE
+MELVIN
+ALFRED
+KYLE
+FRANCIS
+BRADLEY
+JESUS
+HERBERT
+FREDERICK
+RAY
+JOEL
+EDWIN
+DON
+EDDIE
+RICKY
+TROY
+RANDALL
+BARRY
+ALEXANDER
+BERNARD
+MARIO
+LEROY
+FRANCISCO
+MARCUS
+MICHEAL
+THEODORE
+CLIFFORD
+MIGUEL
+OSCAR
+JAY
+JIM
+TOM
+CALVIN
+ALEX
+JON
+RONNIE
+BILL
+LLOYD
+TOMMY
+LEON
+DEREK
+WARREN
+DARRELL
+JEROME
+FLOYD
+LEO
+ALVIN
+TIM
+WESLEY
+GORDON
+DEAN
+GREG
+JORGE
+DUSTIN
+PEDRO
+DERRICK
+DAN
+LEWIS
+ZACHARY
+COREY
+HERMAN
+MAURICE
+VERNON
+ROBERTO
+CLYDE
+GLEN
+HECTOR
+SHANE
+RICARDO
+SAM
+RICK
+LESTER
+BRENT
+RAMON
+CHARLIE
+TYLER
+GILBERT
+GENE
+MARC
+REGINALD
+RUBEN
+BRETT
+ANGEL
+NATHANIEL
+RAFAEL
+LESLIE
+EDGAR
+MILTON
+RAUL
+BEN
+CHESTER
+CECIL
+DUANE
+FRANKLIN
+ANDRE
+ELMER
+BRAD
+GABRIEL
+RON
+MITCHELL
+ROLAND
+ARNOLD
+HARVEY
+JARED
+ADRIAN
+KARL
+CORY
+CLAUDE
+ERIK
+DARRYL
+JAMIE
+NEIL
+JESSIE
+CHRISTIAN
+JAVIER
+FERNANDO
+CLINTON
+TED
+MATHEW
+TYRONE
+DARREN
+LONNIE
+LANCE
+CODY
+JULIO
+KELLY
+KURT
+ALLAN
+NELSON
+GUY
+CLAYTON
+HUGH
+MAX
+DWAYNE
+DWIGHT
+ARMANDO
+FELIX
+JIMMIE
+EVERETT
+JORDAN
+IAN
+WALLACE
+KEN
+BOB
+JAIME
+CASEY
+ALFREDO
+ALBERTO
+DAVE
+IVAN
+JOHNNIE
+SIDNEY
+BYRON
+JULIAN
+ISAAC
+MORRIS
+CLIFTON
+WILLARD
+DARYL
+ROSS
+VIRGIL
+ANDY
+MARSHALL
+SALVADOR
+PERRY
+KIRK
+SERGIO
+MARION
+TRACY
+SETH
+KENT
+TERRANCE
+RENE
+EDUARDO
+TERRENCE
+ENRIQUE
+FREDDIE
+WADE
+AUSTIN
+STUART
+FREDRICK
+ARTURO
+ALEJANDRO
+JACKIE
+JOEY
+NICK
+LUTHER
+WENDELL
+JEREMIAH
+EVAN
+JULIUS
+DANA
+DONNIE
+OTIS
+SHANNON
+TREVOR
+OLIVER
+LUKE
+HOMER
+GERARD
+DOUG
+KENNY
+HUBERT
+ANGELO
+SHAUN
+LYLE
+MATT
+LYNN
+ALFONSO
+ORLANDO
+REX
+CARLTON
+ERNESTO
+CAMERON
+NEAL
+PABLO
+LORENZO
+OMAR
+WILBUR
+BLAKE
+GRANT
+HORACE
+RODERICK
+KERRY
+ABRAHAM
+WILLIS
+RICKEY
+JEAN
+IRA
+ANDRES
+CESAR
+JOHNATHAN
+MALCOLM
+RUDOLPH
+DAMON
+KELVIN
+RUDY
+PRESTON
+ALTON
+ARCHIE
+MARCO
+WM
+PETE
+RANDOLPH
+GARRY
+GEOFFREY
+JONATHON
+FELIPE
+BENNIE
+GERARDO
+ED
+DOMINIC
+ROBIN
+LOREN
+DELBERT
+COLIN
+GUILLERMO
+EARNEST
+LUCAS
+BENNY
+NOEL
+SPENCER
+RODOLFO
+MYRON
+EDMUND
+GARRETT
+SALVATORE
+CEDRIC
+LOWELL
+GREGG
+SHERMAN
+WILSON
+DEVIN
+SYLVESTER
+KIM
+ROOSEVELT
+ISRAEL
+JERMAINE
+FORREST
+WILBERT
+LELAND
+SIMON
+GUADALUPE
+CLARK
+IRVING
+CARROLL
+BRYANT
+OWEN
+RUFUS
+WOODROW
+SAMMY
+KRISTOPHER
+MACK
+LEVI
+MARCOS
+GUSTAVO
+JAKE
+LIONEL
+MARTY
+TAYLOR
+ELLIS
+DALLAS
+GILBERTO
+CLINT
+NICOLAS
+LAURENCE
+ISMAEL
+ORVILLE
+DREW
+JODY
+ERVIN
+DEWEY
+AL
+WILFRED
+JOSH
+HUGO
+IGNACIO
+CALEB
+TOMAS
+SHELDON
+ERICK
+FRANKIE
+STEWART
+DOYLE
+DARREL
+ROGELIO
+TERENCE
+SANTIAGO
+ALONZO
+ELIAS
+BERT
+ELBERT
+RAMIRO
+CONRAD
+PAT
+NOAH
+GRADY
+PHIL
+CORNELIUS
+LAMAR
+ROLANDO
+CLAY
+PERCY
+DEXTER
+BRADFORD
+MERLE
+DARIN
+AMOS
+TERRELL
+MOSES
+IRVIN
+SAUL
+ROMAN
+DARNELL
+RANDAL
+TOMMIE
+TIMMY
+DARRIN
+WINSTON
+BRENDAN
+TOBY
+VAN
+ABEL
+DOMINICK
+BOYD
+COURTNEY
+JAN
+EMILIO
+ELIJAH
+CARY
+DOMINGO
+SANTOS
+AUBREY
+EMMETT
+MARLON
+EMANUEL
+JERALD
+EDMOND
+EMIL
+DEWAYNE
+WILL
+OTTO
+TEDDY
+REYNALDO
+BRET
+MORGAN
+JESS
+TRENT
+HUMBERTO
+EMMANUEL
+STEPHAN
+LOUIE
+VICENTE
+LAMONT
+STACY
+GARLAND
+MILES
+MICAH
+EFRAIN
+BILLIE
+LOGAN
+HEATH
+RODGER
+HARLEY
+DEMETRIUS
+ETHAN
+ELDON
+ROCKY
+PIERRE
+JUNIOR
+FREDDY
+ELI
+BRYCE
+ANTOINE
+ROBBIE
+KENDALL
+ROYCE
+STERLING
+MICKEY
+CHASE
+GROVER
+ELTON
+CLEVELAND
+DYLAN
+CHUCK
+DAMIAN
+REUBEN
+STAN
+AUGUST
+LEONARDO
+JASPER
+RUSSEL
+ERWIN
+BENITO
+HANS
+MONTE
+BLAINE
+ERNIE
+CURT
+QUENTIN
+AGUSTIN
+MURRAY
+JAMAL
+DEVON
+ADOLFO
+HARRISON
+TYSON
+BURTON
+BRADY
+ELLIOTT
+WILFREDO
+BART
+JARROD
+VANCE
+DENIS
+DAMIEN
+JOAQUIN
+HARLAN
+DESMOND
+ELLIOT
+DARWIN
+ASHLEY
+GREGORIO
+BUDDY
+XAVIER
+KERMIT
+ROSCOE
+ESTEBAN
+ANTON
+SOLOMON
+SCOTTY
+NORBERT
+ELVIN
+WILLIAMS
+NOLAN
+CAREY
+ROD
+QUINTON
+HAL
+BRAIN
+ROB
+ELWOOD
+KENDRICK
+DARIUS
+MOISES
+SON
+MARLIN
+FIDEL
+THADDEUS
+CLIFF
+MARCEL
+ALI
+JACKSON
+RAPHAEL
+BRYON
+ARMAND
+ALVARO
+JEFFRY
+DANE
+JOESPH
+THURMAN
+NED
+SAMMIE
+RUSTY
+MICHEL
+MONTY
+RORY
+FABIAN
+REGGIE
+MASON
+GRAHAM
+KRIS
+ISAIAH
+VAUGHN
+GUS
+AVERY
+LOYD
+DIEGO
+ALEXIS
+ADOLPH
+NORRIS
+MILLARD
+ROCCO
+GONZALO
+DERICK
+RODRIGO
+GERRY
+STACEY
+CARMEN
+WILEY
+RIGOBERTO
+ALPHONSO
+TY
+SHELBY
+RICKIE
+NOE
+VERN
+BOBBIE
+REED
+JEFFERSON
+ELVIS
+BERNARDO
+MAURICIO
+HIRAM
+DONOVAN
+BASIL
+RILEY
+OLLIE
+NICKOLAS
+MAYNARD
+SCOT
+VINCE
+QUINCY
+EDDY
+SEBASTIAN
+FEDERICO
+ULYSSES
+HERIBERTO
+DONNELL
+COLE
+DENNY
+DAVIS
+GAVIN
+EMERY
+WARD
+ROMEO
+JAYSON
+DION
+DANTE
+CLEMENT
+COY
+ODELL
+MAXWELL
+JARVIS
+BRUNO
+ISSAC
+MARY
+DUDLEY
+BROCK
+SANFORD
+COLBY
+CARMELO
+BARNEY
+NESTOR
+HOLLIS
+STEFAN
+DONNY
+ART
+LINWOOD
+BEAU
+WELDON
+GALEN
+ISIDRO
+TRUMAN
+DELMAR
+JOHNATHON
+SILAS
+FREDERIC
+DICK
+KIRBY
+IRWIN
+CRUZ
+MERLIN
+MERRILL
+CHARLEY
+MARCELINO
+LANE
+HARRIS
+CLEO
+CARLO
+TRENTON
+KURTIS
+HUNTER
+AURELIO
+WINFRED
+VITO
+COLLIN
+DENVER
+CARTER
+LEONEL
+EMORY
+PASQUALE
+MOHAMMAD
+MARIANO
+DANIAL
+BLAIR
+LANDON
+DIRK
+BRANDEN
+ADAN
+NUMBERS
+CLAIR
+BUFORD
+GERMAN
+BERNIE
+WILMER
+JOAN
+EMERSON
+ZACHERY
+FLETCHER
+JACQUES
+ERROL
+DALTON
+MONROE
+JOSUE
+DOMINIQUE
+EDWARDO
+BOOKER
+WILFORD
+SONNY
+SHELTON
+CARSON
+THERON
+RAYMUNDO
+DAREN
+TRISTAN
+HOUSTON
+ROBBY
+LINCOLN
+JAME
+GENARO
+GALE
+BENNETT
+OCTAVIO
+CORNELL
+LAVERNE
+HUNG
+ARRON
+ANTONY
+HERSCHEL
+ALVA
+GIOVANNI
+GARTH
+CYRUS
+CYRIL
+RONNY
+STEVIE
+LON
+FREEMAN
+ERIN
+DUNCAN
+KENNITH
+CARMINE
+AUGUSTINE
+YOUNG
+ERICH
+CHADWICK
+WILBURN
+RUSS
+REID
+MYLES
+ANDERSON
+MORTON
+JONAS
+FOREST
+MITCHEL
+MERVIN
+ZANE
+RICH
+JAMEL
+LAZARO
+ALPHONSE
+RANDELL
+MAJOR
+JOHNIE
+JARRETT
+BROOKS
+ARIEL
+ABDUL
+DUSTY
+LUCIANO
+LINDSEY
+TRACEY
+SEYMOUR
+SCOTTIE
+EUGENIO
+MOHAMMED
+SANDY
+VALENTIN
+CHANCE
+ARNULFO
+LUCIEN
+FERDINAND
+THAD
+EZRA
+SYDNEY
+ALDO
+RUBIN
+ROYAL
+MITCH
+EARLE
+ABE
+WYATT
+MARQUIS
+LANNY
+KAREEM
+JAMAR
+BORIS
+ISIAH
+EMILE
+ELMO
+ARON
+LEOPOLDO
+EVERETTE
+JOSEF
+GAIL
+ELOY
+DORIAN
+RODRICK
+REINALDO
+LUCIO
+JERROD
+WESTON
+HERSHEL
+BARTON
+PARKER
+LEMUEL
+LAVERN
+BURT
+JULES
+GIL
+ELISEO
+AHMAD
+NIGEL
+EFREN
+ANTWAN
+ALDEN
+MARGARITO
+COLEMAN
+REFUGIO
+DINO
+OSVALDO
+LES
+DEANDRE
+NORMAND
+KIETH
+IVORY
+ANDREA
+TREY
+NORBERTO
+NAPOLEON
+JEROLD
+FRITZ
+ROSENDO
+MILFORD
+SANG
+DEON
+CHRISTOPER
+ALFONZO
+LYMAN
+JOSIAH
+BRANT
+WILTON
+RICO
+JAMAAL
+DEWITT
+CAROL
+BRENTON
+YONG
+OLIN
+FOSTER
+FAUSTINO
+CLAUDIO
+JUDSON
+GINO
+EDGARDO
+BERRY
+ALEC
+TANNER
+JARRED
+DONN
+TRINIDAD
+TAD
+SHIRLEY
+PRINCE
+PORFIRIO
+ODIS
+MARIA
+LENARD
+CHAUNCEY
+CHANG
+TOD
+MEL
+MARCELO
+KORY
+AUGUSTUS
+KEVEN
+HILARIO
+BUD
+SAL
+ROSARIO
+ORVAL
+MAURO
+DANNIE
+ZACHARIAH
+OLEN
+ANIBAL
+MILO
+JED
+FRANCES
+THANH
+DILLON
+AMADO
+NEWTON
+CONNIE
+LENNY
+TORY
+RICHIE
+LUPE
+HORACIO
+BRICE
+MOHAMED
+DELMER
+DARIO
+REYES
+DEE
+MAC
+JONAH
+JERROLD
+ROBT
+HANK
+SUNG
+RUPERT
+ROLLAND
+KENTON
+DAMION
+CHI
+ANTONE
+WALDO
+FREDRIC
+BRADLY
+QUINN
+KIP
+BURL
+WALKER
+TYREE
+JEFFEREY
+AHMED
+WILLY
+STANFORD
+OREN
+NOBLE
+MOSHE
+MIKEL
+ENOCH
+BRENDON
+QUINTIN
+JAMISON
+FLORENCIO
+DARRICK
+TOBIAS
+MINH
+HASSAN
+GIUSEPPE
+DEMARCUS
+CLETUS
+TYRELL
+LYNDON
+KEENAN
+WERNER
+THEO
+GERALDO
+LOU
+COLUMBUS
+CHET
+BERTRAM
+MARKUS
+HUEY
+HILTON
+DWAIN
+DONTE
+TYRON
+OMER
+ISAIAS
+HIPOLITO
+FERMIN
+CHUNG
+ADALBERTO
+VALENTINE
+JAMEY
+BO
+BARRETT
+WHITNEY
+TEODORO
+MCKINLEY
+MAXIMO
+GARFIELD
+SOL
+RALEIGH
+LAWERENCE
+ABRAM
+RASHAD
+KING
+EMMITT
+DARON
+CHONG
+SAMUAL
+PARIS
+OTHA
+MIQUEL
+LACY
+EUSEBIO
+DONG
+DOMENIC
+DARRON
+BUSTER
+ANTONIA
+WILBER
+RENATO
+JC
+HOYT
+HAYWOOD
+EZEKIEL
+CHAS
+FLORENTINO
+ELROY
+CLEMENTE
+ARDEN
+NEVILLE
+KELLEY
+EDISON
+DESHAWN
+CARROL
+SHAYNE
+NATHANIAL
+JORDON
+DANILO
+CLAUD
+VAL
+SHERWOOD
+RAYMON
+RAYFORD
+CRISTOBAL
+AMBROSE
+TITUS
+HYMAN
+FELTON
+EZEQUIEL
+ERASMO
+STANTON
+LONNY
+LEN
+IKE
+MILAN
+LINO
+JAROD
+HERB
+ANDREAS
+WALTON
+RHETT
+PALMER
+JUDE
+DOUGLASS
+CORDELL
+OSWALDO
+ELLSWORTH
+VIRGILIO
+TONEY
+NATHANAEL
+DEL
+BRITT
+BENEDICT
+MOSE
+HONG
+LEIGH
+JOHNSON
+ISREAL
+GAYLE
+GARRET
+FAUSTO
+ASA
+ARLEN
+ZACK
+WARNER
+MODESTO
+FRANCESCO
+MANUAL
+JAE
+GAYLORD
+GASTON
+FILIBERTO
+DEANGELO
+MICHALE
+GRANVILLE
+WES
+MALIK
+ZACKARY
+TUAN
+NICKY
+ELDRIDGE
+CRISTOPHER
+CORTEZ
+ANTIONE
+MALCOM
+LONG
+KOREY
+JOSPEH
+COLTON
+WAYLON
+VON
+HOSEA
+SHAD
+SANTO
+RUDOLF
+ROLF
+REY
+RENALDO
+MARCELLUS
+LUCIUS
+LESLEY
+KRISTOFER
+BOYCE
+BENTON
+MAN
+KASEY
+JEWELL
+HAYDEN
+HARLAND
+ARNOLDO
+RUEBEN
+LEANDRO
+KRAIG
+JERRELL
+JEROMY
+HOBERT
+CEDRICK
+ARLIE
+WINFORD
+WALLY
+PATRICIA
+LUIGI
+KENETH
+JACINTO
+GRAIG
+FRANKLYN
+EDMUNDO
+SID
+PORTER
+LEIF
+LAUREN
+JERAMY
+ELISHA
+BUCK
+WILLIAN
+VINCENZO
+SHON
+MICHAL
+LYNWOOD
+LINDSAY
+JEWEL
+JERE
+HAI
+ELDEN
+DORSEY
+DARELL
+BRODERICK
+ALONSO
diff --git a/tests/test15.ans b/tests/test15.ans
new file mode 100644
index 000000000..ad69a9470
--- /dev/null
+++ b/tests/test15.ans
@@ -0,0 +1 @@
+betty's id is 2
diff --git a/tests/test15.c b/tests/test15.c
new file mode 100644
index 000000000..56140928d
--- /dev/null
+++ b/tests/test15.c
@@ -0,0 +1,40 @@
+#include <string.h> /* strcpy */
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+#include "uthash.h"
+
+struct my_struct {
+ char name[10]; /* key */
+ int id;
+ UT_hash_handle hh; /* makes this structure hashable */
+};
+
+
+int main()
+{
+ const char **n, *names[] = { "joe", "bob", "betty", NULL };
+ struct my_struct *s, *tmp, *users = NULL;
+ int i=0;
+
+ for (n = names; *n != NULL; n++) {
+ s = (struct my_struct*)malloc(sizeof(struct my_struct));
+ if (s == NULL) {
+ exit(-1);
+ }
+ strcpy(s->name, *n);
+ s->id = i++;
+ HASH_ADD_STR( users, name, s );
+ }
+
+ HASH_FIND_STR( users, "betty", s);
+ if (s != NULL) {
+ printf("betty's id is %d\n", s->id);
+ }
+
+ /* free the hash table contents */
+ HASH_ITER(hh, users, s, tmp) {
+ HASH_DEL(users, s);
+ free(s);
+ }
+ return 0;
+}
diff --git a/tests/test16.ans b/tests/test16.ans
new file mode 100644
index 000000000..1b4d7e183
--- /dev/null
+++ b/tests/test16.ans
@@ -0,0 +1 @@
+found: user 5, unix time 157680000
diff --git a/tests/test16.c b/tests/test16.c
new file mode 100644
index 000000000..f9e2f853b
--- /dev/null
+++ b/tests/test16.c
@@ -0,0 +1,53 @@
+#include <stdlib.h> /* malloc */
+#include <stddef.h> /* offsetof */
+#include <stdio.h> /* printf */
+#include <string.h> /* memset */
+#include "uthash.h"
+
+struct inner {
+ int a;
+ int b;
+};
+
+struct my_event {
+ struct inner is; /* key is aggregate of this field */
+ char event_code; /* and this field. */
+ int user_id;
+ UT_hash_handle hh; /* makes this structure hashable */
+};
+
+
+int main()
+{
+ struct my_event *e, ev, *events = NULL;
+ unsigned keylen;
+ int i;
+
+ keylen = offsetof(struct my_event, event_code) + sizeof(char)
+ - offsetof(struct my_event, is);
+
+ for(i = 0; i < 10; i++) {
+ e = (struct my_event*)malloc(sizeof(struct my_event));
+ if (e == NULL) {
+ exit(-1);
+ }
+ memset(e,0,sizeof(struct my_event));
+ e->is.a = i * (60*60*24*365); /* i years (sec)*/
+ e->is.b = 0;
+ e->event_code = 'a'+(i%2); /* meaningless */
+ e->user_id = i;
+
+ HASH_ADD( hh, events, is, keylen, e);
+ }
+
+ /* look for one specific event */
+ memset(&ev,0,sizeof(struct my_event));
+ ev.is.a = 5 * (60*60*24*365);
+ ev.is.b = 0;
+ ev.event_code = 'b';
+ HASH_FIND( hh, events, &ev.is, keylen , e);
+ if (e != NULL) {
+ printf("found: user %d, unix time %d\n", e->user_id, e->is.a);
+ }
+ return 0;
+}
diff --git a/tests/test17.ans b/tests/test17.ans
new file mode 100644
index 000000000..92ae3ef25
--- /dev/null
+++ b/tests/test17.ans
@@ -0,0 +1,134 @@
+user 9, cookie 81
+user 8, cookie 64
+user 7, cookie 49
+user 6, cookie 36
+user 5, cookie 25
+user 4, cookie 16
+user 3, cookie 9
+user 2, cookie 4
+user 1, cookie 1
+user 0, cookie 0
+sorting
+called for a:9, b:8
+called for a:7, b:6
+called for a:5, b:4
+called for a:3, b:2
+called for a:1, b:0
+called for a:8, b:6
+called for a:8, b:7
+called for a:4, b:2
+called for a:4, b:3
+called for a:6, b:2
+called for a:6, b:3
+called for a:6, b:4
+called for a:6, b:5
+called for a:2, b:0
+called for a:2, b:1
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
+adding 10-20
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
+user 20, cookie 400
+user 19, cookie 361
+user 18, cookie 324
+user 17, cookie 289
+user 16, cookie 256
+user 15, cookie 225
+user 14, cookie 196
+user 13, cookie 169
+user 12, cookie 144
+user 11, cookie 121
+user 10, cookie 100
+sorting
+called for a:0, b:1
+called for a:2, b:3
+called for a:4, b:5
+called for a:6, b:7
+called for a:8, b:9
+called for a:20, b:19
+called for a:18, b:17
+called for a:16, b:15
+called for a:14, b:13
+called for a:12, b:11
+called for a:0, b:2
+called for a:1, b:2
+called for a:4, b:6
+called for a:5, b:6
+called for a:8, b:19
+called for a:9, b:19
+called for a:17, b:15
+called for a:17, b:16
+called for a:13, b:11
+called for a:13, b:12
+called for a:0, b:4
+called for a:1, b:4
+called for a:2, b:4
+called for a:3, b:4
+called for a:8, b:15
+called for a:9, b:15
+called for a:19, b:15
+called for a:19, b:16
+called for a:19, b:17
+called for a:19, b:18
+called for a:11, b:10
+called for a:0, b:8
+called for a:1, b:8
+called for a:2, b:8
+called for a:3, b:8
+called for a:4, b:8
+called for a:5, b:8
+called for a:6, b:8
+called for a:7, b:8
+called for a:0, b:10
+called for a:1, b:10
+called for a:2, b:10
+called for a:3, b:10
+called for a:4, b:10
+called for a:5, b:10
+called for a:6, b:10
+called for a:7, b:10
+called for a:8, b:10
+called for a:9, b:10
+called for a:15, b:10
+called for a:15, b:11
+called for a:15, b:12
+called for a:15, b:13
+called for a:15, b:14
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
+user 10, cookie 100
+user 11, cookie 121
+user 12, cookie 144
+user 13, cookie 169
+user 14, cookie 196
+user 15, cookie 225
+user 16, cookie 256
+user 17, cookie 289
+user 18, cookie 324
+user 19, cookie 361
+user 20, cookie 400
diff --git a/tests/test17.c b/tests/test17.c
new file mode 100644
index 000000000..cb6300128
--- /dev/null
+++ b/tests/test17.c
@@ -0,0 +1,63 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+static int rev(void *_a, void *_b)
+{
+ example_user_t *a = (example_user_t*)_a;
+ example_user_t *b = (example_user_t*)_b;
+ printf("called for a:%d, b:%d\n",a->id, b->id);
+ return (a->id - b->id);
+}
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL;
+
+ /* create elements */
+ for(i=9; i>=0; i--) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ printf("sorting\n");
+ HASH_SORT(users,rev);
+ for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+
+ printf("adding 10-20\n");
+ for(i=20; i>=10; i--) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+ for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ printf("sorting\n");
+ HASH_SORT(users,rev);
+ for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ return 0;
+}
diff --git a/tests/test18.ans b/tests/test18.ans
new file mode 100644
index 000000000..db48dcf10
--- /dev/null
+++ b/tests/test18.ans
@@ -0,0 +1,20 @@
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
+deleting id 0
+deleting id 1
+deleting id 2
+deleting id 3
+deleting id 4
+deleting id 5
+deleting id 6
+deleting id 7
+deleting id 8
+deleting id 9
diff --git a/tests/test18.c b/tests/test18.c
new file mode 100644
index 000000000..623cf569c
--- /dev/null
+++ b/tests/test18.c
@@ -0,0 +1,37 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+
+ /* delete them all, pathologically */
+ while(users != NULL) {
+ printf("deleting id %i\n", users->id);
+ HASH_DEL(users,users); /* single head/deletee var! */
+ }
+ return 0;
+}
diff --git a/tests/test19.ans b/tests/test19.ans
new file mode 100644
index 000000000..588d0d9a3
--- /dev/null
+++ b/tests/test19.ans
@@ -0,0 +1,1012 @@
+sorting users ascending
+user 0
+user 1
+user 2
+user 3
+user 4
+user 5
+user 6
+user 7
+user 8
+user 9
+sorting altusers descending
+altuser 999
+altuser 998
+altuser 997
+altuser 996
+altuser 995
+altuser 994
+altuser 993
+altuser 992
+altuser 991
+altuser 990
+altuser 989
+altuser 988
+altuser 987
+altuser 986
+altuser 985
+altuser 984
+altuser 983
+altuser 982
+altuser 981
+altuser 980
+altuser 979
+altuser 978
+altuser 977
+altuser 976
+altuser 975
+altuser 974
+altuser 973
+altuser 972
+altuser 971
+altuser 970
+altuser 969
+altuser 968
+altuser 967
+altuser 966
+altuser 965
+altuser 964
+altuser 963
+altuser 962
+altuser 961
+altuser 960
+altuser 959
+altuser 958
+altuser 957
+altuser 956
+altuser 955
+altuser 954
+altuser 953
+altuser 952
+altuser 951
+altuser 950
+altuser 949
+altuser 948
+altuser 947
+altuser 946
+altuser 945
+altuser 944
+altuser 943
+altuser 942
+altuser 941
+altuser 940
+altuser 939
+altuser 938
+altuser 937
+altuser 936
+altuser 935
+altuser 934
+altuser 933
+altuser 932
+altuser 931
+altuser 930
+altuser 929
+altuser 928
+altuser 927
+altuser 926
+altuser 925
+altuser 924
+altuser 923
+altuser 922
+altuser 921
+altuser 920
+altuser 919
+altuser 918
+altuser 917
+altuser 916
+altuser 915
+altuser 914
+altuser 913
+altuser 912
+altuser 911
+altuser 910
+altuser 909
+altuser 908
+altuser 907
+altuser 906
+altuser 905
+altuser 904
+altuser 903
+altuser 902
+altuser 901
+altuser 900
+altuser 899
+altuser 898
+altuser 897
+altuser 896
+altuser 895
+altuser 894
+altuser 893
+altuser 892
+altuser 891
+altuser 890
+altuser 889
+altuser 888
+altuser 887
+altuser 886
+altuser 885
+altuser 884
+altuser 883
+altuser 882
+altuser 881
+altuser 880
+altuser 879
+altuser 878
+altuser 877
+altuser 876
+altuser 875
+altuser 874
+altuser 873
+altuser 872
+altuser 871
+altuser 870
+altuser 869
+altuser 868
+altuser 867
+altuser 866
+altuser 865
+altuser 864
+altuser 863
+altuser 862
+altuser 861
+altuser 860
+altuser 859
+altuser 858
+altuser 857
+altuser 856
+altuser 855
+altuser 854
+altuser 853
+altuser 852
+altuser 851
+altuser 850
+altuser 849
+altuser 848
+altuser 847
+altuser 846
+altuser 845
+altuser 844
+altuser 843
+altuser 842
+altuser 841
+altuser 840
+altuser 839
+altuser 838
+altuser 837
+altuser 836
+altuser 835
+altuser 834
+altuser 833
+altuser 832
+altuser 831
+altuser 830
+altuser 829
+altuser 828
+altuser 827
+altuser 826
+altuser 825
+altuser 824
+altuser 823
+altuser 822
+altuser 821
+altuser 820
+altuser 819
+altuser 818
+altuser 817
+altuser 816
+altuser 815
+altuser 814
+altuser 813
+altuser 812
+altuser 811
+altuser 810
+altuser 809
+altuser 808
+altuser 807
+altuser 806
+altuser 805
+altuser 804
+altuser 803
+altuser 802
+altuser 801
+altuser 800
+altuser 799
+altuser 798
+altuser 797
+altuser 796
+altuser 795
+altuser 794
+altuser 793
+altuser 792
+altuser 791
+altuser 790
+altuser 789
+altuser 788
+altuser 787
+altuser 786
+altuser 785
+altuser 784
+altuser 783
+altuser 782
+altuser 781
+altuser 780
+altuser 779
+altuser 778
+altuser 777
+altuser 776
+altuser 775
+altuser 774
+altuser 773
+altuser 772
+altuser 771
+altuser 770
+altuser 769
+altuser 768
+altuser 767
+altuser 766
+altuser 765
+altuser 764
+altuser 763
+altuser 762
+altuser 761
+altuser 760
+altuser 759
+altuser 758
+altuser 757
+altuser 756
+altuser 755
+altuser 754
+altuser 753
+altuser 752
+altuser 751
+altuser 750
+altuser 749
+altuser 748
+altuser 747
+altuser 746
+altuser 745
+altuser 744
+altuser 743
+altuser 742
+altuser 741
+altuser 740
+altuser 739
+altuser 738
+altuser 737
+altuser 736
+altuser 735
+altuser 734
+altuser 733
+altuser 732
+altuser 731
+altuser 730
+altuser 729
+altuser 728
+altuser 727
+altuser 726
+altuser 725
+altuser 724
+altuser 723
+altuser 722
+altuser 721
+altuser 720
+altuser 719
+altuser 718
+altuser 717
+altuser 716
+altuser 715
+altuser 714
+altuser 713
+altuser 712
+altuser 711
+altuser 710
+altuser 709
+altuser 708
+altuser 707
+altuser 706
+altuser 705
+altuser 704
+altuser 703
+altuser 702
+altuser 701
+altuser 700
+altuser 699
+altuser 698
+altuser 697
+altuser 696
+altuser 695
+altuser 694
+altuser 693
+altuser 692
+altuser 691
+altuser 690
+altuser 689
+altuser 688
+altuser 687
+altuser 686
+altuser 685
+altuser 684
+altuser 683
+altuser 682
+altuser 681
+altuser 680
+altuser 679
+altuser 678
+altuser 677
+altuser 676
+altuser 675
+altuser 674
+altuser 673
+altuser 672
+altuser 671
+altuser 670
+altuser 669
+altuser 668
+altuser 667
+altuser 666
+altuser 665
+altuser 664
+altuser 663
+altuser 662
+altuser 661
+altuser 660
+altuser 659
+altuser 658
+altuser 657
+altuser 656
+altuser 655
+altuser 654
+altuser 653
+altuser 652
+altuser 651
+altuser 650
+altuser 649
+altuser 648
+altuser 647
+altuser 646
+altuser 645
+altuser 644
+altuser 643
+altuser 642
+altuser 641
+altuser 640
+altuser 639
+altuser 638
+altuser 637
+altuser 636
+altuser 635
+altuser 634
+altuser 633
+altuser 632
+altuser 631
+altuser 630
+altuser 629
+altuser 628
+altuser 627
+altuser 626
+altuser 625
+altuser 624
+altuser 623
+altuser 622
+altuser 621
+altuser 620
+altuser 619
+altuser 618
+altuser 617
+altuser 616
+altuser 615
+altuser 614
+altuser 613
+altuser 612
+altuser 611
+altuser 610
+altuser 609
+altuser 608
+altuser 607
+altuser 606
+altuser 605
+altuser 604
+altuser 603
+altuser 602
+altuser 601
+altuser 600
+altuser 599
+altuser 598
+altuser 597
+altuser 596
+altuser 595
+altuser 594
+altuser 593
+altuser 592
+altuser 591
+altuser 590
+altuser 589
+altuser 588
+altuser 587
+altuser 586
+altuser 585
+altuser 584
+altuser 583
+altuser 582
+altuser 581
+altuser 580
+altuser 579
+altuser 578
+altuser 577
+altuser 576
+altuser 575
+altuser 574
+altuser 573
+altuser 572
+altuser 571
+altuser 570
+altuser 569
+altuser 568
+altuser 567
+altuser 566
+altuser 565
+altuser 564
+altuser 563
+altuser 562
+altuser 561
+altuser 560
+altuser 559
+altuser 558
+altuser 557
+altuser 556
+altuser 555
+altuser 554
+altuser 553
+altuser 552
+altuser 551
+altuser 550
+altuser 549
+altuser 548
+altuser 547
+altuser 546
+altuser 545
+altuser 544
+altuser 543
+altuser 542
+altuser 541
+altuser 540
+altuser 539
+altuser 538
+altuser 537
+altuser 536
+altuser 535
+altuser 534
+altuser 533
+altuser 532
+altuser 531
+altuser 530
+altuser 529
+altuser 528
+altuser 527
+altuser 526
+altuser 525
+altuser 524
+altuser 523
+altuser 522
+altuser 521
+altuser 520
+altuser 519
+altuser 518
+altuser 517
+altuser 516
+altuser 515
+altuser 514
+altuser 513
+altuser 512
+altuser 511
+altuser 510
+altuser 509
+altuser 508
+altuser 507
+altuser 506
+altuser 505
+altuser 504
+altuser 503
+altuser 502
+altuser 501
+altuser 500
+altuser 499
+altuser 498
+altuser 497
+altuser 496
+altuser 495
+altuser 494
+altuser 493
+altuser 492
+altuser 491
+altuser 490
+altuser 489
+altuser 488
+altuser 487
+altuser 486
+altuser 485
+altuser 484
+altuser 483
+altuser 482
+altuser 481
+altuser 480
+altuser 479
+altuser 478
+altuser 477
+altuser 476
+altuser 475
+altuser 474
+altuser 473
+altuser 472
+altuser 471
+altuser 470
+altuser 469
+altuser 468
+altuser 467
+altuser 466
+altuser 465
+altuser 464
+altuser 463
+altuser 462
+altuser 461
+altuser 460
+altuser 459
+altuser 458
+altuser 457
+altuser 456
+altuser 455
+altuser 454
+altuser 453
+altuser 452
+altuser 451
+altuser 450
+altuser 449
+altuser 448
+altuser 447
+altuser 446
+altuser 445
+altuser 444
+altuser 443
+altuser 442
+altuser 441
+altuser 440
+altuser 439
+altuser 438
+altuser 437
+altuser 436
+altuser 435
+altuser 434
+altuser 433
+altuser 432
+altuser 431
+altuser 430
+altuser 429
+altuser 428
+altuser 427
+altuser 426
+altuser 425
+altuser 424
+altuser 423
+altuser 422
+altuser 421
+altuser 420
+altuser 419
+altuser 418
+altuser 417
+altuser 416
+altuser 415
+altuser 414
+altuser 413
+altuser 412
+altuser 411
+altuser 410
+altuser 409
+altuser 408
+altuser 407
+altuser 406
+altuser 405
+altuser 404
+altuser 403
+altuser 402
+altuser 401
+altuser 400
+altuser 399
+altuser 398
+altuser 397
+altuser 396
+altuser 395
+altuser 394
+altuser 393
+altuser 392
+altuser 391
+altuser 390
+altuser 389
+altuser 388
+altuser 387
+altuser 386
+altuser 385
+altuser 384
+altuser 383
+altuser 382
+altuser 381
+altuser 380
+altuser 379
+altuser 378
+altuser 377
+altuser 376
+altuser 375
+altuser 374
+altuser 373
+altuser 372
+altuser 371
+altuser 370
+altuser 369
+altuser 368
+altuser 367
+altuser 366
+altuser 365
+altuser 364
+altuser 363
+altuser 362
+altuser 361
+altuser 360
+altuser 359
+altuser 358
+altuser 357
+altuser 356
+altuser 355
+altuser 354
+altuser 353
+altuser 352
+altuser 351
+altuser 350
+altuser 349
+altuser 348
+altuser 347
+altuser 346
+altuser 345
+altuser 344
+altuser 343
+altuser 342
+altuser 341
+altuser 340
+altuser 339
+altuser 338
+altuser 337
+altuser 336
+altuser 335
+altuser 334
+altuser 333
+altuser 332
+altuser 331
+altuser 330
+altuser 329
+altuser 328
+altuser 327
+altuser 326
+altuser 325
+altuser 324
+altuser 323
+altuser 322
+altuser 321
+altuser 320
+altuser 319
+altuser 318
+altuser 317
+altuser 316
+altuser 315
+altuser 314
+altuser 313
+altuser 312
+altuser 311
+altuser 310
+altuser 309
+altuser 308
+altuser 307
+altuser 306
+altuser 305
+altuser 304
+altuser 303
+altuser 302
+altuser 301
+altuser 300
+altuser 299
+altuser 298
+altuser 297
+altuser 296
+altuser 295
+altuser 294
+altuser 293
+altuser 292
+altuser 291
+altuser 290
+altuser 289
+altuser 288
+altuser 287
+altuser 286
+altuser 285
+altuser 284
+altuser 283
+altuser 282
+altuser 281
+altuser 280
+altuser 279
+altuser 278
+altuser 277
+altuser 276
+altuser 275
+altuser 274
+altuser 273
+altuser 272
+altuser 271
+altuser 270
+altuser 269
+altuser 268
+altuser 267
+altuser 266
+altuser 265
+altuser 264
+altuser 263
+altuser 262
+altuser 261
+altuser 260
+altuser 259
+altuser 258
+altuser 257
+altuser 256
+altuser 255
+altuser 254
+altuser 253
+altuser 252
+altuser 251
+altuser 250
+altuser 249
+altuser 248
+altuser 247
+altuser 246
+altuser 245
+altuser 244
+altuser 243
+altuser 242
+altuser 241
+altuser 240
+altuser 239
+altuser 238
+altuser 237
+altuser 236
+altuser 235
+altuser 234
+altuser 233
+altuser 232
+altuser 231
+altuser 230
+altuser 229
+altuser 228
+altuser 227
+altuser 226
+altuser 225
+altuser 224
+altuser 223
+altuser 222
+altuser 221
+altuser 220
+altuser 219
+altuser 218
+altuser 217
+altuser 216
+altuser 215
+altuser 214
+altuser 213
+altuser 212
+altuser 211
+altuser 210
+altuser 209
+altuser 208
+altuser 207
+altuser 206
+altuser 205
+altuser 204
+altuser 203
+altuser 202
+altuser 201
+altuser 200
+altuser 199
+altuser 198
+altuser 197
+altuser 196
+altuser 195
+altuser 194
+altuser 193
+altuser 192
+altuser 191
+altuser 190
+altuser 189
+altuser 188
+altuser 187
+altuser 186
+altuser 185
+altuser 184
+altuser 183
+altuser 182
+altuser 181
+altuser 180
+altuser 179
+altuser 178
+altuser 177
+altuser 176
+altuser 175
+altuser 174
+altuser 173
+altuser 172
+altuser 171
+altuser 170
+altuser 169
+altuser 168
+altuser 167
+altuser 166
+altuser 165
+altuser 164
+altuser 163
+altuser 162
+altuser 161
+altuser 160
+altuser 159
+altuser 158
+altuser 157
+altuser 156
+altuser 155
+altuser 154
+altuser 153
+altuser 152
+altuser 151
+altuser 150
+altuser 149
+altuser 148
+altuser 147
+altuser 146
+altuser 145
+altuser 144
+altuser 143
+altuser 142
+altuser 141
+altuser 140
+altuser 139
+altuser 138
+altuser 137
+altuser 136
+altuser 135
+altuser 134
+altuser 133
+altuser 132
+altuser 131
+altuser 130
+altuser 129
+altuser 128
+altuser 127
+altuser 126
+altuser 125
+altuser 124
+altuser 123
+altuser 122
+altuser 121
+altuser 120
+altuser 119
+altuser 118
+altuser 117
+altuser 116
+altuser 115
+altuser 114
+altuser 113
+altuser 112
+altuser 111
+altuser 110
+altuser 109
+altuser 108
+altuser 107
+altuser 106
+altuser 105
+altuser 104
+altuser 103
+altuser 102
+altuser 101
+altuser 100
+altuser 99
+altuser 98
+altuser 97
+altuser 96
+altuser 95
+altuser 94
+altuser 93
+altuser 92
+altuser 91
+altuser 90
+altuser 89
+altuser 88
+altuser 87
+altuser 86
+altuser 85
+altuser 84
+altuser 83
+altuser 82
+altuser 81
+altuser 80
+altuser 79
+altuser 78
+altuser 77
+altuser 76
+altuser 75
+altuser 74
+altuser 73
+altuser 72
+altuser 71
+altuser 70
+altuser 69
+altuser 68
+altuser 67
+altuser 66
+altuser 65
+altuser 64
+altuser 63
+altuser 62
+altuser 61
+altuser 60
+altuser 59
+altuser 58
+altuser 57
+altuser 56
+altuser 55
+altuser 54
+altuser 53
+altuser 52
+altuser 51
+altuser 50
+altuser 49
+altuser 48
+altuser 47
+altuser 46
+altuser 45
+altuser 44
+altuser 43
+altuser 42
+altuser 41
+altuser 40
+altuser 39
+altuser 38
+altuser 37
+altuser 36
+altuser 35
+altuser 34
+altuser 33
+altuser 32
+altuser 31
+altuser 30
+altuser 29
+altuser 28
+altuser 27
+altuser 26
+altuser 25
+altuser 24
+altuser 23
+altuser 22
+altuser 21
+altuser 20
+altuser 19
+altuser 18
+altuser 17
+altuser 16
+altuser 15
+altuser 14
+altuser 13
+altuser 12
+altuser 11
+altuser 10
+altuser 9
+altuser 8
+altuser 7
+altuser 6
+altuser 5
+altuser 4
+altuser 3
+altuser 2
+altuser 1
+altuser 0
diff --git a/tests/test19.c b/tests/test19.c
new file mode 100644
index 000000000..aec898f8f
--- /dev/null
+++ b/tests/test19.c
@@ -0,0 +1,66 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+ UT_hash_handle alth;
+} example_user_t;
+
+static int ascending_sort(void *_a, void *_b)
+{
+ example_user_t *a = (example_user_t*)_a;
+ example_user_t *b = (example_user_t*)_b;
+ if (a->id == b->id) {
+ return 0;
+ }
+ return (a->id < b->id) ? -1 : 1;
+}
+
+static int descending_sort(void *_a, void *_b)
+{
+ example_user_t *a = (example_user_t*)_a;
+ example_user_t *b = (example_user_t*)_b;
+ if (a->id == b->id) {
+ return 0;
+ }
+ return (a->id < b->id) ? 1 : -1;
+}
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL, *altusers=NULL;
+
+ /* create elements */
+ for(i=0; i<1000; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ if (i<10) {
+ HASH_ADD_INT(users,id,user);
+ }
+ HASH_ADD(alth,altusers,id,sizeof(int),user);
+ }
+
+ printf("sorting users ascending\n");
+ HASH_SRT(hh,users,ascending_sort);
+ for(user=users; user!=NULL; user=(example_user_t*)user->hh.next) {
+ printf("user %d\n", user->id);
+ }
+
+ printf("sorting altusers descending\n");
+ HASH_SRT(alth,altusers,descending_sort);
+ for(user=altusers; user!=NULL; user=(example_user_t*)user->alth.next) {
+ printf("altuser %d\n", user->id);
+ }
+
+ /* HASH_FSCK(hh,users); */
+ /* HASH_FSCK(alth,altusers); */
+ return 0;
+}
diff --git a/tests/test2.ans b/tests/test2.ans
new file mode 100644
index 000000000..fd4e2f8ad
--- /dev/null
+++ b/tests/test2.ans
@@ -0,0 +1,5 @@
+user id 0 found, cookie 0
+user id 2 found, cookie 4
+user id 4 found, cookie 16
+user id 6 found, cookie 36
+user id 8 found, cookie 64
diff --git a/tests/test2.c b/tests/test2.c
new file mode 100644
index 000000000..30bbb2f11
--- /dev/null
+++ b/tests/test2.c
@@ -0,0 +1,38 @@
+#include "uthash.h"
+#include <time.h>
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ /* find each even ID */
+ for(i=0; i<10; i+=2) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ printf("user id %d found, cookie %d\n", tmp->id, tmp->cookie);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+ return 0;
+}
diff --git a/tests/test20.ans b/tests/test20.ans
new file mode 100644
index 000000000..4d3bb1d0b
--- /dev/null
+++ b/tests/test20.ans
@@ -0,0 +1 @@
+found
diff --git a/tests/test20.c b/tests/test20.c
new file mode 100644
index 000000000..9ac01f0ac
--- /dev/null
+++ b/tests/test20.c
@@ -0,0 +1,34 @@
+#include <string.h> /* memcpy */
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+#include "uthash.h"
+
+struct my_struct {
+ char bkey[5]; /* "binary" key */
+ int data;
+ UT_hash_handle hh;
+};
+
+int main()
+{
+ struct my_struct *s, *t, *bins = NULL;
+ char binary[5] = {'\3','\1','\4','\1','\6'};
+
+ /* allocate our structure. initialize to some values */
+ s = (struct my_struct*)calloc(1UL,sizeof(struct my_struct));
+ if (s == NULL) {
+ exit(-1);
+ }
+ memcpy(s->bkey, binary, sizeof(binary));
+
+ /* add to hash table using general macro */
+ HASH_ADD( hh, bins, bkey, sizeof(binary), s);
+
+ /* look up the structure we just added */
+ HASH_FIND( hh, bins, binary, sizeof(binary), t );
+
+ if (t != NULL) {
+ printf("found\n");
+ }
+ return 0;
+}
diff --git a/tests/test21.ans b/tests/test21.ans
new file mode 100644
index 000000000..af89f48c2
--- /dev/null
+++ b/tests/test21.ans
@@ -0,0 +1 @@
+found a 1
diff --git a/tests/test21.c b/tests/test21.c
new file mode 100644
index 000000000..b4000df1d
--- /dev/null
+++ b/tests/test21.c
@@ -0,0 +1,44 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "uthash.h"
+
+typedef struct {
+ char a;
+ int b;
+} record_key_t;
+
+typedef struct {
+ record_key_t key;
+ /* ... other data ... */
+ UT_hash_handle hh;
+} record_t;
+
+int main()
+{
+ record_t l, *p, *r, *tmp, *records = NULL;
+
+ r = (record_t*)malloc( sizeof(record_t) );
+ if (r == NULL) {
+ exit(-1);
+ }
+ memset(r, 0, sizeof(record_t));
+ r->key.a = 'a';
+ r->key.b = 1;
+ HASH_ADD(hh, records, key, sizeof(record_key_t), r);
+
+ memset(&l, 0, sizeof(record_t));
+ l.key.a = 'a';
+ l.key.b = 1;
+ HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);
+
+ if (p != NULL) {
+ printf("found %c %d\n", p->key.a, p->key.b);
+ }
+
+ HASH_ITER(hh, records, p, tmp) {
+ HASH_DEL(records, p);
+ free(p);
+ }
+ return 0;
+}
+
diff --git a/tests/test22.ans b/tests/test22.ans
new file mode 100644
index 000000000..2483a24d6
--- /dev/null
+++ b/tests/test22.ans
@@ -0,0 +1 @@
+found
diff --git a/tests/test22.c b/tests/test22.c
new file mode 100644
index 000000000..d013db925
--- /dev/null
+++ b/tests/test22.c
@@ -0,0 +1,68 @@
+#include <stdlib.h> /* malloc */
+#include <stddef.h> /* offsetof */
+#include <stdio.h> /* printf */
+#include <string.h> /* memset */
+#include "uthash.h"
+
+#define UTF32 '\x1'
+
+typedef struct {
+ UT_hash_handle hh;
+ size_t len;
+ char encoding; /* these two fields */
+ int text[]; /* comprise the key */
+} msg_t;
+
+typedef struct {
+ char encoding;
+ int text[];
+} lookup_key_t;
+
+int main()
+{
+ unsigned keylen;
+ msg_t *msg, *tmp, *msgs = NULL;
+ lookup_key_t *lookup_key;
+
+ int beijing[] = {0x5317, 0x4eac}; /* UTF-32LE for 北京 */
+
+ /* allocate and initialize our structure */
+ msg = (msg_t*)malloc( sizeof(msg_t) + sizeof(beijing) );
+ if (msg == NULL) {
+ exit(-1);
+ }
+ memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */
+ msg->len = sizeof(beijing);
+ msg->encoding = UTF32;
+ memcpy(msg->text, beijing, sizeof(beijing));
+
+ /* calculate the key length including padding, using formula */
+ keylen = offsetof(msg_t, text) /* offset of last key field */
+ + sizeof(beijing) /* size of last key field */
+ - offsetof(msg_t, encoding); /* offset of first key field */
+
+ /* add our structure to the hash table */
+ HASH_ADD( hh, msgs, encoding, keylen, msg);
+
+ /* look it up to prove that it worked :-) */
+ msg=NULL;
+
+ lookup_key = (lookup_key_t*)malloc(sizeof(*lookup_key) + sizeof(beijing));
+ if (lookup_key == NULL) {
+ exit(-1);
+ }
+ memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing));
+ lookup_key->encoding = UTF32;
+ memcpy(lookup_key->text, beijing, sizeof(beijing));
+ HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg );
+ if (msg != NULL) {
+ printf("found \n");
+ }
+ free(lookup_key);
+
+ HASH_ITER(hh, msgs, msg, tmp) {
+ HASH_DEL(msgs, msg);
+ free(msg);
+ }
+ return 0;
+}
diff --git a/tests/test23.ans b/tests/test23.ans
new file mode 100644
index 000000000..79ed10a1a
--- /dev/null
+++ b/tests/test23.ans
@@ -0,0 +1,6 @@
+found 12345
+found 6789
+found 98765
+deleting 12345
+deleting 6789
+deleting 98765
diff --git a/tests/test23.c b/tests/test23.c
new file mode 100644
index 000000000..132da4207
--- /dev/null
+++ b/tests/test23.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+typedef struct {
+ int key;
+ int data;
+ UT_hash_handle hh;
+} item;
+
+int main()
+{
+ item *i, *j, *items=NULL;
+ int k;
+
+ /* first item */
+ k = 12345;
+ i = (item*)malloc(sizeof(item));
+ if (i == NULL) {
+ exit(-1);
+ }
+ i->key = k;
+ i->data = 0;
+ HASH_ADD_INT(items,key,i);
+
+ /* second item */
+ k = 6789;
+ i = (item*)malloc(sizeof(item));
+ if (i == NULL) {
+ exit(-1);
+ }
+ i->key = k;
+ i->data = 0;
+ HASH_ADD_INT(items,key,i);
+
+ /* third item */
+ k = 98765;
+ i = (item*)malloc(sizeof(item));
+ if (i == NULL) {
+ exit(-1);
+ }
+ i->key = k;
+ i->data = 0;
+ HASH_ADD_INT(items,key,i);
+
+ /* look them all up */
+ k = 12345;
+ HASH_FIND_INT(items, &k, j);
+ if (j != NULL) {
+ printf("found %d\n",k);
+ }
+ k = 6789;
+ HASH_FIND_INT(items, &k, j);
+ if (j != NULL) {
+ printf("found %d\n",k);
+ }
+ k = 98765;
+ HASH_FIND_INT(items, &k, j);
+ if (j != NULL) {
+ printf("found %d\n",k);
+ }
+
+ /* delete them not the way we prefer but it works */
+ for(j=items; j != NULL; j=(item*)j->hh.next) {
+ printf("deleting %d\n", j->key);
+ HASH_DEL(items,j);
+ }
+ return 0;
+}
diff --git a/tests/test24.ans b/tests/test24.ans
new file mode 100644
index 000000000..4a2034ac8
--- /dev/null
+++ b/tests/test24.ans
@@ -0,0 +1 @@
+hash contains 10 items
diff --git a/tests/test24.c b/tests/test24.c
new file mode 100644
index 000000000..459d6bd01
--- /dev/null
+++ b/tests/test24.c
@@ -0,0 +1,29 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ printf("hash contains %u items\n", HASH_COUNT(users));
+ return 0;
+}
diff --git a/tests/test25.ans b/tests/test25.ans
new file mode 100644
index 000000000..9490482f4
--- /dev/null
+++ b/tests/test25.ans
@@ -0,0 +1,31 @@
+CDL macros
+c b a
+count = 3
+advancing head pointer
+b a c
+b a c b a c b a c b
+b c a b c a b c a b
+deleting b
+a c
+deleting (a)
+c
+deleting (c)
+
+DL macros
+a b c
+count = 3
+deleting tail c
+a b
+deleting head a
+b
+deleting head b
+
+LL macros
+a b c
+count = 3
+deleting tail c
+a b
+deleting head a
+b
+deleting head b
+
diff --git a/tests/test25.c b/tests/test25.c
new file mode 100644
index 000000000..22e3761d8
--- /dev/null
+++ b/tests/test25.c
@@ -0,0 +1,138 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ int count;
+ el els[10], *e;
+ el *head = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL macros\n");
+ CDL_PREPEND(head,&els[0]);
+ CDL_PREPEND(head,&els[1]);
+ CDL_PREPEND(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ /* point head to head->next */
+ printf("advancing head pointer\n");
+ head = head->next;
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop backwards a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ CDL_DELETE(head,&els[1]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (a)\n");
+ CDL_DELETE(head,&els[0]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (c)\n");
+ CDL_DELETE(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_APPEND(head,&els[0]);
+ DL_APPEND(head,&els[1]);
+ DL_APPEND(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ printf("deleting tail c\n");
+ DL_DELETE(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head a\n");
+ DL_DELETE(head,&els[0]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head b\n");
+ DL_DELETE(head,&els[1]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_APPEND(head,&els[0]);
+ LL_APPEND(head,&els[1]);
+ LL_APPEND(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ LL_COUNT(head,e,count);
+ printf("count = %d\n", count);
+
+ printf("deleting tail c\n");
+ LL_DELETE(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head a\n");
+ LL_DELETE(head,&els[0]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head b\n");
+ LL_DELETE(head,&els[1]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test26.ans b/tests/test26.ans
new file mode 100644
index 000000000..764b01d2d
--- /dev/null
+++ b/tests/test26.ans
@@ -0,0 +1,53 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
+found WES
+
diff --git a/tests/test26.c b/tests/test26.c
new file mode 100644
index 000000000..6e9d96214
--- /dev/null
+++ b/tests/test26.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+static int namecmp(void *_a, void *_b)
+{
+ el *a = (el*)_a;
+ el *b = (el*)_b;
+ return strcmp(a->bname,b->bname);
+}
+
+int main()
+{
+ el *name, *elt, *tmp, etmp;
+ el *head = NULL; /* important- initialize to NULL! */
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ DL_APPEND(head, name);
+ }
+ DL_SORT(head, namecmp);
+ DL_FOREACH(head,elt) {
+ printf("%s", elt->bname);
+ }
+
+ memcpy(etmp.bname, "WES\n", 5UL);
+ DL_SEARCH(head,elt,&etmp,namecmp);
+ if (elt != NULL) {
+ printf("found %s\n", elt->bname);
+ }
+
+ /* now delete each element, use the safe iterator */
+ DL_FOREACH_SAFE(head,elt,tmp) {
+ DL_DELETE(head,elt);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test27.ans b/tests/test27.ans
new file mode 100644
index 000000000..80b476c66
--- /dev/null
+++ b/tests/test27.ans
@@ -0,0 +1,28 @@
+CDL macros
+c b a
+advancing head pointer
+b a c
+b a c b a c b a c b
+b c a b c a b c a b
+deleting b
+a c
+deleting head (a)
+c
+deleting new head (c)
+
+DL macros
+c b a
+deleting c
+b a
+deleting a
+b
+deleting b
+
+LL macros
+c b a
+deleting c
+b a
+deleting a
+b
+deleting b
+
diff --git a/tests/test27.c b/tests/test27.c
new file mode 100644
index 000000000..73842ee2c
--- /dev/null
+++ b/tests/test27.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[10], *e;
+ el *head = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL macros\n");
+ CDL_PREPEND(head,&els[0]);
+ CDL_PREPEND(head,&els[1]);
+ CDL_PREPEND(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* point head to head->next */
+ printf("advancing head pointer\n");
+ head = head->next;
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop backwards a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ CDL_DELETE(head,&els[1]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting head (a)\n");
+ CDL_DELETE(head,&els[0]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting new head (c)\n");
+ CDL_DELETE(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_PREPEND(head,&els[0]);
+ DL_PREPEND(head,&els[1]);
+ DL_PREPEND(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting c\n");
+ DL_DELETE(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting a\n");
+ DL_DELETE(head,&els[0]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ DL_DELETE(head,&els[1]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_PREPEND(head,&els[0]);
+ LL_PREPEND(head,&els[1]);
+ LL_PREPEND(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting c\n");
+ LL_DELETE(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting a\n");
+ LL_DELETE(head,&els[0]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ LL_DELETE(head,&els[1]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test28.ans b/tests/test28.ans
new file mode 100644
index 000000000..eb88650af
--- /dev/null
+++ b/tests/test28.ans
@@ -0,0 +1,34 @@
+CDL macros
+d c b a
+advancing head pointer
+c b a d
+c b a d c b a d c b
+c d a b c d a b c d
+deleting b
+c a d
+deleting (a)
+c d
+deleting (c)
+d
+deleting (d)
+
+DL macros
+c b a d
+deleting c
+b a d
+deleting a
+b d
+deleting b
+d
+deleting d
+
+LL macros
+c b a d
+deleting c
+b a d
+deleting a
+b d
+deleting b
+d
+deleting d
+
diff --git a/tests/test28.c b/tests/test28.c
new file mode 100644
index 000000000..da32c082c
--- /dev/null
+++ b/tests/test28.c
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[10], *e;
+ el *head = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL macros\n");
+ CDL_PREPEND(head,&els[0]);
+ CDL_PREPEND(head,&els[1]);
+ CDL_PREPEND(head,&els[2]);
+ CDL_PREPEND(head,&els[3]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* point head to head->next */
+ printf("advancing head pointer\n");
+ head = head->next;
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop backwards a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ CDL_DELETE(head,&els[1]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (a)\n");
+ CDL_DELETE(head,&els[0]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (c)\n");
+ CDL_DELETE(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (d)\n");
+ CDL_DELETE(head,&els[3]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_PREPEND(head,&els[0]);
+ DL_PREPEND(head,&els[1]);
+ DL_PREPEND(head,&els[2]);
+ DL_APPEND(head,&els[3]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting c\n");
+ DL_DELETE(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting a\n");
+ DL_DELETE(head,&els[0]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ DL_DELETE(head,&els[1]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting d\n");
+ DL_DELETE(head,&els[3]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_PREPEND(head,&els[0]);
+ LL_PREPEND(head,&els[1]);
+ LL_PREPEND(head,&els[2]);
+ LL_APPEND(head,&els[3]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting c\n");
+ LL_DELETE(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting a\n");
+ LL_DELETE(head,&els[0]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ LL_DELETE(head,&els[1]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting d\n");
+ LL_DELETE(head,&els[3]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test29.ans b/tests/test29.ans
new file mode 100644
index 000000000..6467b600a
--- /dev/null
+++ b/tests/test29.ans
@@ -0,0 +1,103 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
+deleting head ADRIAN
+head->prev: XAVIER
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
diff --git a/tests/test29.c b/tests/test29.c
new file mode 100644
index 000000000..e579a0e23
--- /dev/null
+++ b/tests/test29.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+static int namecmp(void *_a, void *_b)
+{
+ el *a = (el*)_a;
+ el *b = (el*)_b;
+ return strcmp(a->bname,b->bname);
+}
+
+int main()
+{
+ el *name, *tmp;
+ el *head = NULL;
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ DL_APPEND(head, name);
+ }
+ DL_SORT(head, namecmp);
+ DL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ /* now delete the list head */
+ printf("deleting head %shead->prev: %s", head->bname, head->prev->bname);
+ DL_DELETE(head,head);
+ DL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test3.ans b/tests/test3.ans
new file mode 100644
index 000000000..56a52a3f0
--- /dev/null
+++ b/tests/test3.ans
@@ -0,0 +1,5 @@
+user 1, cookie 1
+user 3, cookie 9
+user 5, cookie 25
+user 7, cookie 49
+user 9, cookie 81
diff --git a/tests/test3.c b/tests/test3.c
new file mode 100644
index 000000000..630b8b3d3
--- /dev/null
+++ b/tests/test3.c
@@ -0,0 +1,43 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ /* delete each even ID */
+ for(i=0; i<10; i+=2) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ HASH_DEL(users,tmp);
+ free(tmp);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+
+ /* show the hash */
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ return 0;
+}
diff --git a/tests/test30.ans b/tests/test30.ans
new file mode 100644
index 000000000..2b72e985a
--- /dev/null
+++ b/tests/test30.ans
@@ -0,0 +1,51 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
diff --git a/tests/test30.c b/tests/test30.c
new file mode 100644
index 000000000..58bfb7356
--- /dev/null
+++ b/tests/test30.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+static int namecmp(void *_a, void *_b)
+{
+ el *a = (el*)_a;
+ el *b = (el*)_b;
+ return strcmp(a->bname,b->bname);
+}
+
+int main()
+{
+ el *name, *tmp;
+ el *head = NULL;
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ CDL_PREPEND(head, name);
+ }
+ CDL_SORT(head, namecmp);
+ CDL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test31.ans b/tests/test31.ans
new file mode 100644
index 000000000..2b72e985a
--- /dev/null
+++ b/tests/test31.ans
@@ -0,0 +1,51 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
diff --git a/tests/test31.c b/tests/test31.c
new file mode 100644
index 000000000..58bfb7356
--- /dev/null
+++ b/tests/test31.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+static int namecmp(void *_a, void *_b)
+{
+ el *a = (el*)_a;
+ el *b = (el*)_b;
+ return strcmp(a->bname,b->bname);
+}
+
+int main()
+{
+ el *name, *tmp;
+ el *head = NULL;
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ CDL_PREPEND(head, name);
+ }
+ CDL_SORT(head, namecmp);
+ CDL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test32.ans b/tests/test32.ans
new file mode 100644
index 000000000..2a4d7f716
--- /dev/null
+++ b/tests/test32.ans
@@ -0,0 +1,51 @@
+ARNOLDO
+COLTON
+WES
+WARNER
+TONEY
+NEVILLE
+CHONG
+KENTON
+DARIO
+DANNIE
+ODIS
+TRINIDAD
+DONN
+FRITZ
+NORMAND
+NIGEL
+CORNELL
+LINCOLN
+RAYMUNDO
+WINFRED
+JARVIS
+GUS
+ISAIAH
+XAVIER
+CARY
+ROLANDO
+LAURENCE
+CARROLL
+IRVING
+LOWELL
+DAMON
+OMAR
+REX
+ORLANDO
+DOUG
+WILLARD
+CLIFTON
+NELSON
+CODY
+ADRIAN
+HARVEY
+RON
+LESTER
+SHANE
+WARREN
+FREDERICK
+GERALD
+DOUGLAS
+WALTER
+WILLIAM
+JOHN
diff --git a/tests/test32.c b/tests/test32.c
new file mode 100644
index 000000000..f861b2ad4
--- /dev/null
+++ b/tests/test32.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ el *name, *tmp;
+ el *head = NULL;
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ DL_PREPEND(head, name);
+ }
+ /* DL_SORT(head, namecmp); */
+ DL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test33.ans b/tests/test33.ans
new file mode 100644
index 000000000..2b72e985a
--- /dev/null
+++ b/tests/test33.ans
@@ -0,0 +1,51 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
diff --git a/tests/test33.c b/tests/test33.c
new file mode 100644
index 000000000..54cc31128
--- /dev/null
+++ b/tests/test33.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+static int namecmp(void *_a, void *_b)
+{
+ el *a = (el*)_a;
+ el *b = (el*)_b;
+ return strcmp(a->bname,b->bname);
+}
+
+int main()
+{
+ el *name, *tmp;
+ el *head = NULL;
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ LL_PREPEND(head, name);
+ }
+ LL_SORT(head, namecmp);
+ LL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test34.ans b/tests/test34.ans
new file mode 100644
index 000000000..2a4d7f716
--- /dev/null
+++ b/tests/test34.ans
@@ -0,0 +1,51 @@
+ARNOLDO
+COLTON
+WES
+WARNER
+TONEY
+NEVILLE
+CHONG
+KENTON
+DARIO
+DANNIE
+ODIS
+TRINIDAD
+DONN
+FRITZ
+NORMAND
+NIGEL
+CORNELL
+LINCOLN
+RAYMUNDO
+WINFRED
+JARVIS
+GUS
+ISAIAH
+XAVIER
+CARY
+ROLANDO
+LAURENCE
+CARROLL
+IRVING
+LOWELL
+DAMON
+OMAR
+REX
+ORLANDO
+DOUG
+WILLARD
+CLIFTON
+NELSON
+CODY
+ADRIAN
+HARVEY
+RON
+LESTER
+SHANE
+WARREN
+FREDERICK
+GERALD
+DOUGLAS
+WALTER
+WILLIAM
+JOHN
diff --git a/tests/test34.c b/tests/test34.c
new file mode 100644
index 000000000..59377a024
--- /dev/null
+++ b/tests/test34.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ el *name, *tmp;
+ el *head = NULL;
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ CDL_PREPEND(head, name);
+ }
+ /* CDL_SORT(head, namecmp); */
+ CDL_FOREACH(head,tmp) {
+ printf("%s", tmp->bname);
+ }
+
+ fclose(file);
+
+ return 0;
+}
diff --git a/tests/test35.ans b/tests/test35.ans
new file mode 100644
index 000000000..b33c8088c
--- /dev/null
+++ b/tests/test35.ans
@@ -0,0 +1,30 @@
+0: aello
+1: bello
+2: cello
+3: dello
+4: eello
+5: fello
+6: gello
+7: hello
+8: iello
+9: jello
+found aello
+right address? yes
+found bello
+right address? yes
+found cello
+right address? yes
+found dello
+right address? yes
+found eello
+right address? yes
+found fello
+right address? yes
+found gello
+right address? yes
+found hello
+right address? yes
+found iello
+right address? yes
+found jello
+right address? yes
diff --git a/tests/test35.c b/tests/test35.c
new file mode 100644
index 000000000..a9c82b9c5
--- /dev/null
+++ b/tests/test35.c
@@ -0,0 +1,37 @@
+#include "uthash.h"
+#include <string.h> /* strcpy */
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct elt {
+ char *s;
+ UT_hash_handle hh;
+} elt;
+
+int main()
+{
+ int i;
+ elt *head = NULL;
+ elt elts[10];
+ char label[6] = "hello";
+ for(i=0; i<10; i++) {
+ elts[i].s = (char*)malloc(6UL);
+ strcpy(elts[i].s, "hello");
+ elts[i].s[0] = 'a' + i;
+ printf("%d: %s\n", i, elts[i].s);
+ HASH_ADD_KEYPTR(hh, head, elts[i].s, 6UL, &elts[i]);
+ }
+
+ /* look up each element and verify the result pointer */
+ for(i=0; i<10; i++) {
+ elt *e;
+ label[0] = 'a' + i;
+ HASH_FIND(hh,head,label,6UL,e);
+ if (e != NULL) {
+ printf( "found %s\n", e->s);
+ printf( "right address? %s\n", (e == &elts[i]) ? "yes" : "no");
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/test36.ans b/tests/test36.ans
new file mode 100644
index 000000000..4ea987369
--- /dev/null
+++ b/tests/test36.ans
@@ -0,0 +1,25 @@
+user 0
+user 1
+user 2
+user 3
+user 4
+user 5
+user 6
+user 7
+user 8
+user 9
+user 0, should_find=1, found=1
+user 1, should_find=0, found=0
+user 2, should_find=1, found=1
+user 3, should_find=0, found=0
+user 4, should_find=1, found=1
+user 5, should_find=0, found=0
+user 6, should_find=1, found=1
+user 7, should_find=0, found=0
+user 8, should_find=1, found=1
+user 9, should_find=0, found=0
+auser 0
+auser 2
+auser 4
+auser 6
+auser 8
diff --git a/tests/test36.c b/tests/test36.c
new file mode 100644
index 000000000..5927d9419
--- /dev/null
+++ b/tests/test36.c
@@ -0,0 +1,60 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct {
+ int id;
+ UT_hash_handle hh;
+ UT_hash_handle ah;
+} example_user_t;
+
+#define EVENS(x) (((x)->id % 2) == 0)
+static int evens(void *userv)
+{
+ example_user_t *user = (example_user_t*)userv;
+ return ((user->id % 2) ? 0 : 1);
+}
+
+static int idcmp(void *_a, void *_b)
+{
+ example_user_t *a = (example_user_t*)_a;
+ example_user_t *b = (example_user_t*)_b;
+ return (a->id - b->id);
+}
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL, *ausers=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ for(user=users; user!=NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d\n", user->id);
+ }
+
+ /* now select some users into ausers */
+ HASH_SELECT(ah,ausers,hh,users,evens);
+ HASH_SRT(ah,ausers,idcmp);
+
+ for(user=users; user!=NULL; user=(example_user_t*)(user->hh.next)) {
+ example_user_t *found = NULL;
+ int should_find = !!evens(user);
+ HASH_FIND(ah, ausers, &user->id, sizeof(user->id), found);
+ printf("user %d, should_find=%d, found=%d\n", user->id, should_find, (int)(!!found));
+ }
+
+ for(user=ausers; user!=NULL; user=(example_user_t*)(user->ah.next)) {
+ printf("auser %d\n", user->id);
+ }
+
+ return 0;
+}
diff --git a/tests/test37.ans b/tests/test37.ans
new file mode 100644
index 000000000..f42a945cb
--- /dev/null
+++ b/tests/test37.ans
@@ -0,0 +1,20 @@
+user 0
+user 1
+user 2
+user 3
+user 4
+user 5
+user 6
+user 7
+user 8
+user 9
+users count: 10
+auser 0
+auser 2
+auser 4
+auser 6
+auser 8
+ausers count: 5
+cleared ausers.
+ausers count: 0
+users count: 10
diff --git a/tests/test37.c b/tests/test37.c
new file mode 100644
index 000000000..ea5209fd8
--- /dev/null
+++ b/tests/test37.c
@@ -0,0 +1,56 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct {
+ int id;
+ UT_hash_handle hh;
+ UT_hash_handle ah;
+} example_user_t;
+
+#define EVENS(x) ((((example_user_t*)(x))->id % 2) == 0)
+
+static int idcmp(void *_a, void *_b)
+{
+ example_user_t *a = (example_user_t*)_a;
+ example_user_t *b = (example_user_t*)_b;
+ return (a->id - b->id);
+}
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL, *ausers=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ for(user=users; user!=NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d\n", user->id);
+ }
+ printf("users count: %u\n", HASH_CNT(hh,users));
+
+ /* now select some users into ausers */
+ HASH_SELECT(ah,ausers,hh,users,EVENS);
+ HASH_SRT(ah,ausers,idcmp);
+
+ for(user=ausers; user!=NULL; user=(example_user_t*)(user->ah.next)) {
+ printf("auser %d\n", user->id);
+ }
+ printf("ausers count: %u\n", HASH_CNT(ah,ausers));
+ HASH_CLEAR(ah,ausers);
+ printf("cleared ausers.\n");
+ printf("ausers count: %u\n", HASH_CNT(ah,ausers));
+ for(user=ausers; user!=NULL; user=(example_user_t*)(user->ah.next)) {
+ printf("auser %d\n", user->id);
+ }
+ printf("users count: %u\n", HASH_CNT(hh,users));
+ return 0;
+}
diff --git a/tests/test38.ans b/tests/test38.ans
new file mode 100644
index 000000000..b20e04bcb
--- /dev/null
+++ b/tests/test38.ans
@@ -0,0 +1 @@
+hash count 10
diff --git a/tests/test38.c b/tests/test38.c
new file mode 100644
index 000000000..dc9dae94e
--- /dev/null
+++ b/tests/test38.c
@@ -0,0 +1,31 @@
+#include "uthash.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+struct test_t {
+ int a;
+ UT_hash_handle hh;
+};
+
+int main()
+{
+ struct test_t *tests=NULL, *test;
+ int a, b;
+ for (b=0; b < 3; b++) {
+ for (a=0; a < 10; a++) {
+ test = NULL;
+ HASH_FIND(hh, tests, &a, sizeof(a), test);
+ if (test == NULL) {
+ test = (struct test_t*)malloc(sizeof(struct test_t));
+ if (test == NULL) {
+ exit(-1);
+ }
+ memset(test, 0, sizeof(struct test_t));
+ test->a = a;
+ HASH_ADD(hh, tests, a, sizeof(a), test);
+ }
+ }
+ }
+ printf("hash count %u\n", HASH_COUNT(tests));
+ return 0;
+}
diff --git a/tests/test39.ans b/tests/test39.ans
new file mode 100644
index 000000000..a7e7e6ceb
--- /dev/null
+++ b/tests/test39.ans
@@ -0,0 +1,7 @@
+adding key eins
+adding key zwei
+adding key drei
+hash count is 3
+looking for key eins... found.
+looking for key zwei... found.
+looking for key drei... found.
diff --git a/tests/test39.c b/tests/test39.c
new file mode 100644
index 000000000..18a0af8b4
--- /dev/null
+++ b/tests/test39.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+typedef struct {
+ const char *name;
+ UT_hash_handle hh;
+} ns_t;
+
+int main()
+{
+ const char *keys[] = {"eins", "zwei", "drei"};
+ unsigned i;
+ ns_t *nsp;
+ ns_t *head = NULL;
+
+ for(i=0; i < (sizeof(keys)/sizeof(keys[0])); i++) {
+ printf("adding key %s\n", keys[i]);
+ nsp = (ns_t*)malloc(sizeof(ns_t));
+ if (nsp == NULL) {
+ exit(-1);
+ }
+ nsp->name = keys[i];
+ HASH_ADD_KEYPTR(hh,head,nsp->name,strlen(nsp->name),nsp);
+ }
+ printf("hash count is %u\n", HASH_COUNT(head));
+
+ for(i=0; i < (sizeof(keys)/sizeof(keys[0])); i++) {
+ printf("looking for key %s... ", keys[i]);
+ HASH_FIND(hh,head,keys[i],strlen(keys[i]),nsp);
+ printf("%s.\n", (nsp!=NULL)?"found":"not found");
+ }
+ return 0;
+}
diff --git a/tests/test4.ans b/tests/test4.ans
new file mode 100644
index 000000000..19b148f51
--- /dev/null
+++ b/tests/test4.ans
@@ -0,0 +1,10 @@
+cookie 0, user 0
+cookie 1, user 1
+cookie 4, user 2
+cookie 9, user 3
+cookie 16, user 4
+cookie 25, user 5
+cookie 36, user 6
+cookie 49, user 7
+cookie 64, user 8
+cookie 81, user 9
diff --git a/tests/test4.c b/tests/test4.c
new file mode 100644
index 000000000..fe9bcb403
--- /dev/null
+++ b/tests/test4.c
@@ -0,0 +1,33 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+ UT_hash_handle alth;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL, *altusers=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ HASH_ADD(alth,altusers,cookie,sizeof(int),user);
+ }
+
+ for(user=altusers; user != NULL; user=(example_user_t*)(user->alth.next)) {
+ printf("cookie %d, user %d\n", user->cookie, user->id);
+ }
+ return 0;
+}
diff --git a/tests/test40.ans b/tests/test40.ans
new file mode 100644
index 000000000..ad69a9470
--- /dev/null
+++ b/tests/test40.ans
@@ -0,0 +1 @@
+betty's id is 2
diff --git a/tests/test40.c b/tests/test40.c
new file mode 100644
index 000000000..af754611c
--- /dev/null
+++ b/tests/test40.c
@@ -0,0 +1,40 @@
+#include <string.h> /* strcpy */
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+#include "uthash.h"
+
+struct my_struct {
+ const char *name; /* key */
+ int id;
+ UT_hash_handle hh; /* makes this structure hashable */
+};
+
+
+int main()
+{
+ const char **n, *names[] = { "joe", "bob", "betty", NULL };
+ struct my_struct *s, *tmp, *users = NULL;
+ int i=0;
+
+ for (n = names; *n != NULL; n++) {
+ s = (struct my_struct*)malloc(sizeof(struct my_struct));
+ if (s == NULL) {
+ exit(-1);
+ }
+ s->name = *n;
+ s->id = i++;
+ HASH_ADD_KEYPTR( hh, users, s->name, strlen(s->name), s );
+ }
+
+ HASH_FIND_STR( users, "betty", s);
+ if (s != NULL) {
+ printf("betty's id is %d\n", s->id);
+ }
+
+ /* free the hash table contents */
+ HASH_ITER(hh, users, s, tmp) {
+ HASH_DEL(users, s);
+ free(s);
+ }
+ return 0;
+}
diff --git a/tests/test41.ans b/tests/test41.ans
new file mode 100644
index 000000000..f19e34b76
--- /dev/null
+++ b/tests/test41.ans
@@ -0,0 +1,6 @@
+CDL macros
+c b a deleting c deleting b deleting a
+DL macros
+a b c deleting a deleting b deleting c
+LL macros
+a b c deleting a deleting b deleting c
diff --git a/tests/test41.c b/tests/test41.c
new file mode 100644
index 000000000..88a1a96eb
--- /dev/null
+++ b/tests/test41.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el *head = NULL;
+ el els[10], *e, *tmp, *tmp2;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL macros\n");
+ CDL_PREPEND(head,&els[0]);
+ CDL_PREPEND(head,&els[1]);
+ CDL_PREPEND(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+
+ /* point head to head->next */
+ CDL_FOREACH_SAFE(head,e,tmp,tmp2) {
+ printf("deleting %c ", e->id);
+ CDL_DELETE(head,e);
+ }
+ printf("\n");
+ if (head != NULL) {
+ printf("non-null head\n");
+ }
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_APPEND(head,&els[0]);
+ DL_APPEND(head,&els[1]);
+ DL_APPEND(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ DL_FOREACH_SAFE(head,e,tmp) {
+ printf("deleting %c ", e->id);
+ DL_DELETE(head,e);
+ }
+ printf("\n");
+ if (head != NULL) {
+ printf("non-null head\n");
+ }
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_APPEND(head,&els[0]);
+ LL_APPEND(head,&els[1]);
+ LL_APPEND(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ LL_FOREACH_SAFE(head,e,tmp) {
+ printf("deleting %c ", e->id);
+ LL_DELETE(head,e);
+ }
+ printf("\n");
+ if (head != NULL) {
+ printf("non-null head\n");
+ }
+
+ return 0;
+}
diff --git a/tests/test42.ans b/tests/test42.ans
new file mode 100644
index 000000000..581660e3f
--- /dev/null
+++ b/tests/test42.ans
@@ -0,0 +1,14 @@
+LL macros
+a b c
+search scalar found b
+search found a
+
+DL macros
+a b c
+search scalar found b
+search found a
+
+CDL macros
+c b a
+search scalar found b
+search found a
diff --git a/tests/test42.c b/tests/test42.c
new file mode 100644
index 000000000..4da8f03dd
--- /dev/null
+++ b/tests/test42.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+static int eltcmp(el *a, el *b)
+{
+ return a->id - b->id;
+}
+
+int main()
+{
+ int i;
+ el *head = NULL;
+ el els[10], *e, *tmp, *tmp2;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_APPEND(head,&els[0]);
+ LL_APPEND(head,&els[1]);
+ LL_APPEND(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_SEARCH_SCALAR(head, e, id, 'b');
+ if (e != NULL) {
+ printf("search scalar found b\n");
+ }
+ LL_SEARCH(head, e, &els[0], eltcmp);
+ if (e != NULL) {
+ printf("search found %c\n",e->id);
+ }
+ LL_FOREACH_SAFE(head,e,tmp) {
+ LL_DELETE(head,e);
+ }
+
+ printf("\n");
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_APPEND(head,&els[0]);
+ DL_APPEND(head,&els[1]);
+ DL_APPEND(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_SEARCH_SCALAR(head, e, id, 'b');
+ if (e != NULL) {
+ printf("search scalar found b\n");
+ }
+ DL_SEARCH(head, e, &els[0], eltcmp);
+ if (e != NULL) {
+ printf("search found %c\n",e->id);
+ }
+ DL_FOREACH_SAFE(head,e,tmp) {
+ DL_DELETE(head,e);
+ }
+ printf("\n");
+
+ /* test CDL macros */
+ printf("CDL macros\n");
+ CDL_PREPEND(head,&els[0]);
+ CDL_PREPEND(head,&els[1]);
+ CDL_PREPEND(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_SEARCH_SCALAR(head, e, id, 'b');
+ if (e != NULL) {
+ printf("search scalar found b\n");
+ }
+ CDL_SEARCH(head, e, &els[0], eltcmp);
+ if (e != NULL) {
+ printf("search found %c\n",e->id);
+ }
+ CDL_FOREACH_SAFE(head,e,tmp,tmp2) {
+ CDL_DELETE(head,e);
+ }
+
+ return 0;
+}
diff --git a/tests/test43.ans b/tests/test43.ans
new file mode 100644
index 000000000..d09e9bd4e
--- /dev/null
+++ b/tests/test43.ans
@@ -0,0 +1,88 @@
+length is 0
+push
+length is 1
+back is 1 2
+pop
+length is 0
+push
+push
+length is 2
+1 2
+3 4
+erase [0]
+length is 1
+3 4
+push
+3 4
+1 2
+clear
+length is 0
+extend
+length is 1
+ip points to [0] ? yes
+push
+0 0
+1 2
+erase [1]
+length is 1
+0 0
+push
+0 0
+3 4
+back is 3 4
+copy
+cpy length is 2
+cpy 0 0
+cpy 3 4
+insert cpy[0]
+cpy length is 3
+cpy 5 6
+cpy 0 0
+cpy 3 4
+erase cpy [0] [1]
+cpy length is 1
+cpy 3 4
+inserta at cpy[1]
+cpy length is 3
+cpy 3 4
+cpy 0 0
+cpy 3 4
+free cpy
+length is 2
+resize to 30
+length is 30
+0 0
+3 4
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+0 0
+resize to 1
+length is 1
+resize to 0
+length is 0
+free
diff --git a/tests/test43.c b/tests/test43.c
new file mode 100644
index 000000000..216f529aa
--- /dev/null
+++ b/tests/test43.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include "utarray.h"
+
+typedef struct {
+ int a;
+ int b;
+} intpair_t;
+
+int main()
+{
+ UT_array *pairs, *pairs_cpy;
+ intpair_t it, *ip;
+ UT_icd pairicd = { sizeof(intpair_t),NULL,NULL,NULL};
+ size_t zero=0;
+ utarray_new(pairs, &pairicd);
+ printf("length is %u\n", utarray_len(pairs));
+ it.a = 1;
+ it.b=2;
+ utarray_push_back(pairs, &it);
+ printf("push\n");
+ printf("length is %u\n", utarray_len(pairs));
+ ip = (intpair_t*)utarray_back(pairs);
+ printf("back is %d %d\n", ip->a, ip->b);
+ utarray_pop_back(pairs);
+ printf("pop\n");
+ printf("length is %u\n", utarray_len(pairs));
+ it.a = 1;
+ it.b=2;
+ utarray_push_back(pairs, &it);
+ printf("push\n");
+ it.a = 3;
+ it.b=4;
+ utarray_push_back(pairs, &it);
+ printf("push\n");
+ printf("length is %u\n", utarray_len(pairs));
+ ip=NULL;
+ while( (ip=(intpair_t*)utarray_next(pairs,ip)) != NULL ) {
+ printf("%d %d\n", ip->a, ip->b);
+ }
+ utarray_erase(pairs,0,1);
+ printf("erase [0]\n");
+ printf("length is %u\n", utarray_len(pairs));
+ while( (ip=(intpair_t*)utarray_next(pairs,ip)) != NULL ) {
+ printf("%d %d\n", ip->a, ip->b);
+ }
+ it.a = 1;
+ it.b=2;
+ utarray_push_back(pairs, &it);
+ printf("push\n");
+ while( (ip=(intpair_t*)utarray_next(pairs,ip)) != NULL ) {
+ printf("%d %d\n", ip->a, ip->b);
+ }
+ utarray_clear(pairs);
+ printf("clear\n");
+ printf("length is %u\n", utarray_len(pairs));
+ utarray_extend_back(pairs);
+ printf("extend\n");
+ ip = (intpair_t*)utarray_back(pairs);
+ printf("length is %u\n", utarray_len(pairs));
+ printf("ip points to [0] ? %s\n", (ip==(intpair_t*)utarray_front(pairs)) ? "yes" : "no");
+ it.a = 1;
+ it.b=2;
+ utarray_push_back(pairs, &it);
+ printf("push\n");
+ ip=NULL;
+ while( (ip=(intpair_t*)utarray_next(pairs,ip)) != NULL ) {
+ printf("%d %d\n", ip->a, ip->b);
+ }
+ utarray_erase(pairs,1,1);
+ printf("erase [1]\n");
+ printf("length is %u\n", utarray_len(pairs));
+ while( (ip=(intpair_t*)utarray_next(pairs,ip)) != NULL ) {
+ printf("%d %d\n", ip->a, ip->b);
+ }
+ it.a = 3;
+ it.b=4;
+ utarray_push_back(pairs, &it);
+ printf("push\n");
+ for(ip=(intpair_t*)utarray_front(pairs); ip!=NULL; ip=(intpair_t*)utarray_next(pairs,ip)) {
+ printf("%d %d\n", ip->a,ip->b);
+ }
+ ip = (intpair_t*)utarray_back(pairs);
+ printf("back is %d %d\n", ip->a, ip->b);
+ utarray_new(pairs_cpy, &pairicd);
+ utarray_concat(pairs_cpy, pairs);
+ printf("copy\n");
+ printf("cpy length is %u\n", utarray_len(pairs_cpy));
+ ip=NULL;
+ while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip)) != NULL ) {
+ printf("cpy %d %d\n", ip->a, ip->b);
+ }
+ it.a=5;
+ it.b=6;
+ utarray_insert(pairs_cpy, &it, 0);
+ printf("insert cpy[0]\n");
+ printf("cpy length is %u\n", utarray_len(pairs_cpy));
+ while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip)) != NULL ) {
+ printf("cpy %d %d\n", ip->a, ip->b);
+ }
+ utarray_erase(pairs_cpy,0,2);
+ printf("erase cpy [0] [1]\n");
+ printf("cpy length is %u\n", utarray_len(pairs_cpy));
+ while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip)) != NULL ) {
+ printf("cpy %d %d\n", ip->a, ip->b);
+ }
+ utarray_inserta(pairs_cpy, pairs, 1);
+ printf("inserta at cpy[1]\n");
+ printf("cpy length is %u\n", utarray_len(pairs_cpy));
+ while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip)) != NULL ) {
+ printf("cpy %d %d\n", ip->a, ip->b);
+ }
+ utarray_free(pairs_cpy);
+ printf("free cpy\n");
+ printf("length is %u\n", utarray_len(pairs));
+ utarray_resize(pairs, 30);
+ printf("resize to 30\n");
+ printf("length is %u\n", utarray_len(pairs));
+ while( (ip=(intpair_t*)utarray_next(pairs,ip)) != NULL ) {
+ printf("%d %d\n", ip->a, ip->b);
+ }
+ utarray_resize(pairs, 1);
+ printf("resize to 1\n");
+ printf("length is %u\n", utarray_len(pairs));
+ utarray_resize(pairs, zero);
+ printf("resize to 0\n");
+ printf("length is %u\n", utarray_len(pairs));
+ utarray_free(pairs);
+ printf("free\n");
+ return 0;
+}
diff --git a/tests/test44.ans b/tests/test44.ans
new file mode 100644
index 000000000..f771df887
--- /dev/null
+++ b/tests/test44.ans
@@ -0,0 +1,9 @@
+0 1 2 3 4 5 6 7 8 9
+9 8 7 6 5 4 3 2 1 0
+9 8 7 3 2 1 0
+9 3 2 1 0
+3 2 1 0
+3 2 1
+3 2 1 0 0
+3 2 1
+
diff --git a/tests/test44.c b/tests/test44.c
new file mode 100644
index 000000000..042fc4c50
--- /dev/null
+++ b/tests/test44.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include "utarray.h"
+
+static int reverse(const void *a, const void *b)
+{
+ int _a = *(const int*)a;
+ int _b = *(const int*)b;
+ return _b - _a;
+}
+
+int main()
+{
+ UT_array *a;
+ int i, *p;
+ utarray_new(a, &ut_int_icd);
+ for(i=0; i<10; i++) {
+ utarray_push_back(a,&i);
+ }
+ for(p=(int*)utarray_front(a); p!=NULL; p=(int*)utarray_next(a,p)) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ utarray_sort(a,reverse);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_erase(a,3,3);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_erase(a,1,2);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_erase(a,0,1);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_erase(a,3,1);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_resize(a,5);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_resize(a,3);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_erase(a,0,3);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ utarray_free(a);
+ return 0;
+}
+
diff --git a/tests/test45.ans b/tests/test45.ans
new file mode 100644
index 000000000..7e744bc38
--- /dev/null
+++ b/tests/test45.ans
@@ -0,0 +1,3 @@
+1 2 3 4 5 6 7 8
+1 2 3 100 4 5 6 7 8
+1 2 3 100 4 5 6 7 8 1000
diff --git a/tests/test45.c b/tests/test45.c
new file mode 100644
index 000000000..b0633f2b4
--- /dev/null
+++ b/tests/test45.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *a;
+ int i, *p=NULL;
+ utarray_new(a, &ut_int_icd);
+ for(i=0; i<10; i++) {
+ utarray_push_back(a,&i);
+ }
+ utarray_pop_back(a);
+ utarray_erase(a,0,1);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ i = 100;
+ utarray_insert(a,&i,3);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ utarray_extend_back(a);
+ p = (int*)utarray_back(a);
+ *p = 1000;
+ p = NULL;
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ utarray_clear(a);
+ utarray_free(a);
+ return 0;
+}
+
diff --git a/tests/test46.ans b/tests/test46.ans
new file mode 100644
index 000000000..be625f352
--- /dev/null
+++ b/tests/test46.ans
@@ -0,0 +1,11 @@
+hello world
+begin hello world
+alt begin hello world oth
+hello world oth
+hello world
+hello world begin hello world
+begin hello world
+sorting strs2
+begin hello world
+reverse sorting strs2
+world hello begin
diff --git a/tests/test46.c b/tests/test46.c
new file mode 100644
index 000000000..b3ac63762
--- /dev/null
+++ b/tests/test46.c
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include "utarray.h"
+
+static int strsort(const void *_a, const void *_b)
+{
+ const char *a = *(const char* const *)_a;
+ const char *b = *(const char* const *)_b;
+ return strcmp(a,b);
+}
+
+static int revsort(const void *_a, const void *_b)
+{
+ const char *a = *(const char* const *)_a;
+ const char *b = *(const char* const *)_b;
+ return strcmp(b,a);
+}
+
+int main()
+{
+ UT_array *strs,*strs2;
+ char *s, **p=NULL;
+ utarray_new(strs, &ut_str_icd);
+ s=(char*)"hello";
+ utarray_push_back(strs, &s);
+ s=(char*)"world";
+ utarray_push_back(strs, &s);
+ while ( (p=(char**)utarray_next(strs,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ s=(char*)"begin";
+ utarray_insert(strs,&s,0);
+ while ( (p=(char**)utarray_next(strs,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ utarray_new(strs2, &ut_str_icd);
+ s=(char*)"alt";
+ utarray_push_back(strs2, &s);
+ s=(char*)"oth";
+ utarray_push_back(strs2, &s);
+ utarray_inserta(strs2, strs, 1);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ utarray_erase(strs2,0,2);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ utarray_pop_back(strs2);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ utarray_concat(strs2, strs);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ utarray_clear(strs2);
+ utarray_concat(strs2, strs);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ printf("sorting strs2\n");
+ utarray_sort(strs2,strsort);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ printf("reverse sorting strs2\n");
+ utarray_sort(strs2,revsort);
+ while ( (p=(char**)utarray_next(strs2,p)) != NULL ) {
+ printf("%s ",*p);
+ }
+ printf("\n");
+ utarray_clear(strs2);
+ utarray_free(strs2);
+ utarray_free(strs);
+ return 0;
+}
diff --git a/tests/test47.ans b/tests/test47.ans
new file mode 100644
index 000000000..546e06442
--- /dev/null
+++ b/tests/test47.ans
@@ -0,0 +1,8 @@
+hello world
+hello world text
+ second
+hello world text second
+cleared, length t now: 0
+length s now: 23
+one 1 two 2 three (3)
+length t now: 21
diff --git a/tests/test47.c b/tests/test47.c
new file mode 100644
index 000000000..620c46e9f
--- /dev/null
+++ b/tests/test47.c
@@ -0,0 +1,29 @@
+#include <stdio.h> /* printf */
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s,*t;
+ char a[] = " text";
+ utstring_new(s);
+ utstring_new(t);
+
+ utstring_printf(s,"hello %s", "world");
+ printf("%s\n", utstring_body(s));
+ utstring_bincpy(s,a,sizeof(a)-1);
+ printf("%s\n", utstring_body(s));
+ utstring_printf(t," second");
+ printf("%s\n", utstring_body(t));
+ utstring_concat(s,t);
+ printf("%s\n", utstring_body(s));
+ utstring_clear(t);
+ printf("cleared, length t now: %u\n", (unsigned)utstring_len(t));
+ printf("length s now: %u\n", (unsigned)utstring_len(s));
+ utstring_printf(t,"one %d two %u three %s", 1, 2, "(3)");
+ printf("%s\n", utstring_body(t));
+ printf("length t now: %u\n", (unsigned)utstring_len(t));
+
+ utstring_free(t);
+ utstring_free(s);
+ return 0;
+}
diff --git a/tests/test48.ans b/tests/test48.ans
new file mode 100644
index 000000000..8b1acc12b
--- /dev/null
+++ b/tests/test48.ans
@@ -0,0 +1,10 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
diff --git a/tests/test48.c b/tests/test48.c
new file mode 100644
index 000000000..9e634c83e
--- /dev/null
+++ b/tests/test48.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *nums;
+ int i, *p;
+
+ utarray_new(nums,&ut_int_icd);
+ for(i=0; i < 10; i++) {
+ utarray_push_back(nums,&i);
+ }
+
+ for(p=(int*)utarray_front(nums);
+ p!=NULL;
+ p=(int*)utarray_next(nums,p)) {
+ printf("%d\n",*p);
+ }
+
+ utarray_free(nums);
+
+ return 0;
+}
diff --git a/tests/test49.ans b/tests/test49.ans
new file mode 100644
index 000000000..94954abda
--- /dev/null
+++ b/tests/test49.ans
@@ -0,0 +1,2 @@
+hello
+world
diff --git a/tests/test49.c b/tests/test49.c
new file mode 100644
index 000000000..972f01376
--- /dev/null
+++ b/tests/test49.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *strs;
+ const char *s, **p;
+
+ utarray_new(strs,&ut_str_icd);
+
+ s = "hello";
+ utarray_push_back(strs, &s);
+ s = "world";
+ utarray_push_back(strs, &s);
+ p = NULL;
+ while ( (p=(const char**)utarray_next(strs,p)) != NULL ) {
+ printf("%s\n",*p);
+ }
+
+ utarray_free(strs);
+
+ return 0;
+}
diff --git a/tests/test5.ans b/tests/test5.ans
new file mode 100644
index 000000000..2ece88fe5
--- /dev/null
+++ b/tests/test5.ans
@@ -0,0 +1,5 @@
+cookie 0 found, user id 0
+cookie 4 found, user id 2
+cookie 16 found, user id 4
+cookie 36 found, user id 6
+cookie 64 found, user id 8
diff --git a/tests/test5.c b/tests/test5.c
new file mode 100644
index 000000000..95acdc5ec
--- /dev/null
+++ b/tests/test5.c
@@ -0,0 +1,40 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+ UT_hash_handle alth;
+} example_user_t;
+
+int main()
+{
+ int i,j;
+ example_user_t *user, *tmp, *users=NULL, *altusers=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ HASH_ADD(alth,altusers,cookie,sizeof(int),user);
+ }
+
+ /* find cookie corresponding to each even ID */
+ for(i=0; i<10; i+=2) {
+ j=i*i;
+ HASH_FIND(alth,altusers,&j,sizeof(int),tmp);
+ if (tmp != NULL) {
+ printf("cookie %d found, user id %d\n", tmp->cookie, tmp->id);
+ } else {
+ printf("cookie %d not found\n", j);
+ }
+ }
+ return 0;
+}
diff --git a/tests/test50.ans b/tests/test50.ans
new file mode 100644
index 000000000..1191247b6
--- /dev/null
+++ b/tests/test50.ans
@@ -0,0 +1,2 @@
+1
+2
diff --git a/tests/test50.c b/tests/test50.c
new file mode 100644
index 000000000..82e6be59d
--- /dev/null
+++ b/tests/test50.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *nums;
+ long l, *p;
+ UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };
+ utarray_new(nums, &long_icd);
+
+ l=1;
+ utarray_push_back(nums, &l);
+ l=2;
+ utarray_push_back(nums, &l);
+
+ p=NULL;
+ while( (p=(long*)utarray_next(nums,p)) != NULL ) {
+ printf("%ld\n", *p);
+ }
+
+ utarray_free(nums);
+ return 0;
+}
diff --git a/tests/test51.ans b/tests/test51.ans
new file mode 100644
index 000000000..3caf26976
--- /dev/null
+++ b/tests/test51.ans
@@ -0,0 +1,2 @@
+1 2
+10 20
diff --git a/tests/test51.c b/tests/test51.c
new file mode 100644
index 000000000..92e7889fd
--- /dev/null
+++ b/tests/test51.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include "utarray.h"
+
+typedef struct {
+ int a;
+ int b;
+} intpair_t;
+
+int main()
+{
+
+ UT_array *pairs;
+ intpair_t ip, *p;
+ UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};
+ utarray_new(pairs,&intpair_icd);
+
+ ip.a=1;
+ ip.b=2;
+ utarray_push_back(pairs, &ip);
+ ip.a=10;
+ ip.b=20;
+ utarray_push_back(pairs, &ip);
+
+ for(p=(intpair_t*)utarray_front(pairs);
+ p!=NULL;
+ p=(intpair_t*)utarray_next(pairs,p)) {
+ printf("%d %d\n", p->a, p->b);
+ }
+
+ utarray_free(pairs);
+ return 0;
+}
diff --git a/tests/test52.ans b/tests/test52.ans
new file mode 100644
index 000000000..bd3740718
--- /dev/null
+++ b/tests/test52.ans
@@ -0,0 +1,2 @@
+1 hello
+2 world
diff --git a/tests/test52.c b/tests/test52.c
new file mode 100644
index 000000000..3de12e40a
--- /dev/null
+++ b/tests/test52.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "utarray.h"
+
+typedef struct {
+ int a;
+ char *s;
+} intchar_t;
+
+static void intchar_copy(void *_dst, const void *_src)
+{
+ intchar_t *dst = (intchar_t*)_dst;
+ const intchar_t *src = (const intchar_t*)_src;
+ dst->a = src->a;
+ dst->s = (src->s != NULL) ? strdup(src->s) : NULL;
+}
+
+static void intchar_dtor(void *_elt)
+{
+ intchar_t *elt = (intchar_t*)_elt;
+ if (elt->s != NULL) {
+ free(elt->s);
+ }
+}
+
+int main()
+{
+ UT_array *intchars;
+ intchar_t ic, *p;
+ UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};
+ utarray_new(intchars, &intchar_icd);
+
+ ic.a=1;
+ ic.s=(char*)"hello";
+ utarray_push_back(intchars, &ic);
+ ic.a=2;
+ ic.s=(char*)"world";
+ utarray_push_back(intchars, &ic);
+
+ p=NULL;
+ while( (p=(intchar_t*)utarray_next(intchars,p)) != NULL ) {
+ printf("%d %s\n", p->a, (p->s != NULL) ? p->s : "null");
+ }
+
+ utarray_free(intchars);
+ return 0;
+}
+
diff --git a/tests/test53.ans b/tests/test53.ans
new file mode 100644
index 000000000..a04238969
--- /dev/null
+++ b/tests/test53.ans
@@ -0,0 +1 @@
+hello world!
diff --git a/tests/test53.c b/tests/test53.c
new file mode 100644
index 000000000..b30ee561a
--- /dev/null
+++ b/tests/test53.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s;
+
+ utstring_new(s);
+ utstring_printf(s, "hello world!" );
+ printf("%s\n", utstring_body(s));
+
+ utstring_free(s);
+ return 0;
+}
diff --git a/tests/test54.ans b/tests/test54.ans
new file mode 100644
index 000000000..df0083e39
--- /dev/null
+++ b/tests/test54.ans
@@ -0,0 +1,2 @@
+length: 21
+hello world hi there
diff --git a/tests/test54.c b/tests/test54.c
new file mode 100644
index 000000000..5faaa9e92
--- /dev/null
+++ b/tests/test54.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s, *t;
+
+ utstring_new(s);
+ utstring_new(t);
+
+ utstring_printf(s, "hello " );
+ utstring_printf(s, "world " );
+
+ utstring_printf(t, "hi " );
+ utstring_printf(t, "there " );
+
+ utstring_concat(s, t);
+ printf("length: %u\n", (unsigned)utstring_len(s));
+ printf("%s\n", utstring_body(s));
+
+ utstring_free(s);
+ utstring_free(t);
+ return 0;
+}
diff --git a/tests/test55.ans b/tests/test55.ans
new file mode 100644
index 000000000..a75e7be25
--- /dev/null
+++ b/tests/test55.ans
@@ -0,0 +1,2 @@
+length is 3
+number 10
diff --git a/tests/test55.c b/tests/test55.c
new file mode 100644
index 000000000..95d8c01b6
--- /dev/null
+++ b/tests/test55.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s;
+ char binary[] = "\xff\xff";
+
+ utstring_new(s);
+ utstring_bincpy(s, binary, sizeof(binary));
+ printf("length is %u\n", (unsigned)utstring_len(s));
+
+ utstring_clear(s);
+ utstring_printf(s,"number %d", 10);
+ printf("%s\n", utstring_body(s));
+
+ utstring_free(s);
+ return 0;
+}
diff --git a/tests/test56.ans b/tests/test56.ans
new file mode 100644
index 000000000..f615d0072
--- /dev/null
+++ b/tests/test56.ans
@@ -0,0 +1,65 @@
+ADRIAN
+ARNOLDO
+CARROLL
+CARY
+CHONG
+CLIFTON
+CODY
+COLTON
+CORNELL
+DAMON
+DANNIE
+DARIO
+DONN
+DOUG
+DOUGLAS
+FREDERICK
+FRITZ
+GERALD
+GUS
+HARVEY
+IRVING
+ISAIAH
+JARVIS
+JOHN
+KENTON
+LAURENCE
+LESTER
+LINCOLN
+LOWELL
+NELSON
+NEVILLE
+NIGEL
+NORMAND
+ODIS
+OMAR
+ORLANDO
+RAYMUNDO
+REX
+ROLANDO
+RON
+SHANE
+TONEY
+TRINIDAD
+WALTER
+WARNER
+WARREN
+WES
+WILLARD
+WILLIAM
+WINFRED
+XAVIER
+found WES
+
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
+length is 3
+number 10
diff --git a/tests/test56.c b/tests/test56.c
new file mode 100644
index 000000000..e4a0dce16
--- /dev/null
+++ b/tests/test56.c
@@ -0,0 +1,98 @@
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+#include <string.h>
+#include "uthash.h"
+#include "utlist.h"
+#include "utstring.h"
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+#define BUFLEN 20
+
+typedef struct el {
+ char bname[BUFLEN];
+ struct el *next, *prev;
+} el;
+
+static int namecmp(void *_a, void *_b)
+{
+ el *a = (el*)_a;
+ el *b = (el*)_b;
+ return strcmp(a->bname,b->bname);
+}
+
+int main()
+{
+ el *name, *elt, *tmp, etmp;
+ int i;
+ example_user_t *user, *users=NULL;
+ el *head = NULL; /* important- initialize to NULL! */
+
+ char linebuf[BUFLEN];
+ FILE *file;
+
+ UT_string *s;
+ char binary[] = "\xff\xff";
+
+ file = fopen( "test11.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,BUFLEN,file) != NULL) {
+ name = (el*)malloc(sizeof(el));
+ if (name == NULL) {
+ exit(-1);
+ }
+ strcpy(name->bname, linebuf);
+ DL_APPEND(head, name);
+ }
+ DL_SORT(head, namecmp);
+ DL_FOREACH(head,elt) {
+ printf("%s", elt->bname);
+ }
+
+ memcpy(etmp.bname, "WES\n", 5UL);
+ DL_SEARCH(head,elt,&etmp,namecmp);
+ if (elt != NULL) {
+ printf("found %s\n", elt->bname);
+ }
+
+ /* now delete each element, use the safe iterator */
+ DL_FOREACH_SAFE(head,elt,tmp) {
+ DL_DELETE(head,elt);
+ }
+
+ fclose(file);
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+
+ utstring_new(s);
+ utstring_bincpy(s, binary, sizeof(binary));
+ printf("length is %u\n", (unsigned)utstring_len(s));
+
+ utstring_clear(s);
+ utstring_printf(s,"number %d", 10);
+ printf("%s\n", utstring_body(s));
+
+ utstring_free(s);
+ return 0;
+}
diff --git a/tests/test57.ans b/tests/test57.ans
new file mode 100644
index 000000000..4d3bb1d0b
--- /dev/null
+++ b/tests/test57.ans
@@ -0,0 +1 @@
+found
diff --git a/tests/test57.c b/tests/test57.c
new file mode 100644
index 000000000..3b89fb52d
--- /dev/null
+++ b/tests/test57.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+typedef struct {
+ void *key;
+ int i;
+ UT_hash_handle hh;
+} el_t;
+
+int main()
+{
+ el_t *d;
+ el_t *hash = NULL;
+ char *someaddr = NULL;
+ el_t *e = (el_t*)malloc(sizeof(el_t));
+ if (!e) {
+ return -1;
+ }
+ e->key = (void*)someaddr;
+ e->i = 1;
+ HASH_ADD_PTR(hash,key,e);
+ HASH_FIND_PTR(hash, &someaddr, d);
+ if (d != NULL) {
+ printf("found\n");
+ }
+
+ /* release memory */
+ HASH_DEL(hash,e);
+ free(e);
+ return 0;
+}
diff --git a/tests/test58.ans b/tests/test58.ans
new file mode 100644
index 000000000..a23ca72bc
--- /dev/null
+++ b/tests/test58.ans
@@ -0,0 +1,18 @@
+user 0, cookie 0
+user 1, cookie 1
+user 2, cookie 4
+user 3, cookie 9
+user 4, cookie 16
+user 5, cookie 25
+user 6, cookie 36
+user 7, cookie 49
+user 8, cookie 64
+user 9, cookie 81
+10 users. Deleting odd id's...
+user 0, cookie 0
+user 2, cookie 4
+user 4, cookie 16
+user 6, cookie 36
+user 8, cookie 64
+5 users. Deleting remaining id's...
+0 users.
diff --git a/tests/test58.c b/tests/test58.c
new file mode 100644
index 000000000..8f5683c71
--- /dev/null
+++ b/tests/test58.c
@@ -0,0 +1,60 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ unsigned c;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+ /* show the hash */
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+
+
+ c = HASH_COUNT(users);
+ printf("%u users. Deleting odd id's...\n", c);
+ /* delete the odd id's */
+ HASH_ITER(hh, users, user, tmp) {
+ if ((user->id & 1) != 0) {
+ HASH_DEL(users,user);
+ }
+ }
+
+ /* show the hash */
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+
+ c = HASH_COUNT(users);
+ printf("%u users. Deleting remaining id's...\n", c);
+ /* delete all that are left */
+ HASH_ITER(hh, users, user, tmp) {
+ HASH_DEL(users,user);
+ }
+
+ c = HASH_COUNT(users);
+ printf("%u users.\n", c);
+ /* show the hash */
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ return 0;
+}
diff --git a/tests/test59.ans b/tests/test59.ans
new file mode 100644
index 000000000..e19f2f6cd
--- /dev/null
+++ b/tests/test59.ans
@@ -0,0 +1 @@
+$items{bob}{age} = 37
diff --git a/tests/test59.c b/tests/test59.c
new file mode 100644
index 000000000..f353ae164
--- /dev/null
+++ b/tests/test59.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+/* hash of hashes */
+typedef struct item {
+ char name[10];
+ struct item *sub;
+ int val;
+ UT_hash_handle hh;
+} item_t;
+
+int main()
+{
+ item_t *item1, *item2, *tmp1, *tmp2;
+ item_t *items=NULL;
+
+ /* make initial element */
+ item_t *i = (item_t *)malloc(sizeof(*i));
+ if (i == NULL) {
+ exit(-1);
+ }
+ strcpy(i->name, "bob");
+ i->sub = NULL;
+ i->val = 0;
+ HASH_ADD_STR(items, name, i);
+
+ /* add a sub hash table off this element */
+ item_t *s = (item_t *)malloc(sizeof(*s));
+ if (s == NULL) {
+ exit(-1);
+ }
+ strcpy(s->name, "age");
+ s->sub = NULL;
+ s->val = 37;
+ HASH_ADD_STR(i->sub, name, s);
+
+ /* iterate over hash elements */
+ HASH_ITER(hh, items, item1, tmp1) {
+ HASH_ITER(hh, item1->sub, item2, tmp2) {
+ printf("$items{%s}{%s} = %d\n", item1->name, item2->name, item2->val);
+ }
+ }
+
+ /* clean up both hash tables */
+ HASH_ITER(hh, items, item1, tmp1) {
+ HASH_ITER(hh, item1->sub, item2, tmp2) {
+ HASH_DEL(item1->sub, item2);
+ free(item2);
+ }
+ HASH_DEL(items, item1);
+ free(item1);
+ }
+
+ return 0;
+}
diff --git a/tests/test6.ans b/tests/test6.ans
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/test6.ans
diff --git a/tests/test6.c b/tests/test6.c
new file mode 100644
index 000000000..55ce36bfb
--- /dev/null
+++ b/tests/test6.c
@@ -0,0 +1,121 @@
+#include "uthash.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Set up macros for alternative malloc/free functions */
+#undef uthash_malloc
+#undef uthash_free
+#undef uthash_memcmp
+#undef uthash_strlen
+#undef uthash_bzero
+#define uthash_malloc(sz) alt_malloc(sz)
+#define uthash_free(ptr,sz) alt_free(ptr,sz)
+#define uthash_memcmp(a,b,n) alt_memcmp(a,b,n)
+#define uthash_strlen(s) ..fail_to_compile..
+#define uthash_bzero(a,n) alt_bzero(a,n)
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+static size_t alt_malloc_sizes[10];
+static int alt_malloc_balance = 0;
+static void *alt_malloc(size_t sz)
+{
+ alt_malloc_sizes[alt_malloc_balance++] = sz;
+ if (alt_malloc_balance == 1) {
+ assert(sz == sizeof(UT_hash_table));
+ }
+ return malloc(sz);
+}
+static void alt_free(void *ptr, size_t sz)
+{
+ size_t expected = alt_malloc_sizes[--alt_malloc_balance];
+ if (sz != expected) {
+ printf("expected free of size %d, got %d\n", (int)expected, (int)sz);
+ }
+ free(ptr);
+}
+
+static int alt_memcmp_count = 0;
+static int alt_memcmp(const void *a, const void *b, size_t n)
+{
+ ++alt_memcmp_count;
+ return memcmp(a,b,n);
+}
+
+static int alt_bzero_count = 0;
+static void alt_bzero(void *a, size_t n)
+{
+ ++alt_bzero_count;
+ memset(a,0,n);
+}
+
+static void *real_malloc(size_t n)
+{
+ return malloc(n);
+}
+
+static void real_free(void *p)
+{
+ free(p);
+}
+
+#undef malloc
+#undef realloc
+#undef free
+#undef memset
+#undef memcmp
+#undef strlen
+#define malloc ..fail_to_compile..
+#define realloc ..fail_to_compile..
+#define free ..fail_to_compile..
+#define memset ..fail_to_compile..
+#define memcmp ..fail_to_compile..
+#define strlen ..fail_to_compile..
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)real_malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ /* delete each ID */
+ for(i=0; i<10; i++) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ HASH_DEL(users,tmp);
+ real_free(tmp);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+
+ /* show the hash */
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+
+#ifdef HASH_BLOOM
+ assert(alt_bzero_count == 3);
+#else
+ assert(alt_bzero_count == 2);
+#endif
+ assert(alt_memcmp_count == 10);
+ assert(alt_malloc_balance == 0);
+ return 0;
+}
diff --git a/tests/test60.ans b/tests/test60.ans
new file mode 100644
index 000000000..e19f2f6cd
--- /dev/null
+++ b/tests/test60.ans
@@ -0,0 +1 @@
+$items{bob}{age} = 37
diff --git a/tests/test60.c b/tests/test60.c
new file mode 100644
index 000000000..138c0da86
--- /dev/null
+++ b/tests/test60.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+/* hash of hashes */
+typedef struct item {
+ char name[10];
+ struct item *sub;
+ int val;
+ UT_hash_handle hh;
+} item_t;
+
+int main()
+{
+ item_t *item1, *item2, *tmp1, *tmp2;
+ item_t *items=NULL;
+
+ /* make initial element */
+ item_t *i = (item_t *)malloc(sizeof(*i));
+ if (i == NULL) {
+ exit(-1);
+ }
+ strcpy(i->name, "bob");
+ i->sub = NULL;
+ i->val = 0;
+ HASH_ADD_STR(items, name, i);
+
+ /* add a sub hash table off this element */
+ item_t *s = (item_t *)malloc(sizeof(*s));
+ if (s == NULL) {
+ exit(-1);
+ }
+ strcpy(s->name, "age");
+ s->sub = NULL;
+ s->val = 37;
+ HASH_ADD_STR(i->sub, name, s);
+
+ /* iterate over hash elements, printing and freeing them */
+ HASH_ITER(hh, items, item1, tmp1) {
+ HASH_ITER(hh, item1->sub, item2, tmp2) {
+ printf("$items{%s}{%s} = %d\n", item1->name, item2->name, item2->val);
+ HASH_DEL(item1->sub, item2);
+ free(item2);
+ }
+ HASH_DEL(items, item1);
+ free(item1);
+ }
+
+ return 0;
+}
diff --git a/tests/test61.ans b/tests/test61.ans
new file mode 100644
index 000000000..39c63b45d
--- /dev/null
+++ b/tests/test61.ans
@@ -0,0 +1,16 @@
+hello
+world
+one
+two
+three
+sorting
+finding hello
+ hello
+finding one
+ one
+finding three
+ three
+finding two
+ two
+finding world
+ world
diff --git a/tests/test61.c b/tests/test61.c
new file mode 100644
index 000000000..bbb3d1079
--- /dev/null
+++ b/tests/test61.c
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include "utarray.h"
+
+static int strsort(const void *_a, const void *_b)
+{
+ const char *a = *(const char* const *)_a;
+ const char *b = *(const char* const *)_b;
+ return strcmp(a,b);
+}
+
+int main()
+{
+ UT_array *strs;
+ const char *s, **p;
+
+ utarray_new(strs,&ut_str_icd);
+
+ s = "hello";
+ utarray_push_back(strs, &s);
+ s = "world";
+ utarray_push_back(strs, &s);
+ s = "one";
+ utarray_push_back(strs, &s);
+ s = "two";
+ utarray_push_back(strs, &s);
+ s = "three";
+ utarray_push_back(strs, &s);
+
+ p = NULL;
+ while ( (p=(const char**)utarray_next(strs,p)) != NULL ) {
+ s = *p;
+ printf("%s\n",s);
+ }
+
+ printf("sorting\n");
+ utarray_sort(strs,strsort);
+
+ p = NULL;
+ while ( (p=(const char**)utarray_next(strs,p)) != NULL ) {
+ s = *p;
+ printf("finding %s\n",s);
+#ifdef __cplusplus
+ p = (const char**)utarray_find(strs,&s,strsort);
+#else
+ p = utarray_find(strs,&s,strsort);
+#endif
+ printf(" %s\n", (p != NULL) ? (*p) : "failed");
+ }
+
+ utarray_free(strs);
+
+ return 0;
+}
diff --git a/tests/test62.ans b/tests/test62.ans
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/test62.ans
diff --git a/tests/test62.c b/tests/test62.c
new file mode 100644
index 000000000..ad8ce45e6
--- /dev/null
+++ b/tests/test62.c
@@ -0,0 +1,80 @@
+
+#include <assert.h>
+#include <stdlib.h>
+
+#define HASH_FUNCTION(s, len, hashv) (hashv) = TrivialHash((const char *)s, len)
+#include "uthash.h"
+
+unsigned int TrivialHash(const char *s, size_t len)
+{
+ unsigned int h = 0;
+ size_t i;
+ for (i=0; i < len; ++i) {
+ h += (unsigned char)s[i];
+ }
+ return h;
+}
+
+struct test_t {
+ int a;
+ int b;
+ UT_hash_handle hh;
+};
+
+struct test_t *make_test(int value)
+{
+ struct test_t *test = (struct test_t *)malloc(sizeof *test);
+ assert(test != NULL);
+ test->a = value;
+ return test;
+}
+
+int main()
+{
+ struct test_t *tests = NULL;
+ struct test_t *test = NULL;
+ int x;
+ unsigned int h;
+
+ x = 0x0042;
+ HASH_VALUE(&x, sizeof x, h);
+ assert(h == 0x42);
+
+ x = 0x4002;
+ HASH_VALUE(&x, sizeof x, h);
+ assert(h == 0x42);
+
+ test = make_test(0x0042);
+ HASH_ADD_INT(tests, a, test);
+ test = make_test(0x4002);
+ HASH_ADD_INT(tests, a, test);
+
+ x = 0x4002;
+ test = NULL;
+ HASH_FIND_BYHASHVALUE(hh, tests, &x, sizeof x, 0x42, test);
+ assert(test != NULL);
+ assert(test->a == 0x4002);
+
+ x = 0x0042;
+ test = NULL;
+ HASH_FIND_BYHASHVALUE(hh, tests, &x, sizeof x, 0x42, test);
+ assert(test != NULL);
+ assert(test->a == 0x0042);
+
+ x = 0x4002;
+ test = NULL;
+ HASH_FIND_BYHASHVALUE(hh, tests, &x, sizeof x, 0x43, test);
+ assert(test == NULL);
+
+ x = 0x0042;
+ test = NULL;
+ HASH_FIND_BYHASHVALUE(hh, tests, &x, sizeof x, 0x43, test);
+ assert(test == NULL);
+
+ x = 0x4003;
+ test = NULL;
+ HASH_FIND_BYHASHVALUE(hh, tests, &x, sizeof x, 0x42, test);
+ assert(test == NULL);
+
+ HASH_CLEAR(hh, tests);
+}
diff --git a/tests/test63.ans b/tests/test63.ans
new file mode 100644
index 000000000..c570c50e2
--- /dev/null
+++ b/tests/test63.ans
@@ -0,0 +1,7 @@
+LL macros
+a b c
+d e f
+a b c d e f
+d e f
+d e f
+a b
diff --git a/tests/test63.c b/tests/test63.c
new file mode 100644
index 000000000..e41f695c7
--- /dev/null
+++ b/tests/test63.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[10], *e;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_APPEND(headA,&els[0]);
+ LL_APPEND(headA,&els[1]);
+ LL_APPEND(headA,&els[2]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ LL_APPEND(headB,&els[3]);
+ LL_APPEND(headB,&els[4]);
+ LL_APPEND(headB,&els[5]);
+ LL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ LL_CONCAT(headA,headB);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* other variations */
+ headA = NULL;
+ LL_CONCAT(headA,headB);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ headB = NULL;
+ LL_CONCAT(headA,headB);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ headA=NULL;
+ headB=NULL;
+ LL_APPEND(headA,&els[0]);
+ LL_APPEND(headB,&els[1]);
+ LL_CONCAT(headA,headB);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test64.ans b/tests/test64.ans
new file mode 100644
index 000000000..1640241aa
--- /dev/null
+++ b/tests/test64.ans
@@ -0,0 +1,7 @@
+DL macros
+a b c
+d e f
+a b c d e f
+d e f
+d e f
+a b
diff --git a/tests/test64.c b/tests/test64.c
new file mode 100644
index 000000000..ec5fba860
--- /dev/null
+++ b/tests/test64.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[10], *e;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_APPEND(headA,&els[0]);
+ DL_APPEND(headA,&els[1]);
+ DL_APPEND(headA,&els[2]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ DL_APPEND(headB,&els[3]);
+ DL_APPEND(headB,&els[4]);
+ DL_APPEND(headB,&els[5]);
+ DL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ DL_CONCAT(headA,headB);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* other variations */
+ headA = NULL;
+ DL_CONCAT(headA,headB);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ headB = NULL;
+ DL_CONCAT(headA,headB);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ headA=NULL;
+ headB=NULL;
+ DL_APPEND(headA,&els[0]);
+ DL_APPEND(headB,&els[1]);
+ DL_CONCAT(headA,headB);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test65.ans b/tests/test65.ans
new file mode 100644
index 000000000..5312b22a2
--- /dev/null
+++ b/tests/test65.ans
@@ -0,0 +1,4 @@
+LRU deleting JOHN
+ 0
+LRU deleting WILLIAM
+ 1
diff --git a/tests/test65.c b/tests/test65.c
new file mode 100644
index 000000000..c863d21db
--- /dev/null
+++ b/tests/test65.c
@@ -0,0 +1,65 @@
+#include <string.h>
+#include <stdio.h>
+#include "uthash.h"
+
+// this is an example of how to do a LRU cache in C using uthash
+// http://troydhanson.github.com/uthash/
+// by Jehiah Czebotar 2011 - jehiah@gmail.com
+// this code is in the public domain http://unlicense.org/
+
+#define MAX_CACHE_SIZE 50U /* a real value would be much larger */
+
+struct CacheEntry {
+ char *key;
+ char *value;
+ UT_hash_handle hh;
+};
+struct CacheEntry *cache = NULL;
+
+static void add_to_cache(const char *key, const char *value)
+{
+ struct CacheEntry *entry, *tmp_entry;
+ entry = (struct CacheEntry *)malloc(sizeof(struct CacheEntry));
+ if (entry == NULL) {
+ exit(-1);
+ }
+ entry->key = strdup(key);
+ entry->value = strdup(value);
+ HASH_ADD_KEYPTR(hh, cache, entry->key, strlen(entry->key), entry);
+
+ // prune the cache to MAX_CACHE_SIZE
+ if (HASH_COUNT(cache) >= MAX_CACHE_SIZE) {
+ HASH_ITER(hh, cache, entry, tmp_entry) {
+ // prune the first entry (loop is based on insertion order so this deletes the oldest item)
+ printf("LRU deleting %s %s\n", entry->key, entry->value);
+ HASH_DELETE(hh, cache, entry);
+ free(entry->key);
+ free(entry->value);
+ free(entry);
+ break;
+ }
+ }
+}
+
+/* main added by Troy D. Hanson */
+int main()
+{
+ char linebuf[100];
+ char nbuf[11];
+ FILE *file;
+ unsigned int i=0;
+
+ file = fopen( "test65.dat", "r" );
+ if (file == NULL) {
+ perror("can't open: ");
+ exit(-1);
+ }
+
+ while (fgets(linebuf,sizeof(linebuf),file) != NULL) {
+ snprintf(nbuf,sizeof(nbuf),"%u",i++);
+ add_to_cache(linebuf, nbuf);
+ }
+
+ fclose(file);
+ return 0;
+}
diff --git a/tests/test65.dat b/tests/test65.dat
new file mode 100644
index 000000000..bb6051b75
--- /dev/null
+++ b/tests/test65.dat
@@ -0,0 +1,51 @@
+JOHN
+WILLIAM
+WALTER
+DOUGLAS
+GERALD
+FREDERICK
+WARREN
+SHANE
+LESTER
+RON
+HARVEY
+ADRIAN
+CODY
+NELSON
+CLIFTON
+WILLARD
+DOUG
+ORLANDO
+REX
+OMAR
+DAMON
+LOWELL
+IRVING
+CARROLL
+LAURENCE
+ROLANDO
+CARY
+XAVIER
+ISAIAH
+GUS
+JARVIS
+WINFRED
+RAYMUNDO
+LINCOLN
+CORNELL
+NIGEL
+NORMAND
+FRITZ
+DONN
+TRINIDAD
+ODIS
+DANNIE
+DARIO
+KENTON
+CHONG
+NEVILLE
+TONEY
+WARNER
+WES
+COLTON
+ARNOLDO
diff --git a/tests/test66.ans b/tests/test66.ans
new file mode 100644
index 000000000..727f397eb
--- /dev/null
+++ b/tests/test66.ans
@@ -0,0 +1,20 @@
+added bob (id 0)
+added jack (id 1)
+added gary (id 2)
+added ty (id 3)
+added bo (id 4)
+added phil (id 5)
+added art (id 6)
+added gil (id 7)
+added buck (id 8)
+added ted (id 9)
+found bob (id 0)
+found jack (id 1)
+found gary (id 2)
+found ty (id 3)
+found bo (id 4)
+found phil (id 5)
+found art (id 6)
+found gil (id 7)
+found buck (id 8)
+found ted (id 9)
diff --git a/tests/test66.c b/tests/test66.c
new file mode 100644
index 000000000..e7ebe43d3
--- /dev/null
+++ b/tests/test66.c
@@ -0,0 +1,43 @@
+#include "uthash.h"
+#include <stdio.h>
+#include <stdlib.h> /* malloc */
+
+typedef struct person_t {
+ char first_name[10];
+ int id;
+ UT_hash_handle hh;
+} person_t;
+
+int main()
+{
+ person_t *people=NULL, *person;
+ const char **name;
+ const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art",
+ "gil", "buck", "ted", NULL
+ };
+ int id=0;
+
+ for(name=names; *name!=NULL; name++) {
+ person = (person_t*)malloc(sizeof(person_t));
+ if (person == NULL) {
+ exit(-1);
+ }
+ strcpy(person->first_name, *name);
+ person->id = id++;
+ HASH_ADD_STR(people,first_name,person);
+ printf("added %s (id %d)\n", person->first_name, person->id);
+ }
+
+ person=NULL;
+ person_t **p=&person;
+
+ for(name=names; *name!=NULL; name++) {
+ HASH_FIND_STR(people,*name,*p);
+ if (person != NULL) {
+ printf("found %s (id %d)\n", person->first_name, person->id);
+ } else {
+ printf("failed to find %s\n", *name);
+ }
+ }
+ return 0;
+}
diff --git a/tests/test67.ans b/tests/test67.ans
new file mode 100644
index 000000000..cd707691d
--- /dev/null
+++ b/tests/test67.ans
@@ -0,0 +1,20 @@
+9
+8
+7
+6
+5
+4
+3
+2
+1
+0
+9
+8
+7
+6
+5
+4
+3
+2
+1
+0
diff --git a/tests/test67.c b/tests/test67.c
new file mode 100644
index 000000000..6602c7b59
--- /dev/null
+++ b/tests/test67.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *nums;
+ int i, *p;
+
+ utarray_new(nums,&ut_int_icd);
+ for(i=0; i < 10; i++) {
+ utarray_push_back(nums,&i);
+ }
+
+ for(p=(int*)utarray_back(nums);
+ p!=NULL;
+ p=(int*)utarray_prev(nums,p)) {
+ printf("%d\n",*p);
+ }
+
+ /* the other form of iteration starting from NULL (back) */
+ p=NULL;
+ while ( (p=(int*)utarray_prev(nums,p)) != NULL ) {
+ printf("%d\n",*p);
+ }
+
+
+ utarray_free(nums);
+
+ return 0;
+}
diff --git a/tests/test68.ans b/tests/test68.ans
new file mode 100644
index 000000000..704cdc534
--- /dev/null
+++ b/tests/test68.ans
@@ -0,0 +1,10 @@
+DL replace elem
+a b c d
+e b c d
+f b c d
+f b c g
+f b c h
+f i j h
+k l m n
+s
+t
diff --git a/tests/test68.c b/tests/test68.c
new file mode 100644
index 000000000..090f7d022
--- /dev/null
+++ b/tests/test68.c
@@ -0,0 +1,87 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[20], *e, *tmp;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<20; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test DL macros */
+ printf("DL replace elem\n");
+ DL_APPEND(headA,&els[0]);
+ DL_APPEND(headA,&els[1]);
+ DL_APPEND(headA,&els[2]);
+ DL_APPEND(headA,&els[3]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace head elem */
+ DL_REPLACE_ELEM(headA, &els[0], &els[4]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_REPLACE_ELEM(headA, &els[4], &els[5]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace last elem */
+ DL_REPLACE_ELEM(headA, &els[3], &els[6]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_REPLACE_ELEM(headA, &els[6], &els[7]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace middle elem */
+ DL_REPLACE_ELEM(headA, &els[1], &els[8]);
+ DL_REPLACE_ELEM(headA, &els[2], &els[9]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace all just to be sure the list is intact... */
+ i = 10;
+ DL_FOREACH_SAFE(headA, e, tmp) {
+ DL_REPLACE_ELEM(headA, e, &els[i]);
+ i++;
+ }
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* single elem */
+ DL_APPEND(headB, &els[18]);
+ DL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_REPLACE_ELEM(headB, &els[18], &els[19]);
+ DL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test69.ans b/tests/test69.ans
new file mode 100644
index 000000000..fba8b64c6
--- /dev/null
+++ b/tests/test69.ans
@@ -0,0 +1,11 @@
+DL prepend elem
+a b c d
+e a b c d
+f e a b c d
+f e a b c g d
+f e a b c h g d
+f e a b i j c h g d
+k f l e m a n b o i p j q c r h s g t d
+u
+v u
+w v u
diff --git a/tests/test69.c b/tests/test69.c
new file mode 100644
index 000000000..bc851a58a
--- /dev/null
+++ b/tests/test69.c
@@ -0,0 +1,92 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[26], *e, *tmp;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<25; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test DL macros */
+ printf("DL prepend elem\n");
+ DL_APPEND(headA,&els[0]);
+ DL_APPEND(headA,&els[1]);
+ DL_APPEND(headA,&els[2]);
+ DL_APPEND(headA,&els[3]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend head elem */
+ DL_PREPEND_ELEM(headA, &els[0], &els[4]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_PREPEND_ELEM(headA, &els[4], &els[5]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend last elem */
+ DL_PREPEND_ELEM(headA, &els[3], &els[6]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_PREPEND_ELEM(headA, &els[6], &els[7]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend middle elem */
+ DL_PREPEND_ELEM(headA, &els[2], &els[8]);
+ DL_PREPEND_ELEM(headA, &els[2], &els[9]);
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend all just to be sure the list is intact... */
+ i = 10;
+ DL_FOREACH_SAFE(headA, e, tmp) {
+ DL_PREPEND_ELEM(headA, e, &els[i]);
+ i++;
+ }
+ DL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* single elem */
+ DL_APPEND(headB, &els[20]);
+ DL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_PREPEND_ELEM(headB, &els[20], &els[21]);
+ DL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_PREPEND_ELEM(headB, &els[21], &els[22]);
+ DL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test7.ans b/tests/test7.ans
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/test7.ans
diff --git a/tests/test7.c b/tests/test7.c
new file mode 100644
index 000000000..01ce54749
--- /dev/null
+++ b/tests/test7.c
@@ -0,0 +1,44 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<1000; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ /* delete each ID */
+ for(i=0; i<1000; i++) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ HASH_DEL(users,tmp);
+ free(tmp);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+
+ /* show the hash */
+ for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %d, cookie %d\n", user->id, user->cookie);
+ }
+ return 0;
+}
diff --git a/tests/test70.ans b/tests/test70.ans
new file mode 100644
index 000000000..00298ec4b
--- /dev/null
+++ b/tests/test70.ans
@@ -0,0 +1,10 @@
+LL replace elem
+a b c d
+e b c d
+f b c d
+f b c g
+f b c h
+f i j h
+k l m n
+s
+t
diff --git a/tests/test70.c b/tests/test70.c
new file mode 100644
index 000000000..abddea1c8
--- /dev/null
+++ b/tests/test70.c
@@ -0,0 +1,87 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[20], *e, *tmp;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<20; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test LL macros */
+ printf("LL replace elem\n");
+ LL_APPEND(headA,&els[0]);
+ LL_APPEND(headA,&els[1]);
+ LL_APPEND(headA,&els[2]);
+ LL_APPEND(headA,&els[3]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace head elem */
+ LL_REPLACE_ELEM(headA, &els[0], &els[4]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_REPLACE_ELEM(headA, &els[4], &els[5]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace last elem */
+ LL_REPLACE_ELEM(headA, &els[3], &els[6]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_REPLACE_ELEM(headA, &els[6], &els[7]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace middle elem */
+ LL_REPLACE_ELEM(headA, &els[1], &els[8]);
+ LL_REPLACE_ELEM(headA, &els[2], &els[9]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace all just to be sure the list is intact... */
+ i = 10;
+ LL_FOREACH_SAFE(headA, e, tmp) {
+ LL_REPLACE_ELEM(headA, e, &els[i]);
+ i++;
+ }
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* single elem */
+ LL_APPEND(headB, &els[18]);
+ LL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_REPLACE_ELEM(headB, &els[18], &els[19]);
+ LL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test71.ans b/tests/test71.ans
new file mode 100644
index 000000000..81d6b5377
--- /dev/null
+++ b/tests/test71.ans
@@ -0,0 +1,11 @@
+LL prepend elem
+a b c d
+e a b c d
+f e a b c d
+f e a b c g d
+f e a b c h g d
+f e a b i j c h g d
+k f l e m a n b o i p j q c r h s g t d
+u
+v u
+w v u
diff --git a/tests/test71.c b/tests/test71.c
new file mode 100644
index 000000000..8cda49935
--- /dev/null
+++ b/tests/test71.c
@@ -0,0 +1,92 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[26], *e, *tmp;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<25; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test LL macros */
+ printf("LL prepend elem\n");
+ LL_APPEND(headA,&els[0]);
+ LL_APPEND(headA,&els[1]);
+ LL_APPEND(headA,&els[2]);
+ LL_APPEND(headA,&els[3]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend head elem */
+ LL_PREPEND_ELEM(headA, &els[0], &els[4]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_PREPEND_ELEM(headA, &els[4], &els[5]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend last elem */
+ LL_PREPEND_ELEM(headA, &els[3], &els[6]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_PREPEND_ELEM(headA, &els[6], &els[7]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend middle elem */
+ LL_PREPEND_ELEM(headA, &els[2], &els[8]);
+ LL_PREPEND_ELEM(headA, &els[2], &els[9]);
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend all just to be sure the list is intact... */
+ i = 10;
+ LL_FOREACH_SAFE(headA, e, tmp) {
+ LL_PREPEND_ELEM(headA, e, &els[i]);
+ i++;
+ }
+ LL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* single elem */
+ LL_APPEND(headB, &els[20]);
+ LL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_PREPEND_ELEM(headB, &els[20], &els[21]);
+ LL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_PREPEND_ELEM(headB, &els[21], &els[22]);
+ LL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test72.ans b/tests/test72.ans
new file mode 100644
index 000000000..b5ccb1493
--- /dev/null
+++ b/tests/test72.ans
@@ -0,0 +1,10 @@
+CDL replace elem
+a b c d
+e b c d
+f b c d
+f b c g
+f b c h
+f i j h
+k l m n
+s
+t
diff --git a/tests/test72.c b/tests/test72.c
new file mode 100644
index 000000000..61ae11d15
--- /dev/null
+++ b/tests/test72.c
@@ -0,0 +1,87 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[20], *e, *tmp, *tmp2;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<20; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL replace elem\n");
+ CDL_PREPEND(headA,&els[3]);
+ CDL_PREPEND(headA,&els[2]);
+ CDL_PREPEND(headA,&els[1]);
+ CDL_PREPEND(headA,&els[0]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace head elem */
+ CDL_REPLACE_ELEM(headA, &els[0], &els[4]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_REPLACE_ELEM(headA, &els[4], &els[5]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace last elem */
+ CDL_REPLACE_ELEM(headA, &els[3], &els[6]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_REPLACE_ELEM(headA, &els[6], &els[7]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace middle elem */
+ CDL_REPLACE_ELEM(headA, &els[1], &els[8]);
+ CDL_REPLACE_ELEM(headA, &els[2], &els[9]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* replace all just to be sure the list is intact... */
+ i = 10;
+ CDL_FOREACH_SAFE(headA, e, tmp, tmp2) {
+ CDL_REPLACE_ELEM(headA, e, &els[i]);
+ i++;
+ }
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* single elem */
+ CDL_PREPEND(headB, &els[18]);
+ CDL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_REPLACE_ELEM(headB, &els[18], &els[19]);
+ CDL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test73.ans b/tests/test73.ans
new file mode 100644
index 000000000..2aa7bdc79
--- /dev/null
+++ b/tests/test73.ans
@@ -0,0 +1,11 @@
+CDL prepend elem
+a b c d
+e a b c d
+f e a b c d
+f e a b c g d
+f e a b c h g d
+f e a b i j c h g d
+k f l e m a n b o i p j q c r h s g t d
+u
+v u
+w v u
diff --git a/tests/test73.c b/tests/test73.c
new file mode 100644
index 000000000..ac7e78568
--- /dev/null
+++ b/tests/test73.c
@@ -0,0 +1,92 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[26], *e, *tmp, *tmp2;
+ el *headA = NULL;
+ el *headB = NULL;
+ for(i=0; i<25; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL prepend elem\n");
+ CDL_PREPEND(headA,&els[3]);
+ CDL_PREPEND(headA,&els[2]);
+ CDL_PREPEND(headA,&els[1]);
+ CDL_PREPEND(headA,&els[0]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend head elem */
+ CDL_PREPEND_ELEM(headA, &els[0], &els[4]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_PREPEND_ELEM(headA, &els[4], &els[5]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend last elem */
+ CDL_PREPEND_ELEM(headA, &els[3], &els[6]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_PREPEND_ELEM(headA, &els[6], &els[7]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend middle elem */
+ CDL_PREPEND_ELEM(headA, &els[2], &els[8]);
+ CDL_PREPEND_ELEM(headA, &els[2], &els[9]);
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* prepend all just to be sure the list is intact... */
+ i = 10;
+ CDL_FOREACH_SAFE(headA, e, tmp, tmp2) {
+ CDL_PREPEND_ELEM(headA, e, &els[i]);
+ i++;
+ }
+ CDL_FOREACH(headA,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* single elem */
+ CDL_PREPEND(headB, &els[20]);
+ CDL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_PREPEND_ELEM(headB, &els[20], &els[21]);
+ CDL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_PREPEND_ELEM(headB, &els[21], &els[22]);
+ CDL_FOREACH(headB,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test74.ans b/tests/test74.ans
new file mode 100644
index 000000000..79a16baa2
--- /dev/null
+++ b/tests/test74.ans
@@ -0,0 +1,6 @@
+"There are two needle" len=55
+"needle" len=8
+utstring_find()=14
+utstring_find()=46
+utstring_find()=-1
+FindCnt=2
diff --git a/tests/test74.c b/tests/test74.c
new file mode 100644
index 000000000..6cb035211
--- /dev/null
+++ b/tests/test74.c
@@ -0,0 +1,40 @@
+#include <stdio.h> /* printf */
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s,*t;
+ char V_TestStr[] = "There are two needle\0s in this \0haystack with needle\0s.";
+ char V_NeedleStr[] = "needle\0s";
+ long V_FindPos;
+ size_t V_FindCnt;
+
+
+ utstring_new(s);
+ utstring_new(t);
+
+ utstring_bincpy(s, V_TestStr, sizeof(V_TestStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(s), (unsigned)utstring_len(s));
+ utstring_bincpy(t, V_NeedleStr, sizeof(V_NeedleStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(t), (unsigned)utstring_len(t));
+
+ V_FindCnt = 0;
+ V_FindPos = 0;
+ do {
+ V_FindPos = utstring_find(s,
+ V_FindPos,
+ utstring_body(t),
+ utstring_len(t));
+ printf("utstring_find()=%ld\n", V_FindPos);
+ if (V_FindPos >= 0) {
+ V_FindPos++;
+ V_FindCnt++;
+ }
+ } while (V_FindPos >= 0);
+ printf("FindCnt=%u\n", (unsigned)V_FindCnt);
+
+ utstring_free(s);
+ utstring_free(t);
+
+ return 0;
+}
diff --git a/tests/test75.ans b/tests/test75.ans
new file mode 100644
index 000000000..8261ba353
--- /dev/null
+++ b/tests/test75.ans
@@ -0,0 +1,6 @@
+"There are two needle" len=55
+"needle" len=8
+utstring_findR()=46
+utstring_findR()=14
+utstring_findR()=-1
+FindCnt=2
diff --git a/tests/test75.c b/tests/test75.c
new file mode 100644
index 000000000..fa3adedb8
--- /dev/null
+++ b/tests/test75.c
@@ -0,0 +1,40 @@
+#include <stdio.h> /* printf */
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s,*t;
+ char V_TestStr[] = "There are two needle\0s in this \0haystack with needle\0s.";
+ char V_NeedleStr[] = "needle\0s";
+ long V_FindPos;
+ size_t V_FindCnt;
+
+
+ utstring_new(s);
+ utstring_new(t);
+
+ utstring_bincpy(s, V_TestStr, sizeof(V_TestStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(s), (unsigned)utstring_len(s));
+ utstring_bincpy(t, V_NeedleStr, sizeof(V_NeedleStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(t), (unsigned)utstring_len(t));
+
+ V_FindCnt = 0;
+ V_FindPos = -1;
+ do {
+ V_FindPos = utstring_findR(s,
+ V_FindPos,
+ utstring_body(t),
+ utstring_len(t));
+ printf("utstring_findR()=%ld\n", V_FindPos);
+ if (V_FindPos >= 0) {
+ V_FindPos--;
+ V_FindCnt++;
+ }
+ } while (V_FindPos >= 0);
+ printf("FindCnt=%u\n", (unsigned)V_FindCnt);
+
+ utstring_free(s);
+ utstring_free(t);
+
+ return 0;
+}
diff --git a/tests/test76.ans b/tests/test76.ans
new file mode 100644
index 000000000..79a16baa2
--- /dev/null
+++ b/tests/test76.ans
@@ -0,0 +1,6 @@
+"There are two needle" len=55
+"needle" len=8
+utstring_find()=14
+utstring_find()=46
+utstring_find()=-1
+FindCnt=2
diff --git a/tests/test76.c b/tests/test76.c
new file mode 100644
index 000000000..a18685cae
--- /dev/null
+++ b/tests/test76.c
@@ -0,0 +1,54 @@
+#include <stdio.h> /* printf */
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s,*t;
+ char V_TestStr[] = "There are two needle\0s in this \0haystack with needle\0s.";
+ char V_NeedleStr[] = "needle\0s";
+ long *V_KMP_Table;
+ long V_FindPos;
+ size_t V_StartPos;
+ size_t V_FindCnt;
+
+
+ utstring_new(s);
+ utstring_new(t);
+
+ utstring_bincpy(s, V_TestStr, sizeof(V_TestStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(s), (unsigned)utstring_len(s));
+ utstring_bincpy(t, V_NeedleStr, sizeof(V_NeedleStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(t), (unsigned)utstring_len(t));
+
+ V_KMP_Table = (long *)malloc(sizeof(long) * (utstring_len(t) + 1));
+ if (V_KMP_Table != NULL) {
+ _utstring_BuildTable(utstring_body(t), utstring_len(t), V_KMP_Table);
+
+ V_FindCnt = 0;
+ V_FindPos = 0;
+ V_StartPos = 0;
+ do {
+ V_FindPos = _utstring_find(utstring_body(s) + V_StartPos,
+ utstring_len(s) - V_StartPos,
+ utstring_body(t),
+ utstring_len(t),
+ V_KMP_Table);
+ if (V_FindPos >= 0) {
+ V_FindPos += V_StartPos;
+ V_FindCnt++;
+ V_StartPos = V_FindPos + 1;
+ }
+ printf("utstring_find()=%ld\n", V_FindPos);
+ } while (V_FindPos >= 0);
+ printf("FindCnt=%u\n", (unsigned)V_FindCnt);
+
+ free(V_KMP_Table);
+ } else {
+ printf("malloc() failed...\n");
+ }
+
+ utstring_free(s);
+ utstring_free(t);
+
+ return 0;
+}
diff --git a/tests/test77.ans b/tests/test77.ans
new file mode 100644
index 000000000..3b2a10971
--- /dev/null
+++ b/tests/test77.ans
@@ -0,0 +1,13 @@
+"There are two needle" len=55
+"needle" len=8
+utstring_find()=46
+utstring_find()=14
+utstring_find()=-1
+FindCnt=2
+expect 15 15
+expect 4 4
+expect -1 -1
+expect 11 11
+expect 4 4
+expect 11 11
+expect 0 0
diff --git a/tests/test77.c b/tests/test77.c
new file mode 100644
index 000000000..7ff97518e
--- /dev/null
+++ b/tests/test77.c
@@ -0,0 +1,74 @@
+#include <stdio.h> /* printf */
+#include "utstring.h"
+
+int main()
+{
+ UT_string *s,*t;
+ char V_TestStr[] = "There are two needle\0s in this \0haystack with needle\0s.";
+ char V_NeedleStr[] = "needle\0s";
+ long *V_KMP_Table;
+ long V_FindPos;
+ size_t V_StartPos;
+ size_t V_FindCnt;
+
+
+ utstring_new(s);
+ utstring_new(t);
+
+ utstring_bincpy(s, V_TestStr, sizeof(V_TestStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(s), (unsigned)utstring_len(s));
+ utstring_bincpy(t, V_NeedleStr, sizeof(V_NeedleStr)-1);
+ printf("\"%s\" len=%u\n", utstring_body(t), (unsigned)utstring_len(t));
+
+ V_KMP_Table = (long *)malloc(sizeof(long) * (utstring_len(t) + 1));
+ if (V_KMP_Table != NULL) {
+ _utstring_BuildTableR(utstring_body(t), utstring_len(t), V_KMP_Table);
+
+ V_FindCnt = 0;
+ V_FindPos = 0;
+ V_StartPos = utstring_len(s) - 1;
+ do {
+ V_FindPos = _utstring_findR(utstring_body(s),
+ V_StartPos + 1,
+ utstring_body(t),
+ utstring_len(t),
+ V_KMP_Table);
+ if (V_FindPos >= 0) {
+ V_FindCnt++;
+ V_StartPos = V_FindPos - 1;
+ }
+ printf("utstring_find()=%ld\n", V_FindPos);
+ } while (V_FindPos >= 0);
+ printf("FindCnt=%u\n", (unsigned)V_FindCnt);
+
+ free(V_KMP_Table);
+ } else {
+ printf("malloc() failed...\n");
+ }
+
+ utstring_free(t);
+ utstring_clear(s);
+ utstring_printf(s,"ABC ABCDAB ABCDABCDABDE");
+ int o;
+
+ o=utstring_find( s, -9, "ABC", 3 ) ;
+ printf("expect 15 %d\n",o);
+ o=utstring_find( s, 3, "ABC", 3 ) ;
+ printf("expect 4 %d\n",o);
+ o=utstring_find( s, 16, "ABC", 3 ) ;
+ printf("expect -1 %d\n",o);
+ o=utstring_findR( s, -9, "ABC", 3 ) ;
+ printf("expect 11 %d\n",o);
+ o=utstring_findR( s, 12, "ABC", 3 ) ;
+ printf("expect 4 %d\n",o);
+ o=utstring_findR( s, 13, "ABC", 3 ) ;
+ printf("expect 11 %d\n",o);
+ o=utstring_findR( s, 2, "ABC", 3 ) ;
+ printf("expect 0 %d\n",o);
+
+
+
+ utstring_free(s);
+
+ return 0;
+}
diff --git a/tests/test78.ans b/tests/test78.ans
new file mode 100644
index 000000000..5c71cdfdc
--- /dev/null
+++ b/tests/test78.ans
@@ -0,0 +1,28 @@
+CDL macros
+c b a
+advancing head pointer
+b a c
+b a c b a c b a c b
+b c a b c a b c a b
+deleting b
+a c
+deleting (a)
+c
+deleting (c)
+
+DL macros
+a b c
+deleting tail c
+a b
+deleting head a
+b
+deleting head b
+
+LL macros
+a b c
+deleting tail c
+a b
+deleting head a
+b
+deleting head b
+
diff --git a/tests/test78.c b/tests/test78.c
new file mode 100644
index 000000000..02424d543
--- /dev/null
+++ b/tests/test78.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *Next, *Prev;
+} el;
+
+int main()
+{
+ int i;
+ el els[10], *e;
+ el *head = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL macros\n");
+ CDL_PREPEND2(head,&els[0],Prev,Next);
+ CDL_PREPEND2(head,&els[1],Prev,Next);
+ CDL_PREPEND2(head,&els[2],Prev,Next);
+ CDL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* point head to head->next */
+ printf("advancing head pointer\n");
+ head = head->Next;
+ CDL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop backwards a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->Prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting b\n");
+ CDL_DELETE2(head,&els[1],Prev,Next);
+ CDL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (a)\n");
+ CDL_DELETE2(head,&els[0],Prev,Next);
+ CDL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (c)\n");
+ CDL_DELETE2(head,&els[2],Prev,Next);
+ CDL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test DL macros */
+ printf("DL macros\n");
+ DL_APPEND2(head,&els[0],Prev,Next);
+ DL_APPEND2(head,&els[1],Prev,Next);
+ DL_APPEND2(head,&els[2],Prev,Next);
+ DL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting tail c\n");
+ DL_DELETE2(head,&els[2],Prev,Next);
+ DL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head a\n");
+ DL_DELETE2(head,&els[0],Prev,Next);
+ DL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head b\n");
+ DL_DELETE2(head,&els[1],Prev,Next);
+ DL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test LL macros */
+ printf("LL macros\n");
+ LL_APPEND2(head,&els[0],Next);
+ LL_APPEND2(head,&els[1],Next);
+ LL_APPEND2(head,&els[2],Next);
+ LL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting tail c\n");
+ LL_DELETE2(head,&els[2],Next);
+ LL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head a\n");
+ LL_DELETE2(head,&els[0],Next);
+ LL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting head b\n");
+ LL_DELETE2(head,&els[1],Next);
+ LL_FOREACH2(head,e,Next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test79.ans b/tests/test79.ans
new file mode 100644
index 000000000..c8737e47c
--- /dev/null
+++ b/tests/test79.ans
@@ -0,0 +1,8 @@
+added 10 100
+id 10, tag 100
+added 11 101
+id 10, tag 100
+id 11, tag 101
+replaced 11 that had tag 101 with tag 102
+id 10, tag 100
+id 11, tag 102
diff --git a/tests/test79.c b/tests/test79.c
new file mode 100644
index 000000000..4abbe94b1
--- /dev/null
+++ b/tests/test79.c
@@ -0,0 +1,71 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "uthash.h"
+
+typedef struct hs_t {
+ int id;
+ int tag;
+ UT_hash_handle hh;
+} hs_t;
+
+
+static void pr(hs_t **hdpp)
+{
+ hs_t *el, *tmp, *hdp = *hdpp;
+ HASH_ITER(hh, hdp, el, tmp) {
+ printf("id %d, tag %d\n",el->id,el->tag);
+ }
+}
+
+int main()
+{
+
+ hs_t *hs_head=NULL, *tmp, *replaced=NULL;
+
+ tmp = (hs_t*)malloc(sizeof(hs_t));
+ if (tmp == NULL) {
+ exit(-1);
+ }
+ tmp->id = 10;
+ tmp->tag = 100;
+ HASH_REPLACE_INT(hs_head,id,tmp,replaced);
+ if(replaced == NULL) {
+ printf("added %d %d\n",tmp->id,tmp->tag);
+ } else {
+ printf("ERROR, ended up replacing a value, replaced: %p\n",(void*)replaced);
+ }
+
+ pr(&hs_head);
+
+ tmp = (hs_t*)malloc(sizeof(hs_t));
+ if (tmp == NULL) {
+ exit(-1);
+ }
+ tmp->id=11;
+ tmp->tag = 101;
+ HASH_REPLACE_INT(hs_head,id,tmp,replaced);
+ if(replaced == NULL) {
+ printf("added %d %d\n",tmp->id,tmp->tag);
+ } else {
+ printf("ERROR, ended up replacing a value, replaced: %p\n",(void*)replaced);
+ }
+
+ pr(&hs_head);
+
+ tmp = (hs_t*)malloc(sizeof(hs_t));
+ if (tmp == NULL) {
+ exit(-1);
+ }
+ tmp->id=11;
+ tmp->tag = 102;
+ HASH_REPLACE_INT(hs_head,id,tmp,replaced);
+ if(replaced == NULL) {
+ printf("ERROR, exected to replace a value with key: %d\n",tmp->id);
+ } else {
+ printf("replaced %d that had tag %d with tag %d\n",tmp->id,replaced->tag,tmp->tag);
+ }
+
+ pr(&hs_head);
+
+ return 0;
+}
diff --git a/tests/test8.ans b/tests/test8.ans
new file mode 100644
index 000000000..9d28857c4
--- /dev/null
+++ b/tests/test8.ans
@@ -0,0 +1,15 @@
+num_items in hash: 1
+num_items in hash: 2
+num_items in hash: 3
+num_items in hash: 4
+num_items in hash: 5
+num_items in hash: 6
+num_items in hash: 7
+num_items in hash: 8
+num_items in hash: 9
+num_items in hash: 10
+deleted; num_items in hash: 9
+deleted; num_items in hash: 8
+deleted; num_items in hash: 7
+deleted; num_items in hash: 6
+deleted; num_items in hash: 5
diff --git a/tests/test8.c b/tests/test8.c
new file mode 100644
index 000000000..3497101b6
--- /dev/null
+++ b/tests/test8.c
@@ -0,0 +1,40 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ printf("num_items in hash: %u\n", user->hh.tbl->num_items);
+ }
+
+ /* delete each even ID */
+ for(i=0; i<10; i+=2) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ HASH_DEL(users,tmp);
+ free(tmp);
+ printf("deleted; num_items in hash: %u\n", user->hh.tbl->num_items);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+ return 0;
+}
diff --git a/tests/test80.ans b/tests/test80.ans
new file mode 100644
index 000000000..763f6377a
--- /dev/null
+++ b/tests/test80.ans
@@ -0,0 +1,6 @@
+0 1 2 3 4 5 6 7 8 9
+len: 10
+
+0 1 2 3 4 5 6 7 8 9 0 11
+len: 12
+
diff --git a/tests/test80.c b/tests/test80.c
new file mode 100644
index 000000000..293fac853
--- /dev/null
+++ b/tests/test80.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *a;
+ int i, *p;
+ utarray_new(a, &ut_int_icd);
+ for(i=0; i<10; i++) {
+ utarray_push_back(a,&i);
+ }
+ for(p=(int*)utarray_front(a); p!=NULL; p=(int*)utarray_next(a,p)) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ printf("len: %u\n\n", utarray_len(a));
+
+ i=11;
+ utarray_insert(a, &i, 11);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ printf("len: %u\n\n", utarray_len(a));
+
+ utarray_free(a);
+ return 0;
+}
+
diff --git a/tests/test81.ans b/tests/test81.ans
new file mode 100644
index 000000000..24adbd9c2
--- /dev/null
+++ b/tests/test81.ans
@@ -0,0 +1,6 @@
+0 1 2 3 4 5 6 7 8 9
+len: 10
+
+0 1 2 3 4 5 6 7 8 9 10
+len: 11
+
diff --git a/tests/test81.c b/tests/test81.c
new file mode 100644
index 000000000..90494379b
--- /dev/null
+++ b/tests/test81.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *a;
+ int i, *p;
+ utarray_new(a, &ut_int_icd);
+ for(i=0; i<10; i++) {
+ utarray_push_back(a,&i);
+ }
+ for(p=(int*)utarray_front(a); p!=NULL; p=(int*)utarray_next(a,p)) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ printf("len: %u\n\n", utarray_len(a));
+
+ i=10;
+ utarray_insert(a, &i, 10);
+ while ( (p=(int*)utarray_next(a,p)) != NULL ) {
+ printf("%d ", *p);
+ }
+ printf("\n");
+ printf("len: %u\n\n", utarray_len(a));
+
+ utarray_free(a);
+ return 0;
+}
+
diff --git a/tests/test82.ans b/tests/test82.ans
new file mode 100644
index 000000000..ce8f5b579
--- /dev/null
+++ b/tests/test82.ans
@@ -0,0 +1,9 @@
+0 1 2 3 4 5 6 7 8 9
+len: 10
+
+0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 len: 20
+
+0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 len: 30
+
+0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 0 1 2 3 4 5 6 7 8 9 5 6 7 8 9 len: 40
+
diff --git a/tests/test82.c b/tests/test82.c
new file mode 100644
index 000000000..3a5d05f72
--- /dev/null
+++ b/tests/test82.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include "utarray.h"
+
+int main()
+{
+ UT_array *a,*b;
+ int i, *p;
+ utarray_new(a, &ut_int_icd);
+ utarray_new(b, &ut_int_icd);
+
+ for(i=0; i<10; i++) {
+ utarray_push_back(a,&i);
+ }
+ for(p=(int*)utarray_front(a); p!=NULL; p=(int*)utarray_next(a,p)) {
+ printf("%d ",*p);
+ }
+ printf("\n");
+ printf("len: %u\n\n", utarray_len(a));
+
+ utarray_inserta(b,a,10);
+ for(p=(int*)utarray_front(b); p!=NULL; p=(int*)utarray_next(b,p)) {
+ printf("%d ",*p);
+ }
+ printf("len: %u\n\n", utarray_len(b));
+
+ utarray_inserta(b,a,0);
+ for(p=(int*)utarray_front(b); p!=NULL; p=(int*)utarray_next(b,p)) {
+ printf("%d ",*p);
+ }
+ printf("len: %u\n\n", utarray_len(b));
+
+ utarray_inserta(b,a,25);
+ for(p=(int*)utarray_front(b); p!=NULL; p=(int*)utarray_next(b,p)) {
+ printf("%d ",*p);
+ }
+ printf("len: %u\n\n", utarray_len(b));
+
+ utarray_free(a);
+ utarray_free(b);
+ return 0;
+}
+
diff --git a/tests/test83.ans b/tests/test83.ans
new file mode 100644
index 000000000..ada2c442b
--- /dev/null
+++ b/tests/test83.ans
@@ -0,0 +1,41 @@
+added bob (id 0)
+added jack (id 1)
+added gary (id 2)
+added ty (id 3)
+added bo (id 4)
+added phil (id 5)
+added art (id 6)
+added gil (id 7)
+added buck (id 8)
+added ted (id 9)
+found bob (id 0)
+replaced (y) with bob (id 0)
+found jack (id 1)
+replaced (y) with jack (id 10)
+found gary (id 2)
+replaced (y) with gary (id 20)
+found ty (id 3)
+replaced (y) with ty (id 30)
+found bo (id 4)
+replaced (y) with bo (id 40)
+found phil (id 5)
+replaced (y) with phil (id 50)
+found art (id 6)
+replaced (y) with art (id 60)
+found gil (id 7)
+replaced (y) with gil (id 70)
+found buck (id 8)
+replaced (y) with buck (id 80)
+found ted (id 9)
+replaced (y) with ted (id 90)
+traversing...
+bob (id 0)
+jack (id 10)
+gary (id 20)
+ty (id 30)
+bo (id 40)
+phil (id 50)
+art (id 60)
+gil (id 70)
+buck (id 80)
+ted (id 90)
diff --git a/tests/test83.c b/tests/test83.c
new file mode 100644
index 000000000..b2140c54d
--- /dev/null
+++ b/tests/test83.c
@@ -0,0 +1,61 @@
+#include "uthash.h"
+#include <stdio.h>
+#include <stdlib.h> /* malloc */
+
+typedef struct person_t {
+ char first_name[10];
+ int id;
+ UT_hash_handle hh;
+} person_t;
+
+int main()
+{
+ person_t *people=NULL, *person, *new_person, *tmp;
+ const char **name;
+ const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art",
+ "gil", "buck", "ted", NULL
+ };
+ int id=0;
+
+ for(name=names; *name!=NULL; name++) {
+ person = (person_t*)malloc(sizeof(person_t));
+ if (person == NULL) {
+ exit(-1);
+ }
+ strcpy(person->first_name, *name);
+ person->id = id++;
+ HASH_ADD_STR(people,first_name,person);
+ printf("added %s (id %d)\n", person->first_name, person->id);
+ }
+
+ person=NULL;
+ person_t **p=&person;
+
+ for(name=names; *name!=NULL; name++) {
+ HASH_FIND_STR(people,*name,*p);
+ if (person != NULL) {
+ printf("found %s (id %d)\n", person->first_name, person->id);
+ new_person = (person_t *)malloc(sizeof(person_t));
+ if (new_person == NULL) {
+ exit(-1);
+ }
+ memcpy(new_person, person, sizeof(person_t));
+ new_person->id = person->id*10;
+ HASH_REPLACE_STR(people,first_name,new_person,tmp);
+ printf("replaced (%c) with %s (id %d)\n", (tmp!=NULL)?'y':'n', new_person->first_name, new_person->id);
+ if (tmp != NULL) {
+ free(tmp);
+ }
+ } else {
+ printf("failed to find %s\n", *name);
+ }
+ }
+
+ printf("traversing... \n");
+ HASH_ITER(hh, people, person, tmp) {
+ printf("%s (id %d)\n", person->first_name, person->id);
+ HASH_DEL(people,person);
+ free(person);
+ }
+ return 0;
+}
diff --git a/tests/test84.ans b/tests/test84.ans
new file mode 100644
index 000000000..ada2c442b
--- /dev/null
+++ b/tests/test84.ans
@@ -0,0 +1,41 @@
+added bob (id 0)
+added jack (id 1)
+added gary (id 2)
+added ty (id 3)
+added bo (id 4)
+added phil (id 5)
+added art (id 6)
+added gil (id 7)
+added buck (id 8)
+added ted (id 9)
+found bob (id 0)
+replaced (y) with bob (id 0)
+found jack (id 1)
+replaced (y) with jack (id 10)
+found gary (id 2)
+replaced (y) with gary (id 20)
+found ty (id 3)
+replaced (y) with ty (id 30)
+found bo (id 4)
+replaced (y) with bo (id 40)
+found phil (id 5)
+replaced (y) with phil (id 50)
+found art (id 6)
+replaced (y) with art (id 60)
+found gil (id 7)
+replaced (y) with gil (id 70)
+found buck (id 8)
+replaced (y) with buck (id 80)
+found ted (id 9)
+replaced (y) with ted (id 90)
+traversing...
+bob (id 0)
+jack (id 10)
+gary (id 20)
+ty (id 30)
+bo (id 40)
+phil (id 50)
+art (id 60)
+gil (id 70)
+buck (id 80)
+ted (id 90)
diff --git a/tests/test84.c b/tests/test84.c
new file mode 100644
index 000000000..cbbfdedf7
--- /dev/null
+++ b/tests/test84.c
@@ -0,0 +1,71 @@
+#include "uthash.h"
+#include <stdio.h>
+#include <stdlib.h> /* malloc */
+
+typedef struct person_t {
+ char *first_name;
+ int id;
+ UT_hash_handle hh;
+} person_t;
+
+int main()
+{
+ person_t *people=NULL, *person, *new_person, *tmp;
+ const char **name;
+ const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art",
+ "gil", "buck", "ted", NULL
+ };
+ int id=0;
+
+ for(name=names; *name!=NULL; name++) {
+ person = (person_t*)malloc(sizeof(person_t));
+ if (person == NULL) {
+ exit(-1);
+ }
+ person->first_name = (char*)malloc(10UL);
+ if (person->first_name == NULL) {
+ exit(-1);
+ }
+ strcpy(person->first_name, *name);
+ person->id = id++;
+ HASH_ADD_STR(people,first_name,person);
+ printf("added %s (id %d)\n", person->first_name, person->id);
+ }
+
+ person=NULL;
+ person_t **p=&person;
+
+ for(name=names; *name!=NULL; name++) {
+ HASH_FIND_STR(people,*name,*p);
+ if (person != NULL) {
+ printf("found %s (id %d)\n", person->first_name, person->id);
+ new_person = (person_t*)malloc(sizeof(person_t));
+ if (new_person == NULL) {
+ exit(-1);
+ }
+ new_person->first_name = (char*)malloc(10UL);
+ if (new_person->first_name == NULL) {
+ exit(-1);
+ }
+ strcpy(new_person->first_name, person->first_name);
+ new_person->id = person->id*10;
+ HASH_REPLACE_STR(people,first_name,new_person,tmp);
+ printf("replaced (%c) with %s (id %d)\n", (tmp!=NULL)?'y':'n', new_person->first_name, new_person->id);
+ if (tmp != NULL) {
+ free(tmp->first_name);
+ free(tmp);
+ }
+ } else {
+ printf("failed to find %s\n", *name);
+ }
+ }
+
+ printf("traversing... \n");
+ HASH_ITER(hh, people, person, tmp) {
+ printf("%s (id %d)\n", person->first_name, person->id);
+ HASH_DEL(people,person);
+ free(person->first_name);
+ free(person);
+ }
+ return 0;
+}
diff --git a/tests/test85.ans b/tests/test85.ans
new file mode 100644
index 000000000..1a0715f3d
--- /dev/null
+++ b/tests/test85.ans
@@ -0,0 +1,2 @@
+overhead non-zero
+overhead zero
diff --git a/tests/test85.c b/tests/test85.c
new file mode 100644
index 000000000..bb62ca636
--- /dev/null
+++ b/tests/test85.c
@@ -0,0 +1,35 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ size_t s = HASH_OVERHEAD(hh,users);
+ printf("overhead %s\n", (s==0U)?"zero":"non-zero");
+ HASH_CLEAR(hh,users);
+ // should free those elements
+ // but this test is not concerned with that
+ s = HASH_OVERHEAD(hh,users);
+ printf("overhead %s\n", (s==0U)?"zero":"non-zero");
+ return 0;
+}
diff --git a/tests/test86.ans b/tests/test86.ans
new file mode 100644
index 000000000..6533053eb
--- /dev/null
+++ b/tests/test86.ans
@@ -0,0 +1,79 @@
+CDL appends
+a b c
+count = 3
+Test CDL_PREPEND_ELEM d with elt NULL
+a b c d
+Test CDL_PREPEND_ELEM e before item b
+a e b c d
+Test CDL_APPEND_ELEM f with elt NULL
+f a e b c d
+Test CDL_APPEND_ELEM g after item b
+f a e b g c d
+count = 7
+advancing head pointer
+a e b g c d f
+a e b g c d f a e b g c d f a e b g c d
+a f d c g b e a f d
+deleting (b)
+a e g c d f
+deleting (a)
+e g c d f
+deleting (c)
+e g d f
+deleting (g)
+e d f
+deleting (e)
+d f
+deleting (d)
+f deleting (f)
+
+DL appends
+a b c
+count = 3
+Test DL_PREPEND_ELEM d with elt NULL
+a b c d
+Test DL_PREPEND_ELEM e before item b
+a e b c d
+Test DL_APPEND_ELEM f with elt NULL
+f a e b c d
+Test DL_APPEND_ELEM g after item b
+f a e b g c d
+count = 7
+deleting (b)
+f a e g c d
+deleting (a)
+f e g c d
+deleting (c)
+f e g d
+deleting (g)
+f e d
+deleting (e)
+f d
+deleting (d)
+f deleting (f)
+
+LL appends
+a b c
+count = 3
+Test LL_PREPEND_ELEM d with elt NULL
+a b c d
+Test LL_PREPEND_ELEM e before item b
+a e b c d
+Test LL_APPEND_ELEM f with elt NULL
+f a e b c d
+Test LL_APPEND_ELEM g after item b
+f a e b g c d
+count = 7
+deleting (b)
+f a e g c d
+deleting (a)
+f e g c d
+deleting (c)
+f e g d
+deleting (g)
+f e d
+deleting (e)
+f d
+deleting (d)
+f deleting (f)
+
diff --git a/tests/test86.c b/tests/test86.c
new file mode 100644
index 000000000..fb5723f32
--- /dev/null
+++ b/tests/test86.c
@@ -0,0 +1,296 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id;
+ struct el *next, *prev;
+} el;
+
+int main()
+{
+ int i;
+ int count;
+ el els[10], *e;
+ el *head = NULL;
+ el *zeroptr = NULL;
+ for(i=0; i<10; i++) {
+ els[i].id=(int)'a'+i;
+ }
+
+ /* test CDL macros */
+ printf("CDL appends\n");
+ CDL_APPEND(head,&els[0]);
+ CDL_APPEND(head,&els[1]);
+ CDL_APPEND(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ printf("Test CDL_PREPEND_ELEM %c with elt NULL\n", els[3].id);
+ CDL_PREPEND_ELEM(head, zeroptr, &els[3]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test CDL_PREPEND_ELEM %c before item %c\n", els[4].id, els[1].id);
+ CDL_PREPEND_ELEM(head, &els[1], &els[4]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test CDL_APPEND_ELEM %c with elt NULL\n", els[5].id);
+ CDL_APPEND_ELEM(head, zeroptr, &els[5]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test CDL_APPEND_ELEM %c after item %c\n", els[6].id, els[1].id);
+ CDL_APPEND_ELEM(head, &els[1], &els[6]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ /* point head to head->next */
+ printf("advancing head pointer\n");
+ head = head->next;
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop a few times */
+ for(i=0,e=head; e && i<20; i++,e=e->next) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* follow circular loop backwards a few times */
+ for(i=0,e=head; e && i<10; i++,e=e->prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("deleting (b)\n");
+ CDL_DELETE(head,&els[1]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (a)\n");
+ CDL_DELETE(head,&els[0]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (c)\n");
+ CDL_DELETE(head,&els[2]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (g)\n");
+ CDL_DELETE(head,&els[6]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (e)\n");
+ CDL_DELETE(head,&els[4]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (d)\n");
+ CDL_DELETE(head,&els[3]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("deleting (f)\n");
+ CDL_DELETE(head,&els[5]);
+ CDL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ /* test DL macros */
+ printf("DL appends\n");
+
+ DL_APPEND(head,&els[0]);
+ DL_APPEND(head,&els[1]);
+ DL_APPEND(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ printf("Test DL_PREPEND_ELEM %c with elt NULL\n", els[3].id);
+ DL_PREPEND_ELEM(head, zeroptr, &els[3]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test DL_PREPEND_ELEM %c before item %c\n", els[4].id, els[1].id);
+ DL_PREPEND_ELEM(head, &els[1], &els[4]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test DL_APPEND_ELEM %c with elt NULL\n", els[5].id);
+ DL_APPEND_ELEM(head, zeroptr, &els[5]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test DL_APPEND_ELEM %c after item %c\n", els[6].id, els[1].id);
+ DL_APPEND_ELEM(head, &els[1], &els[6]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ DL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ printf("deleting (b)\n");
+ DL_DELETE(head,&els[1]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (a)\n");
+ DL_DELETE(head,&els[0]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (c)\n");
+ DL_DELETE(head,&els[2]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (g)\n");
+ DL_DELETE(head,&els[6]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (e)\n");
+ DL_DELETE(head,&els[4]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (d)\n");
+ DL_DELETE(head,&els[3]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("deleting (f)\n");
+ DL_DELETE(head,&els[5]);
+ DL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+
+ /* test LL macros */
+ printf("LL appends\n");
+
+ LL_APPEND(head,&els[0]);
+ LL_APPEND(head,&els[1]);
+ LL_APPEND(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ printf("Test LL_PREPEND_ELEM %c with elt NULL\n", els[3].id);
+ LL_PREPEND_ELEM(head, zeroptr, &els[3]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test LL_PREPEND_ELEM %c before item %c\n", els[4].id, els[1].id);
+ LL_PREPEND_ELEM(head, &els[1], &els[4]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test LL_APPEND_ELEM %c with elt NULL\n", els[5].id);
+ LL_APPEND_ELEM(head, zeroptr, &els[5]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("Test LL_APPEND_ELEM %c after item %c\n", els[6].id, els[1].id);
+ LL_APPEND_ELEM(head, &els[1], &els[6]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ LL_COUNT(head,e, count);
+ printf("count = %d\n", count);
+
+ printf("deleting (b)\n");
+ LL_DELETE(head,&els[1]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (a)\n");
+ LL_DELETE(head,&els[0]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (c)\n");
+ LL_DELETE(head,&els[2]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (g)\n");
+ LL_DELETE(head,&els[6]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (e)\n");
+ LL_DELETE(head,&els[4]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("deleting (d)\n");
+ LL_DELETE(head,&els[3]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("deleting (f)\n");
+ LL_DELETE(head,&els[5]);
+ LL_FOREACH(head,e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test87.ans b/tests/test87.ans
new file mode 100644
index 000000000..32fc95e09
--- /dev/null
+++ b/tests/test87.ans
@@ -0,0 +1,78 @@
+1: muh3
+2: muh1
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+43: muh12
+###
+1: muh3
+2: muh1
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+7: muh12
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+2: muh1
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+7: muh12
+8: muh2
+8: muh4
+9: muh10
+9: muh3
+10: muh11
+15: muh8
+###
+2: muh1
+3: muh5
+5: muh6
+6: muh9
+7: muh12
+8: muh2
+8: muh4
+9: muh10
+9: muh3
+10: muh11
+15: muh8
+16: muh7
+###
+2: muh1
+3: muh5
+5: muh6
+6: muh9
+7: muh12
+8: muh4
+9: muh10
+9: muh3
+10: muh11
+15: muh8
+16: muh7
+###
+2: muh1
+3: muh5
+5: muh6
+6: muh9
+7: muh12
+8: muh4
+8: muh2
+9: muh10
+9: muh3
+10: muh11
+15: muh8
+16: muh7
+###
+###
diff --git a/tests/test87.c b/tests/test87.c
new file mode 100644
index 000000000..c17275ebc
--- /dev/null
+++ b/tests/test87.c
@@ -0,0 +1,116 @@
+#include <assert.h>
+#include <stdio.h>
+#include "uthash.h"
+
+typedef struct {
+ char name[32];
+ int weight;
+ UT_hash_handle hh;
+} hstruct_t;
+
+static int cmpfunc(const hstruct_t *s1, const hstruct_t *s2)
+{
+ return (s1->weight < s2->weight) ? -1 : (s1->weight > s2->weight);
+}
+
+// Test that CMPFUNC can safely be a macro.
+#define CMPFUNC(a,b) cmpfunc(a,b)
+
+void printtable(const hstruct_t *hTable)
+{
+ const hstruct_t *search, *tmp;
+ HASH_ITER(hh, hTable, search, tmp) {
+ printf("%d: %s\n", search->weight, search->name);
+ }
+ printf("###\n");
+}
+
+void delitem(hstruct_t **hTable, const char *name)
+{
+ hstruct_t *item;
+ HASH_FIND_STR(*hTable, name, item);
+ HASH_DEL(*hTable, item);
+}
+
+int main()
+{
+ hstruct_t *hTable = NULL;
+ hstruct_t *replaced = NULL;
+ unsigned hashvalue;
+
+ hstruct_t tst[] = {
+ {"muh1", 2, {0}},
+ {"muh2", 8, {0}},
+ {"muh3", 1, {0}},
+ {"muh4", 8, {0}},
+ {"muh5", 3, {0}},
+ {"muh6", 5, {0}},
+ {"muh7", 6, {0}},
+ {"muh8", 15, {0}},
+ {"muh9", 6, {0}},
+ {"muh10", 9, {0}},
+ {"muh11", 10, {0}},
+ {"muh12", 43, {0}},
+ {"muh12", 7, {0}}
+ };
+
+ int index;
+ for (index = 0; index < 11; ++index) {
+ HASH_ADD_INORDER(hh, hTable, name[0], strlen(tst[index].name), &tst[index], CMPFUNC);
+ }
+
+ // test HASH_ADD_BYHASHVALUE_INORDER
+ HASH_VALUE(tst[11].name, strlen(tst[11].name), hashvalue);
+ HASH_ADD_BYHASHVALUE_INORDER(hh, hTable, name[0], strlen(tst[11].name), hashvalue, &tst[11], CMPFUNC);
+
+ printtable(hTable);
+
+ // replace "43: muh12" with "7: muh12"
+ HASH_REPLACE_INORDER(hh, hTable, name[0], strlen(tst[11].name), &tst[12], replaced, CMPFUNC);
+ assert(replaced == &tst[11]);
+
+ printtable(hTable);
+
+ // rehash "1: muh3" to "9: muh3"
+ tst[2].weight = 9;
+ HASH_REPLACE_INORDER(hh, hTable, name[0], strlen(tst[2].name), &tst[2], replaced, CMPFUNC);
+ assert(replaced == &tst[2]);
+
+ printtable(hTable);
+
+ // rehash "6: muh7" to "16: muh7"
+ tst[6].weight = 16;
+ HASH_VALUE(&tst[6].name[0], strlen(tst[6].name), hashvalue);
+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, hTable, name[0], strlen(tst[6].name), hashvalue, &tst[6], replaced, CMPFUNC);
+ assert(replaced == &tst[6]);
+
+ printtable(hTable);
+
+ // remove "8: muh2"...
+ HASH_DELETE(hh, hTable, &tst[1]);
+
+ printtable(hTable);
+
+ // ...and then reinsert "8: muh2"
+ HASH_VALUE(tst[1].name, strlen(tst[1].name), hashvalue);
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, hTable, tst[1].name, strlen(tst[1].name), hashvalue, &tst[1], CMPFUNC);
+
+ printtable(hTable);
+
+ delitem(&hTable, "muh1");
+ delitem(&hTable, "muh7");
+ delitem(&hTable, "muh3");
+ delitem(&hTable, "muh9");
+ delitem(&hTable, "muh2");
+ delitem(&hTable, "muh11");
+ delitem(&hTable, "muh4");
+ delitem(&hTable, "muh6");
+ delitem(&hTable, "muh5");
+ delitem(&hTable, "muh8");
+ delitem(&hTable, "muh10");
+ delitem(&hTable, "muh12");
+
+ printtable(hTable);
+
+ return 0;
+}
diff --git a/tests/test88.ans b/tests/test88.ans
new file mode 100644
index 000000000..6f35d6e98
--- /dev/null
+++ b/tests/test88.ans
@@ -0,0 +1,30 @@
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
+alt_strlen
+alt_memcmp
diff --git a/tests/test88.c b/tests/test88.c
new file mode 100644
index 000000000..46f3ee76f
--- /dev/null
+++ b/tests/test88.c
@@ -0,0 +1,68 @@
+#ifdef HASH_FUNCTION
+#undef HASH_FUNCTION /* this test's output depends on the pattern of hash collisions */
+#endif
+
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+/* This is mostly a copy of test6.c. */
+
+#undef uthash_memcmp
+#undef uthash_strlen
+#define uthash_memcmp(a,b,n) alt_memcmp(a,b,n)
+#define uthash_strlen(s) alt_strlen(s)
+
+typedef struct example_user_t {
+ char id[3];
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+static int alt_memcmp(const void *a, const void *b, size_t n)
+{
+ puts("alt_memcmp");
+ return memcmp(a,b,n);
+}
+
+static size_t alt_strlen(const char *s)
+{
+ puts("alt_strlen");
+ return strlen(s);
+}
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for (i=0; i<10; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ sprintf(user->id, "%d", i);
+ user->cookie = i*i;
+ HASH_ADD_STR(users,id,user);
+ }
+
+ /* delete each ID */
+ for (i=0; i<10; i++) {
+ char buffer[3];
+ sprintf(buffer, "%d", i);
+ HASH_FIND_STR(users,buffer,tmp);
+ if (tmp != NULL) {
+ HASH_DEL(users,tmp);
+ free(tmp);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+
+ /* show the hash */
+ for (user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
+ printf("user %s, cookie %d\n", user->id, user->cookie);
+ }
+ return 0;
+}
diff --git a/tests/test89.ans b/tests/test89.ans
new file mode 100644
index 000000000..8e63ada73
--- /dev/null
+++ b/tests/test89.ans
@@ -0,0 +1,5 @@
+node #0, timeout: 100
+node #1, timeout: 200
+node #2, timeout: 300
+node #3, timeout: 400
+node #4, timeout: 500
diff --git a/tests/test89.c b/tests/test89.c
new file mode 100644
index 000000000..74d7ec488
--- /dev/null
+++ b/tests/test89.c
@@ -0,0 +1,65 @@
+/* Minified version of code from tinydtls 0.9 */
+/* See https://projects.eclipse.org/projects/iot.tinydtls */
+
+#include "utlist.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef int clock_time_t;
+
+typedef struct netq_t {
+ struct netq_t *next;
+ clock_time_t t;
+} netq_t;
+
+void dump_queue(struct netq_t *queue)
+{
+ struct netq_t *p;
+ int i = 0;
+
+ if (!queue) {
+ printf("(null)\n");
+ } else {
+ LL_FOREACH(queue, p) {
+ printf("node #%d, timeout: %d\n", i++, p->t);
+ }
+ }
+}
+
+int netq_insert_node(netq_t **queue, netq_t *node)
+{
+ netq_t *p = *queue;
+ while (p && p->t <= node->t) {
+ p = p->next;
+ }
+ /* *INDENT-OFF* */
+ if (p)
+ LL_PREPEND_ELEM(*queue, p, node);
+ else
+ LL_APPEND(*queue, node);
+ /* *INDENT-ON* */
+ return 1;
+}
+
+int main()
+{
+ struct netq_t *nq = NULL;
+ size_t i;
+
+ clock_time_t timestamps[] = { 300, 100, 200, 400, 500 };
+
+ for (i = 0; i < sizeof(timestamps)/sizeof(clock_time_t); i++) {
+ struct netq_t *node = (struct netq_t *)malloc(sizeof *node);
+ memset(node, '\0', sizeof *node);
+ node->t = timestamps[i];
+
+ if (netq_insert_node(&nq, node) != 1) {
+ puts("ERROR");
+ }
+ }
+
+ dump_queue(nq);
+ return 0;
+}
diff --git a/tests/test9.ans b/tests/test9.ans
new file mode 100644
index 000000000..13a9402ed
--- /dev/null
+++ b/tests/test9.ans
@@ -0,0 +1,500 @@
+user 0, cookie 0
+user 2, cookie 4
+user 4, cookie 16
+user 6, cookie 36
+user 8, cookie 64
+user 10, cookie 100
+user 12, cookie 144
+user 14, cookie 196
+user 16, cookie 256
+user 18, cookie 324
+user 20, cookie 400
+user 22, cookie 484
+user 24, cookie 576
+user 26, cookie 676
+user 28, cookie 784
+user 30, cookie 900
+user 32, cookie 1024
+user 34, cookie 1156
+user 36, cookie 1296
+user 38, cookie 1444
+user 40, cookie 1600
+user 42, cookie 1764
+user 44, cookie 1936
+user 46, cookie 2116
+user 48, cookie 2304
+user 50, cookie 2500
+user 52, cookie 2704
+user 54, cookie 2916
+user 56, cookie 3136
+user 58, cookie 3364
+user 60, cookie 3600
+user 62, cookie 3844
+user 64, cookie 4096
+user 66, cookie 4356
+user 68, cookie 4624
+user 70, cookie 4900
+user 72, cookie 5184
+user 74, cookie 5476
+user 76, cookie 5776
+user 78, cookie 6084
+user 80, cookie 6400
+user 82, cookie 6724
+user 84, cookie 7056
+user 86, cookie 7396
+user 88, cookie 7744
+user 90, cookie 8100
+user 92, cookie 8464
+user 94, cookie 8836
+user 96, cookie 9216
+user 98, cookie 9604
+user 100, cookie 10000
+user 102, cookie 10404
+user 104, cookie 10816
+user 106, cookie 11236
+user 108, cookie 11664
+user 110, cookie 12100
+user 112, cookie 12544
+user 114, cookie 12996
+user 116, cookie 13456
+user 118, cookie 13924
+user 120, cookie 14400
+user 122, cookie 14884
+user 124, cookie 15376
+user 126, cookie 15876
+user 128, cookie 16384
+user 130, cookie 16900
+user 132, cookie 17424
+user 134, cookie 17956
+user 136, cookie 18496
+user 138, cookie 19044
+user 140, cookie 19600
+user 142, cookie 20164
+user 144, cookie 20736
+user 146, cookie 21316
+user 148, cookie 21904
+user 150, cookie 22500
+user 152, cookie 23104
+user 154, cookie 23716
+user 156, cookie 24336
+user 158, cookie 24964
+user 160, cookie 25600
+user 162, cookie 26244
+user 164, cookie 26896
+user 166, cookie 27556
+user 168, cookie 28224
+user 170, cookie 28900
+user 172, cookie 29584
+user 174, cookie 30276
+user 176, cookie 30976
+user 178, cookie 31684
+user 180, cookie 32400
+user 182, cookie 33124
+user 184, cookie 33856
+user 186, cookie 34596
+user 188, cookie 35344
+user 190, cookie 36100
+user 192, cookie 36864
+user 194, cookie 37636
+user 196, cookie 38416
+user 198, cookie 39204
+user 200, cookie 40000
+user 202, cookie 40804
+user 204, cookie 41616
+user 206, cookie 42436
+user 208, cookie 43264
+user 210, cookie 44100
+user 212, cookie 44944
+user 214, cookie 45796
+user 216, cookie 46656
+user 218, cookie 47524
+user 220, cookie 48400
+user 222, cookie 49284
+user 224, cookie 50176
+user 226, cookie 51076
+user 228, cookie 51984
+user 230, cookie 52900
+user 232, cookie 53824
+user 234, cookie 54756
+user 236, cookie 55696
+user 238, cookie 56644
+user 240, cookie 57600
+user 242, cookie 58564
+user 244, cookie 59536
+user 246, cookie 60516
+user 248, cookie 61504
+user 250, cookie 62500
+user 252, cookie 63504
+user 254, cookie 64516
+user 256, cookie 65536
+user 258, cookie 66564
+user 260, cookie 67600
+user 262, cookie 68644
+user 264, cookie 69696
+user 266, cookie 70756
+user 268, cookie 71824
+user 270, cookie 72900
+user 272, cookie 73984
+user 274, cookie 75076
+user 276, cookie 76176
+user 278, cookie 77284
+user 280, cookie 78400
+user 282, cookie 79524
+user 284, cookie 80656
+user 286, cookie 81796
+user 288, cookie 82944
+user 290, cookie 84100
+user 292, cookie 85264
+user 294, cookie 86436
+user 296, cookie 87616
+user 298, cookie 88804
+user 300, cookie 90000
+user 302, cookie 91204
+user 304, cookie 92416
+user 306, cookie 93636
+user 308, cookie 94864
+user 310, cookie 96100
+user 312, cookie 97344
+user 314, cookie 98596
+user 316, cookie 99856
+user 318, cookie 101124
+user 320, cookie 102400
+user 322, cookie 103684
+user 324, cookie 104976
+user 326, cookie 106276
+user 328, cookie 107584
+user 330, cookie 108900
+user 332, cookie 110224
+user 334, cookie 111556
+user 336, cookie 112896
+user 338, cookie 114244
+user 340, cookie 115600
+user 342, cookie 116964
+user 344, cookie 118336
+user 346, cookie 119716
+user 348, cookie 121104
+user 350, cookie 122500
+user 352, cookie 123904
+user 354, cookie 125316
+user 356, cookie 126736
+user 358, cookie 128164
+user 360, cookie 129600
+user 362, cookie 131044
+user 364, cookie 132496
+user 366, cookie 133956
+user 368, cookie 135424
+user 370, cookie 136900
+user 372, cookie 138384
+user 374, cookie 139876
+user 376, cookie 141376
+user 378, cookie 142884
+user 380, cookie 144400
+user 382, cookie 145924
+user 384, cookie 147456
+user 386, cookie 148996
+user 388, cookie 150544
+user 390, cookie 152100
+user 392, cookie 153664
+user 394, cookie 155236
+user 396, cookie 156816
+user 398, cookie 158404
+user 400, cookie 160000
+user 402, cookie 161604
+user 404, cookie 163216
+user 406, cookie 164836
+user 408, cookie 166464
+user 410, cookie 168100
+user 412, cookie 169744
+user 414, cookie 171396
+user 416, cookie 173056
+user 418, cookie 174724
+user 420, cookie 176400
+user 422, cookie 178084
+user 424, cookie 179776
+user 426, cookie 181476
+user 428, cookie 183184
+user 430, cookie 184900
+user 432, cookie 186624
+user 434, cookie 188356
+user 436, cookie 190096
+user 438, cookie 191844
+user 440, cookie 193600
+user 442, cookie 195364
+user 444, cookie 197136
+user 446, cookie 198916
+user 448, cookie 200704
+user 450, cookie 202500
+user 452, cookie 204304
+user 454, cookie 206116
+user 456, cookie 207936
+user 458, cookie 209764
+user 460, cookie 211600
+user 462, cookie 213444
+user 464, cookie 215296
+user 466, cookie 217156
+user 468, cookie 219024
+user 470, cookie 220900
+user 472, cookie 222784
+user 474, cookie 224676
+user 476, cookie 226576
+user 478, cookie 228484
+user 480, cookie 230400
+user 482, cookie 232324
+user 484, cookie 234256
+user 486, cookie 236196
+user 488, cookie 238144
+user 490, cookie 240100
+user 492, cookie 242064
+user 494, cookie 244036
+user 496, cookie 246016
+user 498, cookie 248004
+user 500, cookie 250000
+user 502, cookie 252004
+user 504, cookie 254016
+user 506, cookie 256036
+user 508, cookie 258064
+user 510, cookie 260100
+user 512, cookie 262144
+user 514, cookie 264196
+user 516, cookie 266256
+user 518, cookie 268324
+user 520, cookie 270400
+user 522, cookie 272484
+user 524, cookie 274576
+user 526, cookie 276676
+user 528, cookie 278784
+user 530, cookie 280900
+user 532, cookie 283024
+user 534, cookie 285156
+user 536, cookie 287296
+user 538, cookie 289444
+user 540, cookie 291600
+user 542, cookie 293764
+user 544, cookie 295936
+user 546, cookie 298116
+user 548, cookie 300304
+user 550, cookie 302500
+user 552, cookie 304704
+user 554, cookie 306916
+user 556, cookie 309136
+user 558, cookie 311364
+user 560, cookie 313600
+user 562, cookie 315844
+user 564, cookie 318096
+user 566, cookie 320356
+user 568, cookie 322624
+user 570, cookie 324900
+user 572, cookie 327184
+user 574, cookie 329476
+user 576, cookie 331776
+user 578, cookie 334084
+user 580, cookie 336400
+user 582, cookie 338724
+user 584, cookie 341056
+user 586, cookie 343396
+user 588, cookie 345744
+user 590, cookie 348100
+user 592, cookie 350464
+user 594, cookie 352836
+user 596, cookie 355216
+user 598, cookie 357604
+user 600, cookie 360000
+user 602, cookie 362404
+user 604, cookie 364816
+user 606, cookie 367236
+user 608, cookie 369664
+user 610, cookie 372100
+user 612, cookie 374544
+user 614, cookie 376996
+user 616, cookie 379456
+user 618, cookie 381924
+user 620, cookie 384400
+user 622, cookie 386884
+user 624, cookie 389376
+user 626, cookie 391876
+user 628, cookie 394384
+user 630, cookie 396900
+user 632, cookie 399424
+user 634, cookie 401956
+user 636, cookie 404496
+user 638, cookie 407044
+user 640, cookie 409600
+user 642, cookie 412164
+user 644, cookie 414736
+user 646, cookie 417316
+user 648, cookie 419904
+user 650, cookie 422500
+user 652, cookie 425104
+user 654, cookie 427716
+user 656, cookie 430336
+user 658, cookie 432964
+user 660, cookie 435600
+user 662, cookie 438244
+user 664, cookie 440896
+user 666, cookie 443556
+user 668, cookie 446224
+user 670, cookie 448900
+user 672, cookie 451584
+user 674, cookie 454276
+user 676, cookie 456976
+user 678, cookie 459684
+user 680, cookie 462400
+user 682, cookie 465124
+user 684, cookie 467856
+user 686, cookie 470596
+user 688, cookie 473344
+user 690, cookie 476100
+user 692, cookie 478864
+user 694, cookie 481636
+user 696, cookie 484416
+user 698, cookie 487204
+user 700, cookie 490000
+user 702, cookie 492804
+user 704, cookie 495616
+user 706, cookie 498436
+user 708, cookie 501264
+user 710, cookie 504100
+user 712, cookie 506944
+user 714, cookie 509796
+user 716, cookie 512656
+user 718, cookie 515524
+user 720, cookie 518400
+user 722, cookie 521284
+user 724, cookie 524176
+user 726, cookie 527076
+user 728, cookie 529984
+user 730, cookie 532900
+user 732, cookie 535824
+user 734, cookie 538756
+user 736, cookie 541696
+user 738, cookie 544644
+user 740, cookie 547600
+user 742, cookie 550564
+user 744, cookie 553536
+user 746, cookie 556516
+user 748, cookie 559504
+user 750, cookie 562500
+user 752, cookie 565504
+user 754, cookie 568516
+user 756, cookie 571536
+user 758, cookie 574564
+user 760, cookie 577600
+user 762, cookie 580644
+user 764, cookie 583696
+user 766, cookie 586756
+user 768, cookie 589824
+user 770, cookie 592900
+user 772, cookie 595984
+user 774, cookie 599076
+user 776, cookie 602176
+user 778, cookie 605284
+user 780, cookie 608400
+user 782, cookie 611524
+user 784, cookie 614656
+user 786, cookie 617796
+user 788, cookie 620944
+user 790, cookie 624100
+user 792, cookie 627264
+user 794, cookie 630436
+user 796, cookie 633616
+user 798, cookie 636804
+user 800, cookie 640000
+user 802, cookie 643204
+user 804, cookie 646416
+user 806, cookie 649636
+user 808, cookie 652864
+user 810, cookie 656100
+user 812, cookie 659344
+user 814, cookie 662596
+user 816, cookie 665856
+user 818, cookie 669124
+user 820, cookie 672400
+user 822, cookie 675684
+user 824, cookie 678976
+user 826, cookie 682276
+user 828, cookie 685584
+user 830, cookie 688900
+user 832, cookie 692224
+user 834, cookie 695556
+user 836, cookie 698896
+user 838, cookie 702244
+user 840, cookie 705600
+user 842, cookie 708964
+user 844, cookie 712336
+user 846, cookie 715716
+user 848, cookie 719104
+user 850, cookie 722500
+user 852, cookie 725904
+user 854, cookie 729316
+user 856, cookie 732736
+user 858, cookie 736164
+user 860, cookie 739600
+user 862, cookie 743044
+user 864, cookie 746496
+user 866, cookie 749956
+user 868, cookie 753424
+user 870, cookie 756900
+user 872, cookie 760384
+user 874, cookie 763876
+user 876, cookie 767376
+user 878, cookie 770884
+user 880, cookie 774400
+user 882, cookie 777924
+user 884, cookie 781456
+user 886, cookie 784996
+user 888, cookie 788544
+user 890, cookie 792100
+user 892, cookie 795664
+user 894, cookie 799236
+user 896, cookie 802816
+user 898, cookie 806404
+user 900, cookie 810000
+user 902, cookie 813604
+user 904, cookie 817216
+user 906, cookie 820836
+user 908, cookie 824464
+user 910, cookie 828100
+user 912, cookie 831744
+user 914, cookie 835396
+user 916, cookie 839056
+user 918, cookie 842724
+user 920, cookie 846400
+user 922, cookie 850084
+user 924, cookie 853776
+user 926, cookie 857476
+user 928, cookie 861184
+user 930, cookie 864900
+user 932, cookie 868624
+user 934, cookie 872356
+user 936, cookie 876096
+user 938, cookie 879844
+user 940, cookie 883600
+user 942, cookie 887364
+user 944, cookie 891136
+user 946, cookie 894916
+user 948, cookie 898704
+user 950, cookie 902500
+user 952, cookie 906304
+user 954, cookie 910116
+user 956, cookie 913936
+user 958, cookie 917764
+user 960, cookie 921600
+user 962, cookie 925444
+user 964, cookie 929296
+user 966, cookie 933156
+user 968, cookie 937024
+user 970, cookie 940900
+user 972, cookie 944784
+user 974, cookie 948676
+user 976, cookie 952576
+user 978, cookie 956484
+user 980, cookie 960400
+user 982, cookie 964324
+user 984, cookie 968256
+user 986, cookie 972196
+user 988, cookie 976144
+user 990, cookie 980100
+user 992, cookie 984064
+user 994, cookie 988036
+user 996, cookie 992016
+user 998, cookie 996004
diff --git a/tests/test9.c b/tests/test9.c
new file mode 100644
index 000000000..93dd7db70
--- /dev/null
+++ b/tests/test9.c
@@ -0,0 +1,37 @@
+#include "uthash.h"
+#include <stdlib.h> /* malloc */
+#include <stdio.h> /* printf */
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+int main()
+{
+ int i;
+ example_user_t *user, *tmp, *users=NULL;
+
+ /* create elements */
+ for(i=0; i<1000; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ if (user == NULL) {
+ exit(-1);
+ }
+ user->id = i;
+ user->cookie = i*i;
+ HASH_ADD_INT(users,id,user);
+ }
+
+ /* delete each ID */
+ for(i=0; i<1000; i+=2) {
+ HASH_FIND_INT(users,&i,tmp);
+ if (tmp != NULL) {
+ printf("user %d, cookie %d\n", tmp->id, tmp->cookie);
+ } else {
+ printf("user id %d not found\n", i);
+ }
+ }
+ return 0;
+}
diff --git a/tests/test90.ans b/tests/test90.ans
new file mode 100644
index 000000000..e9d17d7db
--- /dev/null
+++ b/tests/test90.ans
@@ -0,0 +1,2 @@
+filling in is ok
+cleanup is ok
diff --git a/tests/test90.c b/tests/test90.c
new file mode 100644
index 000000000..6fce121ff
--- /dev/null
+++ b/tests/test90.c
@@ -0,0 +1,53 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+struct item {
+ unsigned char *sort_field;
+ size_t sort_field_len; /** Sort field length, in bytes */
+ int some_user_data;
+ UT_hash_handle hh;
+};
+
+int sort_func(const struct item *a, const struct item *b)
+{
+ int va = *(int*)(void*)a->sort_field;
+ int vb = *(int*)(void*)b->sort_field;
+ return (va < vb) ? -1 : (va > vb);
+}
+
+int main()
+{
+ size_t i;
+ struct item *p, *tmp;
+ int total = 0;
+
+ /** The sorted list */
+ struct item *list = NULL;
+ int counter = 0;
+
+ /* fill in the sorted list */
+ for(i=0; i<100; i++) {
+ p = (struct item *)malloc(sizeof *p);
+
+ p->sort_field_len = sizeof(int);
+ p->sort_field = (unsigned char *)malloc(p->sort_field_len);
+ *(int*)(void*)p->sort_field = counter++;
+
+ HASH_ADD_KEYPTR_INORDER(hh, list, p->sort_field, p->sort_field_len, p, sort_func);
+ }
+
+ printf("filling in is ok\n");
+
+ HASH_ITER(hh, list, p, tmp) {
+ total += *(int*)(void*)p->sort_field;
+ HASH_DEL(list, p);
+ free(p->sort_field);
+ free(p);
+ }
+ assert(total == 4950); // sum of 0 through 99
+
+ printf("cleanup is ok\n");
+ return 0;
+}
diff --git a/tests/test91.ans b/tests/test91.ans
new file mode 100644
index 000000000..6f21be083
--- /dev/null
+++ b/tests/test91.ans
@@ -0,0 +1,6 @@
+n g m f l e k d j c i b o h a
+DL_INSERT_INORDER
+n g m f l e k d j c i b o h a
+CDL_INSERT_INORDER
+n g m f l e k d j c i b o h a
+n a h o b i c j d k e l f m g
diff --git a/tests/test91.c b/tests/test91.c
new file mode 100644
index 000000000..8c8f63e48
--- /dev/null
+++ b/tests/test91.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id, score;
+ struct el *next, *prev;
+} el;
+
+static int order_desc(el *a, el *b)
+{
+ return (a->score > b->score) ? -1 : (a->score < b->score);
+}
+
+int main()
+{
+ int i;
+ el *head = NULL;
+ el els[15], *e;
+
+ for (i=0; i<15; i++) {
+ els[i].id = (int)'a'+i;
+ els[i].score = i%7;
+ LL_INSERT_INORDER(head, &els[i], order_desc);
+ }
+ LL_FOREACH(head, e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("DL_INSERT_INORDER\n");
+ head = NULL;
+ for (i=0; i<15; i++) {
+ DL_INSERT_INORDER(head, &els[i], order_desc);
+ }
+ DL_FOREACH(head, e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("CDL_INSERT_INORDER\n");
+ head = NULL;
+ for (i=0; i<15; i++) {
+ CDL_INSERT_INORDER(head, &els[i], order_desc);
+ }
+ CDL_FOREACH(head, e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_FOREACH2(head, e, prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ return 0;
+}
diff --git a/tests/test92.ans b/tests/test92.ans
new file mode 100644
index 000000000..528694d82
--- /dev/null
+++ b/tests/test92.ans
@@ -0,0 +1 @@
+End
diff --git a/tests/test92.c b/tests/test92.c
new file mode 100644
index 000000000..106819451
--- /dev/null
+++ b/tests/test92.c
@@ -0,0 +1,245 @@
+#include <stdio.h>
+
+#define HASH_NONFATAL_OOM 1
+
+#include "uthash.h"
+
+#undef uthash_malloc
+#undef uthash_free
+#undef uthash_nonfatal_oom
+#define uthash_malloc(sz) alt_malloc(sz)
+#define uthash_free(ptr,sz) alt_free(ptr)
+#define uthash_nonfatal_oom(e) do{(e)->mem_failed=1;}while(0)
+#define all_select(a) 1
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+ UT_hash_handle hh2;
+ int mem_failed;
+} example_user_t;
+
+static int malloc_cnt = 0;
+static int malloc_failed = 0;
+static int free_cnt = 0;
+
+static void *alt_malloc(size_t sz)
+{
+ if (--malloc_cnt <= 0) {
+ malloc_failed = 1;
+ return 0;
+ }
+ malloc_failed = 0;
+ return malloc(sz);
+}
+
+static void alt_free(void *ptr) {
+ free_cnt++;
+ free(ptr);
+}
+
+static void complain(int index, example_user_t *users, example_user_t *user)
+{
+ int expected_frees = (3 - index);
+ if (users) {
+ printf("%d: users hash must be empty\n", index);
+ }
+ if (user->hh.tbl) {
+ printf("%d hash table must be empty\n", index);
+ }
+ if (free_cnt != expected_frees) {
+ printf("%d Expected %d frees, only had %d\n", index, expected_frees, free_cnt);
+ }
+ if (user->mem_failed != 1) {
+ printf("%d Expected user->mem_failed(%d) to be 1\n", index, user->mem_failed);
+ }
+}
+
+int main()
+{
+ example_user_t *users = NULL;
+ example_user_t *user = (example_user_t*)malloc(sizeof(example_user_t));
+ example_user_t *test;
+ example_user_t *users2 = NULL;
+ int id = 0;
+ int i;
+ int saved_cnt;
+
+ user->id = id;
+
+#ifdef HASH_BLOOM
+ malloc_cnt = 3; // bloom filter must fail
+ user->mem_failed = 0;
+ user->hh.tbl = (UT_hash_table*)1;
+ free_cnt = 0;
+ HASH_ADD_INT(users, id, user);
+ complain(1, users, user);
+#endif /* HASH_BLOOM */
+
+ malloc_cnt = 2; // bucket creation must fail
+ user->mem_failed = 0;
+ free_cnt = 0;
+ user->hh.tbl = (UT_hash_table*)1;
+ HASH_ADD_INT(users, id, user);
+ complain(2, users, user);
+
+ malloc_cnt = 1; // table creation must fail
+ user->mem_failed = 0;
+ free_cnt = 0;
+ user->hh.tbl = (UT_hash_table*)1;
+ HASH_ADD_INT(users, id, user);
+ complain(3, users, user);
+
+ malloc_cnt = 4; // hash must create OK
+ user->mem_failed = 0;
+ HASH_ADD_INT(users, id, user);
+ if (user->mem_failed) {
+ printf("mem_failed must be 0, not %d\n", user->mem_failed);
+ }
+ HASH_FIND_INT(users,&id,test);
+ if (!test) {
+ printf("test user ID %d not found\n", id);
+ }
+
+ if (HASH_COUNT(users) != 1) {
+ printf("Got HASH_COUNT(users)=%d, should be 1\n", HASH_COUNT(users));
+ }
+
+ // let's add users until expansion fails.
+ malloc_failed = 0;
+ free_cnt = 0;
+ malloc_cnt = 1;
+ for (id = 1; 1; ++id) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ user->id = id;
+ if (id >= 1000) {
+ // prevent infinite, or too long of a loop here
+ puts("too many allocs before memory request");
+ break;
+ }
+ user->hh.tbl = (UT_hash_table*)1;
+ HASH_ADD_INT(users, id, user);
+ if (malloc_failed) {
+ if (id < 10) {
+ puts("there is no way your bucket size is <= 10");
+ }
+
+ if (user->hh.tbl) {
+ puts("user->hh.tbl should be NULL after failure");
+ } else if (user->mem_failed != 1) {
+ printf("mem_failed should be 1 after failure, not %d\n", user->mem_failed);
+ }
+
+ if (free_cnt != 0) {
+ printf("Expected 0 frees, had %d\n", free_cnt);
+ }
+
+ // let's make sure all previous IDs are there.
+ for (i=0; i<id; ++i) {
+ HASH_FIND_INT(users,&i,test);
+ if (test == NULL) {
+ printf("test user ID %d not found\n", i);
+ }
+ }
+
+ // let's try to add again, but with mem_failed set to 0
+ user->hh.tbl = NULL;
+ user->mem_failed = 0;
+ malloc_failed = 0;
+ HASH_ADD_INT(users, id, user);
+ if (!malloc_failed) {
+ puts("malloc should have been attempted");
+ }
+ if (user->hh.tbl) {
+ puts("user->hh.tbl should be NULL after second failure");
+ } else if (user->mem_failed != 1) {
+ printf("mem_failed should be 1 after second failure, not %d\n", user->mem_failed);
+ }
+
+ break;
+ }
+ }
+
+ // let's test HASH_SELECT.
+ // let's double the size of the table we've already built.
+ saved_cnt = id;
+
+ if (HASH_COUNT(users) != (unsigned)saved_cnt) {
+ printf("Got HASH_COUNT(users)=%d, should be %d\n", HASH_COUNT(users), saved_cnt);
+ }
+
+ for (i=0; i < saved_cnt; i++) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ user->id = ++id;
+ malloc_cnt = 20; // don't fail
+ HASH_ADD_INT(users, id, user);
+ }
+
+ HASH_ITER(hh, users, user, test) {
+ user->mem_failed = 0;
+ }
+
+// HASH_SELECT calls uthash_nonfatal_oom() with an argument of type (void*).
+#undef uthash_nonfatal_oom
+#define uthash_nonfatal_oom(e) do{((example_user_t*)e)->mem_failed=1;}while(0)
+
+ malloc_cnt = 0;
+ free_cnt = 0;
+ HASH_SELECT(hh2, users2, hh, users, all_select);
+ if (users2) {
+ puts("Nothing should have been copied into users2");
+ }
+ HASH_ITER(hh, users, user, test) {
+ if (user->hh2.tbl) {
+ printf("User ID %d has tbl at %p\n", user->id, (void*)user->hh2.tbl);
+ }
+ if (user->mem_failed != 1) {
+ printf("User ID %d has mem_failed(%d), should be 1\n", user->id, user->mem_failed);
+ }
+ user->mem_failed = 0;
+ }
+
+ malloc_cnt = 4;
+ HASH_SELECT(hh2, users2, hh, users, all_select);
+
+ // note about the above.
+ // we tried to stick up to 1,000 entries into users,
+ // and the malloc failed after saved_cnt. The bucket threshold must have
+ // been triggered. We then doubled the amount of entries in user,
+ // and just ran HASH_SELECT, trying to copy them into users2.
+ // because the order is different, and because we continue after
+ // failures, the bucket threshold may get triggered on arbitrary
+ // elements, depending on the hash function.
+
+ saved_cnt = 0;
+ HASH_ITER(hh, users, user, test) {
+ example_user_t * user2;
+ HASH_FIND(hh2, users2, &user->id, sizeof(int), user2);
+ if (user2) {
+ if (!user->hh2.tbl) {
+ printf("User ID %d has tbl==NULL\n", user->id);
+ }
+ if (user->mem_failed != 0) {
+ printf("User ID %d has mem_failed(%d), expected 0\n", user->id, user->mem_failed);
+ }
+ } else {
+ saved_cnt++;
+ if (user->hh2.tbl) {
+ printf("User ID %d has tbl at %p, expected 0\n", user->id, (void*)user->hh2.tbl);
+ }
+ if (user->mem_failed != 1) {
+ printf("User ID %d has mem_failed(%d), expected is 1\n", user->id, user->mem_failed);
+ }
+ }
+ }
+
+ if (saved_cnt + HASH_CNT(hh2, users2) != HASH_COUNT(users)) {
+ printf("Selected elements : %d + %d != %d\n",
+ saved_cnt, HASH_CNT(hh2, users2), HASH_COUNT(users));
+ }
+
+ puts("End");
+
+ return 0;
+}
diff --git a/tests/test93.ans b/tests/test93.ans
new file mode 100644
index 000000000..528694d82
--- /dev/null
+++ b/tests/test93.ans
@@ -0,0 +1 @@
+End
diff --git a/tests/test93.c b/tests/test93.c
new file mode 100644
index 000000000..4afe7d51e
--- /dev/null
+++ b/tests/test93.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <setjmp.h>
+
+#define HASH_BLOOM 16
+
+#include "uthash.h"
+
+#undef uthash_malloc
+#undef uthash_fatal
+#define uthash_malloc(sz) alt_malloc(sz)
+#define uthash_fatal(s) alt_fatal(s)
+
+typedef struct example_user_t {
+ int id;
+ int cookie;
+ UT_hash_handle hh;
+} example_user_t;
+
+static int malloc_cnt = 0;
+static int malloc_failed;
+static int is_fatal;
+static jmp_buf j_buf;
+static example_user_t * users;
+static int user_id = 0;
+
+static void *alt_malloc(size_t sz)
+{
+ if (--malloc_cnt <= 0) {
+ malloc_failed = 1;
+ return 0;
+ }
+ malloc_failed = 0;
+ return malloc(sz);
+}
+
+static void alt_fatal(char const * s) {
+ (void)s;
+ is_fatal = 1;
+ longjmp(j_buf, 1);
+}
+
+static example_user_t * init_user(int need_malloc_cnt) {
+ users = 0;
+ example_user_t * user = (example_user_t*)malloc(sizeof(example_user_t));
+ user->id = user_id;
+ is_fatal = 0;
+ malloc_cnt = need_malloc_cnt;
+ /* printf("adding to hash...\n"); */
+ if (!setjmp(j_buf)) {
+ HASH_ADD_INT(users, id, user);
+ }
+ return user;
+}
+
+int main()
+{
+
+#define init(a) do { \
+} while(0)
+
+ example_user_t * user;
+
+ user = init_user(3); /* bloom filter must fail */
+ if (!is_fatal) {
+ printf("fatal not called after bloom failure\n");
+ }
+
+ user = init_user(2); /* bucket creation must fail */
+ if (!is_fatal) {
+ printf("fatal not called after bucket creation failure\n");
+ }
+
+ user = init_user(1); /* table creation must fail */
+ if (!is_fatal) {
+ printf("fatal not called after table creation failure\n");
+ }
+
+ user = init_user(4); /* hash must create OK */
+ if (is_fatal) {
+ printf("fatal error when creating hash normally\n");
+ /* bad idea to continue running */
+ return 1;
+ }
+
+ /* let's add users until expansion fails */
+ users = 0;
+ malloc_cnt = 4;
+ while (1) {
+ user = (example_user_t*)malloc(sizeof(example_user_t));
+ user->id = user_id;
+ if (user_id++ == 1000) {
+ printf("there is no way 1000 iterations didn't require realloc\n");
+ break;
+ }
+ if (!setjmp(j_buf)) {
+ HASH_ADD_INT(users, id, user);
+ }
+ malloc_cnt = 0;
+ if (malloc_failed) {
+
+ if (!is_fatal) {
+ printf("fatal not called after bucket not extended\n");
+ }
+ if (user_id < 10) {
+ printf("there is no way your bucket size is 10\n");
+ }
+
+ /* we can't really do anything, the hash is not in consistent
+ * state, so assume this is a success. */
+ break;
+
+ }
+ }
+
+ printf("End\n");
+
+ return 0;
+
+}
diff --git a/tests/test94.ans b/tests/test94.ans
new file mode 100644
index 000000000..9ce194240
--- /dev/null
+++ b/tests/test94.ans
@@ -0,0 +1,13 @@
+LL_INSERT_INORDER
+list1: n g m f l e k d j c i b o h a
+list2: o h a i b j c k d l e m f n g
+DL_INSERT_INORDER
+list1: n g m f l e k d j c i b o h a
+list2: o h a i b j c k d l e m f n g
+CDL_INSERT_INORDER
+list1:
+n g m f l e k d j c i b o h a
+n a h o b i c j d k e l f m g
+list2:
+o h a i b j c k d l e m f n g
+o g n f m e l d k c j b i a h
diff --git a/tests/test94.c b/tests/test94.c
new file mode 100644
index 000000000..b51acc51b
--- /dev/null
+++ b/tests/test94.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include "utlist.h"
+
+typedef struct el {
+ int id, score;
+ struct el *next, *prev;
+ struct el *next_list2, *prev_list2;
+} el;
+
+static int order_desc(el *a, el *b)
+{
+ return (a->score > b->score) ? -1 : (a->score < b->score);
+}
+
+static int order_asc(el *a, el *b)
+{
+ return -order_desc(a, b);
+}
+
+int main()
+{
+ int i;
+ el *head = NULL;
+ el *head2 = NULL;
+ el els[15], *e;
+
+ for (i=0; i<15; i++) {
+ els[i].id = (int)'a'+i;
+ els[i].score = i%7;
+ LL_INSERT_INORDER(head, &els[i], order_desc);
+ LL_INSERT_INORDER2(head2, &els[i], order_asc, next_list2);
+ }
+
+ printf("LL_INSERT_INORDER\n");
+ printf("list1: ");
+ LL_FOREACH(head, e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("list2: ");
+ LL_FOREACH2(head2, e, next_list2) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("DL_INSERT_INORDER\n");
+ head = NULL;
+ head2 = NULL;
+ for (i=0; i<15; i++) {
+ DL_INSERT_INORDER(head, &els[i], order_desc);
+ DL_INSERT_INORDER2(head2, &els[i], order_asc, prev_list2, next_list2);
+ }
+
+ printf("list1: ");
+ DL_FOREACH(head, e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ printf("list2: ");
+ DL_FOREACH2(head2, e, next_list2) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("CDL_INSERT_INORDER\n");
+ head = NULL;
+ head2 = NULL;
+ for (i=0; i<15; i++) {
+ CDL_INSERT_INORDER(head, &els[i], order_desc);
+ CDL_INSERT_INORDER2(head2, &els[i], order_asc, prev_list2, next_list2);
+ }
+ printf("list1:\n");
+ CDL_FOREACH(head, e) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_FOREACH2(head, e, prev) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ printf("list2:\n");
+ CDL_FOREACH2(head2, e, next_list2) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+ CDL_FOREACH2(head2, e, prev_list2) {
+ printf("%c ", e->id);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/tests/test95.ans b/tests/test95.ans
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/test95.ans
diff --git a/tests/test95.c b/tests/test95.c
new file mode 100644
index 000000000..7e715b582
--- /dev/null
+++ b/tests/test95.c
@@ -0,0 +1,66 @@
+#include <assert.h>
+#include <stddef.h> /* size_t, NULL */
+#include "utstack.h"
+
+typedef struct el {
+ int id, score;
+ struct el *next;
+ struct el *next2;
+} el;
+
+int main()
+{
+ el alpha = {1, 100, NULL, NULL};
+ el beta = {2, 100, NULL, NULL};
+ el gamma = {3, 100, NULL, NULL};
+ el delta = {4, 100, NULL, NULL};
+
+ el *stack1 = NULL;
+ el *stack2 = NULL;
+ el *dummy;
+ int size1;
+ size_t size2;
+
+ STACK_COUNT(stack1, dummy, size1); assert(size1 == 0);
+ STACK_COUNT(stack2, dummy, size2); assert(size2 == 0);
+ assert(STACK_EMPTY(stack1));
+ assert(STACK_EMPTY(stack2));
+
+ STACK_PUSH(stack1, &alpha);
+ STACK_COUNT(stack1, dummy, size1); assert(size1 == 1);
+ STACK_PUSH(stack1, &beta);
+ STACK_COUNT(stack1, dummy, size1); assert(size1 == 2);
+ STACK_PUSH(stack1, &gamma);
+ STACK_PUSH2(stack1, &delta, next);
+ STACK_COUNT(stack1, dummy, size1); assert(size1 == 4);
+ assert(stack1 == &delta);
+
+ STACK_PUSH2(stack2, &alpha, next2);
+ assert(stack2 == &alpha);
+ assert(alpha.next2 == NULL);
+ STACK_PUSH2(stack2, &delta, next2);
+ assert(stack2 == &delta);
+ assert(delta.next2 == &alpha);
+ STACK_COUNT2(stack2, dummy, size2, next2); assert(size2 == 2);
+ assert(!STACK_EMPTY(stack2));
+ assert(stack2 == &delta);
+
+ assert(!STACK_EMPTY(stack1));
+ assert(!STACK_EMPTY(stack2));
+
+ STACK_POP(stack1, dummy); assert(stack1 == &gamma); assert(dummy == &delta);
+ STACK_POP(stack1, dummy); assert(stack1 == &beta); assert(dummy == &gamma);
+ STACK_POP(stack1, dummy); assert(stack1 == &alpha); assert(dummy == &beta);
+ STACK_COUNT(stack1, dummy, size1); assert(size1 == 1);
+ STACK_POP(stack1, dummy); assert(stack1 == NULL); assert(dummy == &alpha);
+
+ assert(STACK_TOP(stack2) == &delta);
+ while (!STACK_EMPTY(stack2)) {
+ STACK_POP2(stack2, dummy, next2);
+ }
+
+ assert(STACK_EMPTY(stack1));
+ assert(STACK_EMPTY(stack2));
+
+ return 0;
+}
diff --git a/tests/threads/Makefile b/tests/threads/Makefile
new file mode 100644
index 000000000..01060060a
--- /dev/null
+++ b/tests/threads/Makefile
@@ -0,0 +1,31 @@
+HASHDIR = ../../src
+PROGS = test1 test2
+
+# Thread support requires compiler-specific options
+# ----------------------------------------------------------------------------
+# GNU
+CFLAGS += -I$(HASHDIR) -g -pthread
+# Solaris (Studio 11)
+#CFLAGS = -I$(HASHDIR) -g -mt
+# ----------------------------------------------------------------------------
+
+ifeq ($(HASH_DEBUG),1)
+CFLAGS += -DHASH_DEBUG=1
+endif
+
+all: $(PROGS) run_tests
+
+$(PROGS) : $(HASHDIR)/uthash.h
+ $(CC) $(CPPLFAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
+
+debug:
+ $(MAKE) all HASH_DEBUG=1
+
+run_tests: $(PROGS)
+ perl ../do_tests
+
+.PHONY: clean
+
+clean:
+ rm -f $(PROGS) test*.out
+ rm -rf test*.dSYM
diff --git a/tests/threads/README b/tests/threads/README
new file mode 100644
index 000000000..e49a62eed
--- /dev/null
+++ b/tests/threads/README
@@ -0,0 +1,2 @@
+test1: exercise a two-reader, one-writer, rwlock-protected hash.
+test2: a template for a nthread, nloop kind of program
diff --git a/tests/threads/do_tests b/tests/threads/do_tests
new file mode 100755
index 000000000..e6809a157
--- /dev/null
+++ b/tests/threads/do_tests
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "test*[0-9]") {
+ push @tests, $_ if -e "$_.ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out 2> $test.err`;
+ `diff $test.out $test.ans`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+ unlink "$test.err" if -z "$test.err";
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
+exit $num_failed;
diff --git a/tests/threads/test1.c b/tests/threads/test1.c
new file mode 100644
index 000000000..8d6cb6747
--- /dev/null
+++ b/tests/threads/test1.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include "uthash.h"
+
+#undef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) fprintf(stderr,"warning: bucket expansion inhibited\n")
+
+#define LOOPS 100000
+
+typedef struct {
+ int i;
+ UT_hash_handle hh;
+} elt;
+
+elt *elts=NULL; /* this is our hash table which two threads will use */
+pthread_rwlock_t lock;
+
+void *thread_routine_r( void *arg ) {
+ int i;
+ long num_found=0;
+ elt *e;
+
+ for(i=0;i<LOOPS;i++) {
+ if (pthread_rwlock_rdlock(&lock) != 0) {
+ fprintf(stderr,"can't acquire read lock\n");
+ exit(-1);
+ }
+ HASH_FIND_INT(elts, &i, e);
+ if (e) num_found++;
+ pthread_rwlock_unlock(&lock);
+ }
+ return (void*)num_found;
+}
+void *thread_routine_w( void *arg ) {
+ int i;
+ long num_deld=0;
+ elt *e;
+
+ for(i=0;i<LOOPS;i++) {
+ if (pthread_rwlock_wrlock(&lock) != 0) {
+ fprintf(stderr,"can't acquire write lock\n");
+ exit(-1);
+ }
+ HASH_FIND_INT(elts, &i, e);
+ if (e) {
+ /* HASH_DEL(elts, e); */
+ /* num_deld++; */
+ } else {
+ e = malloc(sizeof(elt));
+ if (!e) exit(-1);
+ e->i = i;
+ HASH_ADD_INT(elts, i, e);
+ }
+ pthread_rwlock_unlock(&lock);
+ }
+ return (void*)num_deld;
+}
+
+int main() {
+ unsigned i;
+ long num_added=0;
+ int status;
+ pthread_t thread_r1,thread_r2,thread_w1,thread_w2;
+ void *thread_result;
+ elt tmp, *e;
+
+ if (pthread_rwlock_init(&lock,NULL) != 0) {
+ fprintf(stderr,"lock init failed\n");
+ exit(-1);
+ }
+
+ if (( status = pthread_create( &thread_r1, NULL, thread_routine_r, NULL) )) {
+ printf("failure: status %d\n", status);
+ exit(-1);
+ }
+ if (( status = pthread_create( &thread_r2, NULL, thread_routine_r, NULL) )) {
+ printf("failure: status %d\n", status);
+ exit(-1);
+ }
+ if (( status = pthread_create( &thread_w1, NULL, thread_routine_w, NULL) )) {
+ printf("failure: status %d\n", status);
+ exit(-1);
+ }
+ if (( status = pthread_create( &thread_w2, NULL, thread_routine_w, NULL) )) {
+ printf("failure: status %d\n", status);
+ exit(-1);
+ }
+
+ status = pthread_join( thread_r1, &thread_result );
+ printf("thread result: %d %ld\n", status, (long)thread_result);
+
+ status = pthread_join( thread_r2, &thread_result );
+ printf("thread result: %d %ld\n", status, (long)thread_result);
+
+ status = pthread_join( thread_w1, &thread_result );
+ printf("thread result: %d %ld\n", status, (long)thread_result);
+
+ status = pthread_join( thread_w2, &thread_result );
+ printf("thread result: %d %ld\n", status, (long)thread_result);
+
+ i = HASH_COUNT(elts);
+ printf("final count of items in hash: %u\n", i);
+
+ if (pthread_rwlock_destroy(&lock) != 0) {
+ fprintf(stderr,"lock destroy failed\n");
+ exit(-1);
+ }
+}
diff --git a/tests/threads/test2.c b/tests/threads/test2.c
new file mode 100644
index 000000000..316b7e699
--- /dev/null
+++ b/tests/threads/test2.c
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include "uthash.h"
+
+#undef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) fprintf(stderr,"warning: bucket expansion inhibited\n");
+
+#define LOOPS 100000
+#define NTHREADS 2
+
+typedef struct {
+ int i;
+ int v;
+ UT_hash_handle hh;
+} elt;
+
+elt *elts=NULL; /* this is our hash table which two threads will use */
+pthread_rwlock_t lock;
+
+void *thread_routine( void *arg ) {
+ int keepgoing=0;
+ /* TODO write me */
+ return NULL;
+}
+
+int main() {
+ unsigned i;
+ long num_added=0;
+ int status;
+ pthread_t thread[NTHREADS];
+ void *thread_result;
+ elt tmp, *e;
+
+ if (pthread_rwlock_init(&lock,NULL) != 0) {
+ fprintf(stderr,"lock init failed\n");
+ exit(-1);
+ }
+
+ /* populate it to start */
+ for(i=0; i<LOOPS; i++) {
+ e = malloc(sizeof(elt));
+ if (!e) exit(-1);
+ e->i = i;
+ e->v = 0;
+ HASH_ADD_INT(elts, i, e);
+ }
+
+ for(i=0; i<NTHREADS; i++) {
+ if (( status = pthread_create( &thread[i], NULL, thread_routine, NULL) )) {
+ printf("failure: status %d\n", status);
+ exit(-1);
+ }
+ }
+
+ for(i=0; i<NTHREADS; i++) {
+ status = pthread_join( thread[i], &thread_result );
+ printf("thread result: %d %ld\n", status, (long)thread_result);
+ }
+
+ i = HASH_COUNT(elts);
+ printf("final count of items in hash: %u\n", i);
+
+ if (pthread_rwlock_destroy(&lock) != 0) {
+ fprintf(stderr,"lock destroy failed\n");
+ exit(-1);
+ }
+}