commit 4e09f4e980d6f9936719aa5f9579a878a7b412f1 Author: Михаил Капелько Date: Tue Mar 19 22:26:27 2024 +0300 d diff --git a/c/Memory/src/Memory.Aux.c b/c/Memory/src/Memory.Aux.c new file mode 100644 index 0000000..1857741 --- /dev/null +++ b/c/Memory/src/Memory.Aux.c @@ -0,0 +1,3 @@ + +UT_hash + diff --git a/c/ext/uthash/.gitignore b/c/ext/uthash/.gitignore new file mode 100644 index 0000000..1f152b2 --- /dev/null +++ b/c/ext/uthash/.gitignore @@ -0,0 +1,3 @@ +*~ +*.out +keystat.* diff --git a/c/ext/uthash/.travis.yml b/c/ext/uthash/.travis.yml new file mode 100644 index 0000000..6264694 --- /dev/null +++ b/c/ext/uthash/.travis.yml @@ -0,0 +1,14 @@ +language: cpp +matrix: + include: + - os: linux + compiler: gcc + - os: linux + compiler: clang + - os: osx +script: +- make -C tests EXTRA_CFLAGS="-W -Wall -Wextra -Wswitch-default" +- make -C tests clean ; make -C tests pedantic +- make -C tests clean ; make -C tests pedantic EXTRA_CFLAGS=-DNO_DECLTYPE +- make -C tests clean ; make -C tests cplusplus +- make -C tests clean ; make -C tests cplusplus EXTRA_CFLAGS=-DNO_DECLTYPE diff --git a/c/ext/uthash/LICENSE b/c/ext/uthash/LICENSE new file mode 100644 index 0000000..e75a243 --- /dev/null +++ b/c/ext/uthash/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2021, 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. + diff --git a/c/ext/uthash/README.md b/c/ext/uthash/README.md new file mode 100644 index 0000000..a85c10d --- /dev/null +++ b/c/ext/uthash/README.md @@ -0,0 +1,8 @@ + +[![Build status](https://api.travis-ci.org/troydhanson/uthash.svg?branch=master)](https://travis-ci.org/troydhanson/uthash) + +Documentation for uthash is available at: + +https://troydhanson.github.com/uthash/ + + diff --git a/c/ext/uthash/doc/.gitignore b/c/ext/uthash/doc/.gitignore new file mode 100644 index 0000000..9a2594e --- /dev/null +++ b/c/ext/uthash/doc/.gitignore @@ -0,0 +1,7 @@ +ChangeLog.html +userguide.html +utarray.html +utlist.html +utringbuffer.html +utstack.html +utstring.html diff --git a/c/ext/uthash/doc/ChangeLog.txt b/c/ext/uthash/doc/ChangeLog.txt new file mode 100644 index 0000000..053a75d --- /dev/null +++ b/c/ext/uthash/doc/ChangeLog.txt @@ -0,0 +1,282 @@ +uthash ChangeLog +================ + +Click to return to the link:index.html[uthash home page]. + +NOTE: This ChangeLog may be incomplete and/or incorrect. See the git commit log. + +Version 2.3.0 (2021-02-25) +-------------------------- +* remove HASH_FCN; the HASH_FUNCTION and HASH_KEYCMP macros now behave similarly +* remove uthash_memcmp (deprecated in v2.1.0) in favor of HASH_KEYCMP +* silence -Wswitch-default warnings (thanks, Olaf Bergmann!) + +Version 2.2.0 (2020-12-17) +-------------------------- +* add HASH_NO_STDINT for platforms without C99 +* silence many -Wcast-qual warnings (thanks, Olaf Bergmann!) +* skip hash computation when finding in an empty hash (thanks, Huansong Fu!) +* rename oom to utarray_oom, in utarray.h (thanks, Hong Xu!) +* rename oom to utstring_oom, in utstring.h (thanks, Hong Xu!) +* remove MurmurHash/HASH_MUR + +Version 2.1.0 (2018-12-20) +-------------------------- +* silence some Clang static analysis warnings +* add LL_INSERT_INORDER and LL_LOWER_BOUND etc (thanks, Jeffrey Lovitz and Mattias Eriksson!) +* add uthash_bzero for platforms without +* fix a missing HASH_BLOOM_ADD in HASH_SELECT (thanks, Pawel Veselov!) +* permit malloc failure to be recoverable via HASH_NONFATAL_OOM (thanks, Pawel Veselov!) +* avoid repeated calls to uthash_strlen in HASH_FIND_STR +* rename uthash_memcmp to HASH_KEYCMP, leaving the old name for compatibility +* add utstack.h +* remove libut + +Version 2.0.2 (2017-03-02) +-------------------------- +* fix segfault in HASH_ADD_INORDER etc (thanks, Yana Kireyonok!) +* remove spurious cast to unsigned in utstring_len (thanks, Michal Sestrienka!) +* add uthash_memcmp and uthash_strlen for platforms without (thanks, Pawel Veselov!) +* fix a C++ incompatibility in utringbuffer + +Version 2.0.1 (2016-07-05) +-------------------------- +* in-order insertion macros HASH_ADD_INORDER etc (thanks, Thilo Schulz!) +* by-hashvalue insertion macros HASH_ADD_BYHASHVALUE etc +* during key comparison, check hashvalue before doing a full memcmp +* add utringbuffer.h + +Version 1.9.9.1 (2014-11-18) +---------------------------- +* inclusion of experimental libut bundle with utvector in opt/ +* use shift in Bernstein hash instead of multiply (thanks, Jimmy Zhuo!) +* switch ssize_t types in utarray/utstring to size_t (thanks, Hong Xu!) +* fix utstring pointer math on >4GB strings (thanks, Thomas Bottesch!) +* change FNV hash to FNV-1a varation (thanks, dwest1975!) +* fix bug in HASH_REPLACE_STR (thanks, Ilya Kaliman!) + +Version 1.9.9 (2014-02-25) +-------------------------- +* made HASH_ADD_STR compatible with char* or char[] (thanks, Samuel Thibault!) +* fixed header inclusion of sys/types.h for ssize_t (thanks, Fernando Campos!) +* added LL_COUNT/DL_COUNT/CDL_COUNT (thansk, Paul Praet!) +* added LRU cache example in `tests/lru_cache` (thanks, Oliver Lorenz!) +* fix LL_DELETE2 for VS2008 (thanks, Greg Davydouski!) +* fix missing argument in `HASH_REPLACE_STR` (thanks, Alex!) +* bump version number in source files to match docs (thanks, John Crow!) +* add `HASH_OVERHEAD` macro to get overhead size for hash table + +Version 1.9.8 (2013-03-10) +-------------------------- +* `HASH_REPLACE` now in uthash (thanks, Nick Vatamaniuc!) +* fixed clang warnings (thanks wynnw!) +* fixed `utarray_insert` when inserting past array end (thanks Rob Willett!) +* you can now find http://troydhanson.github.com/uthash/[uthash on GitHub] +* there's a https://groups.google.com/d/forum/uthash[uthash Google Group] +* uthash has been downloaded 29,000+ times since 2006 on SourceForge + +Version 1.9.7 (2012-10-09) +-------------------------- +* utstring now supports substring search using `utstring_find` (thanks, Joe Wei!) +* utlist now supports element 'prepend' and 'replace' (thanks, Zoltán Lajos Kis!) +* utlist element prev/next fields can now have any names (thanks, Pawel S. Veselov!) +* uthash cast quiets a clang warning (thanks, Roman Divacky and Baptiste Daroussin!) +* uthash userguide example shows how to check key uniqueness (thanks, Richard Cook!) +* uthash HASH_MUR compiles under MSVC++ 10 in C mode (thanks, Arun Kirthi Cherian!) +* `utstring_printf` now supports format checking (thanks, Donald Carr!) + +Version 1.9.6 (2012-04-28) +-------------------------- +* add utarray_prev (thanks, Ben Hiett!) +* add parens/casts for greater compatibility (thanks, Atis, Debasis Ganguly, and Steve McClellan!) +* added ifndef to uthash_malloc and related hooks (thanks, Holger Machens!) +* edit examples so they do not leak memory (thanks, 任晶磊!) + +Version 1.9.5 (2011-11-16) +-------------------------- +* added `utarray_renew` +* fixed memory leak in `uthash_clear` when using Bloom filter (thanks, Jan Hättig!) +* utarray now copies the UT_icd on array creation rather than storing a pointer +* add parentheses to `HASH_ADD` to fix preprocessing of certain arguments (thanks, Aaron Rosen!) +* more parenthesizations for greater macro argument flexibility + +Version 1.9.4 (2011-06-05) +-------------------------- +* uthash now supports MurmurHash v3 +* utlist now includes concatenation macros (`LL_CONCAT` and `DL_CONCAT`) +* utarray now supports binary search (`utarray_find`) +* utstring now supports a new-or-clear-existing macro (`utstring_renew`) +* documented technique for a multi-level hash table +* clarified scope requirements for `UT_icd` in the utarray documentation +* fixed termination when `utstring_clear` is followed by `utstring_body` +* fixed utarray_inserta macro when used with complex arguments +* on Visual Studio define missing type `uint8_t` +* Debian/Ubuntu include uthash in the package `uthash-dev`. +* uthash has been downloaded 16,211 times. + +Thanks to Yu Feng, Richard Cook, Dino Ciuffetti, Chris Groer, and Arun Cherian +for feedback and fixes in this release! + +Version 1.9.3 (2010-10-31) +-------------------------- +* fix an `ifdef` for compatibility with Intel compiler (thanks, degski!) +* fix `HASH_ITER` macro to satisfy C++ casting rules (thanks, Erik Bai!) + +Version 1.9.2 (2010-10-04) +-------------------------- +* new `HASH_ITER` macro for more convenient deletion-safe iteration +* `hashscan` can now run on FreeBSD 8.1 and later (thanks, Markus Gebert!) +* More parens to evaluate complex macro arguments properly (thanks, ngg!) +* Add sz parameter to the `uthash_free` hook for platforms that do their own memory management. Hopefully this minor API change doesn't cause too much breakage for people. (thanks, Niall Douglas!) +* uthash has been downloaded 12,294 times + +Version 1.9.1 (2010-05-15) +-------------------------- +* Fix a redefinition warning when using `uthash.h` and `utstring.h` together +* Fix a bug in `utstring_init` +* Added `HASH_FIND_PTR` and `HASH_ADD_PTR` (thanks, Niall Douglas!) + +Version 1.9 (2010-03-31) +-------------------------- +* uthash now supports Visual Studio 2008 and 2010 in C or C++ code! +* new headers link:utarray.html[utarray.h] and link:utstring.html[utstring.h] + are now included. These implement dynamic arrays and strings using macros +* link:utlist.html[utlist.h] now has deletion-safe iterators and search macros +* the test suite now runs under Visual Studio (thanks again degski!) +* special thanks for suggesting utarray and utlist features to Charalampos P.! +* uthash has been downloaded 9,616 times + +Version 1.8 (2009-09-08) +-------------------------- +* Added the `hashscan` utility that can report on the size and quality of + hash tables in a running process (Linux-only) +* Added Bloom filter support. This has the potential to speed up certain + types of programs that look up non-existant keys in sufficient numbers. +* Restored the MurmurHash, which can once again be used, if an additional + symbol is defined. This is a "safety" by which the user declares they + understand that `-fno-strict-aliasing` flag must be used if they are + using MurmurHash under gcc with optimization. +* Unified the bucket/table malloc hooks; now there is only one malloc hook +* Re-organized the manual into a main section and advanced topics section +* Fixed a bug in `utlist.h` where sorting a singly-linked list threw a + compile-time error. +* Fixed a bug in `utlist.h` where a doubly-linked list that is sorted + did not maintain the special `head->prev` pointer back to the list tail. + +Version 1.7 (2009-06-11) +-------------------------- +* The MurmurHash has been removed, and Jenkin's hash is once again the default. + While MurmurHash performed well, it's unsafe with regard to the strict + aliasing rule. This results in incorrect code when compiled with optimization. + It's not possible to enable `-fno-strict-aliasing` from within a header file. +* The linked list macros in `utlist.h` now comply with the strict-aliasing + rule so they generate correct code under high optimization levels (O2 or O3). + The use of the `__typeof__` extension, which was originally a GNU extension, + may reduce portability to other compilers that do not support this extension. + This extension is used in the singly-linked list macros and the sort macros. + +Version 1.6 (2009-05-08) +-------------------------- +Special thanks to Alfred Heisner for contributing several enhancements: + +* Support for two new hash functions: + - the Paul Hsieh hash function (`HASH_SFH`) + - Austin Appleby's MurmurHash function (`HASH_MUR`) +* Because of its excellent performance, MurmurHash is now the default hash function. +* `keystats` now has much better elapsed time accuracy under Cygwin and MinGW +* fixed casting in `HASH_FNV`, `HASH_SAX` and `HASH_OAT` for non-char keys + +This release also includes: + +* a new `HASH_CLEAR` operation clears a hash table in one step. +* a new `HASH_SELECT` operation inserts those elements from one hash that + satisfy a given condition into another hash. The selected items have + dual presence in both hash tables. For example a game could select the + visible polygons from a hash of all polygons. +* fixed a compile-time error which occurred if the final argument to + `HASH_ADD_KEYPTR` was a pointer to an array member like `&a[i]` +* added another test script `tests/all_funcs` which executes the test suite + using every supported hash function + +And lastly, + +* a new, separate header called link:utlist.html[utlist.h] is included which + provides 'linked list macros' for C structures, similar in style to the + uthash macros + +Version 1.5 (2009-02-19) +-------------------------- +* now thread-safe for concurrent readers +* use scratch variables on stack rather than in table (thanks, Petter Arvidsson!). + This change made HASH_FIND about 13% faster and enabled reader concurrency. +* made link:license.html[BSD license] terms even more permissive +* added link:userguide.pdf[PDF version] of User Guide +* added http://troydhanson.wordpress.com/feed/[update news] image:rss.png[(RSS)] + +Version 1.4 (2008-09-23) +-------------------------- +* Add `HASH_COUNT` for counting items in the hash +* Compatibility with C\+\+. Satisfy additional casting requirements. + Also in the `tests/` directory, running `make cplusplus` now compiles + all the test programs with the C++ compiler. +* Eliminate `elmt` pointer from the UT_hash_handle. Calculate elmt + from hash handle address by subtracting `hho` (hash handle offset). +* Contributed by L.S.Chin: + Cast `void*` to char* before pointer arithmetic to suppress compiler + warnings. We assume compilers abide to C standards which impose + requirement that `sizeof(void*) == sizeof(char*)`. +* Return meaningful exit status from do_tests per Tiago Cunha, + so that package manager-based install can verify tests are successful + + +Version 1.3 (2008-07-27) +------------------------ +* use integer-only math-- no floating point! Support FPU-less CPU's. +* eliminate `hash_q` metric, which measured the fraction of items with + non-ideal chain positions. We only need to know if this fraction + is below 0.5. This is now determined using fast bitwise tests. +* when an item is added to the hash, calculate the key's hash value + upfront and store it, instead of recomputing it as needed. This hashv + is stored in the hash handle. Potentially major speed benefit for + bucket expansion algorithm. Deleting is marginally improved too. +* fixed a minor bug in the calculation of the max ideal chain length; + line 446 in v1.2 erroneously calculated a/b*2 instead of a/(b*2). + The effect of this bug was that bucket expansion could occur more + readily because the per-bucket 'max chain length multiplier factor' + (which delays bucket expansion when certain buckets are overused) + was set to a lower, expansion-favoring value than intended. +* improved source commenting and improved variable names in structures +* remove `HASH_JSW`. Lengthy random number array made code less readable +* add `HASH_SRT(hh,hash,cmp)` as a generalized `HASH_SORT(hash,cmp)`. + It was an omission in uthash 1.2 that there was no sort macro for + hash handles with names other than hh. +* Corrected `HASH_FSCK` so it works with any name for the hash handle. +* behave properly in pathological `HASH_DEL(a,a)` case where the same + variable references the head and the deletee (advancing the head + then loses the correct reference to the deletee); fix by using + scratch area in the hash table to store deletee hash handle. +* made tests runnable on MinGW +* 3000+ downloads since uthash-1.0 + + +Version 1.2 (2006-11-22) +------------------------ +* new `HASH_SORT` macro +* Cygwin support +* User Guide now features a clickable Table of Contents. + (The technique for generating the TOC on the browser was contributed + back to the AsciiDoc project and incorporated into AsciiDoc v8.1.0). + + +Version 1.1 (2006-06-28) +------------------------ +* uthash-1.1 released +* supports several built-in user-selectable hash functions +* new keystats utility quantifies performance of hash functions + + +Version 1.0 (2006-06-02) +------------------------ +* Initial release + +// vim: set syntax=asciidoc: diff --git a/c/ext/uthash/doc/Makefile b/c/ext/uthash/doc/Makefile new file mode 100644 index 0000000..099a373 --- /dev/null +++ b/c/ext/uthash/doc/Makefile @@ -0,0 +1,18 @@ +HTML=$(patsubst %.txt,%.html,$(wildcard *.txt)) + +all: $(HTML) + +# when each target of a multi-target rule has its own prereq +# we use a static pattern rule. +$(HTML): %.html: %.txt + asciidoc -a toc2 $< + +TMP=/tmp/uthash-gh-pages +stage: + mkdir -p ${TMP} + rm -if ${TMP}/* + cp *.html *.css *.png ${TMP} + +.PHONY: clean +clean: + $(RM) $(HTML) diff --git a/c/ext/uthash/doc/banner.png b/c/ext/uthash/doc/banner.png new file mode 100644 index 0000000..de4f310 Binary files /dev/null and b/c/ext/uthash/doc/banner.png differ diff --git a/c/ext/uthash/doc/banner.svg b/c/ext/uthash/doc/banner.svg new file mode 100644 index 0000000..f3143f1 --- /dev/null +++ b/c/ext/uthash/doc/banner.svg @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ut hash + + + + + + + + + + + + + + + + + + + + + + + a hash tablefor C structures + + + diff --git a/c/ext/uthash/doc/google315d692c9c632ed0.html b/c/ext/uthash/doc/google315d692c9c632ed0.html new file mode 100644 index 0000000..c79038e --- /dev/null +++ b/c/ext/uthash/doc/google315d692c9c632ed0.html @@ -0,0 +1 @@ +google-site-verification: google315d692c9c632ed0.html \ No newline at end of file diff --git a/c/ext/uthash/doc/index.html b/c/ext/uthash/doc/index.html new file mode 100644 index 0000000..48d729a --- /dev/null +++ b/c/ext/uthash/doc/index.html @@ -0,0 +1,122 @@ + + + + + uthash: a hash table for C structures + + + + + +
+ GitHub page > + uthash home + + + +
+ +
+
+ + + +
+ Any C structure can be stored in a hash table using uthash. Just add a + UT_hash_handle to the structure and choose one or more fields + in your structure to act as the key. Then use these macros to store, + retrieve or delete items from the hash table. + +
+Example 1. Adding an item to a hash. +
+
+#include "uthash.h"
+
+struct my_struct {
+    int id;            /* we'll use this field as the key */
+    char name[10];
+    UT_hash_handle hh; /* makes this structure hashable */
+};
+
+struct my_struct *users = NULL;
+
+void add_user(struct my_struct *s) {
+    HASH_ADD_INT(users, id, s);
+}
+
+
+
+
+ +
+Example 2. Looking up an item in a hash. +
+
+struct my_struct *find_user(int user_id) {
+    struct my_struct *s;
+
+    HASH_FIND_INT(users, &user_id, s);
+    return s;
+}
+
+
+
+
+ +
+Example 3. Deleting an item from a hash. +
+ +
+void delete_user(struct my_struct *user) {
+    HASH_DEL(users, user);
+}
+
+
+
+
+ + For more information and examples, please see the User Guide. + +
+
+ +
+ + + + + + diff --git a/c/ext/uthash/doc/license.html b/c/ext/uthash/doc/license.html new file mode 100644 index 0000000..b68cb4d --- /dev/null +++ b/c/ext/uthash/doc/license.html @@ -0,0 +1,55 @@ + + + + + uthash: a hash table for C structures + + + + + +
+ uthash home > + BSD license +
+ +
+
+
+
+Copyright (c) 2005-2021, 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.
+
+
+
+ +
+ + + + + + diff --git a/c/ext/uthash/doc/rss.png b/c/ext/uthash/doc/rss.png new file mode 100644 index 0000000..b3c949d Binary files /dev/null and b/c/ext/uthash/doc/rss.png differ diff --git a/c/ext/uthash/doc/styles.css b/c/ext/uthash/doc/styles.css new file mode 100644 index 0000000..0611722 --- /dev/null +++ b/c/ext/uthash/doc/styles.css @@ -0,0 +1,141 @@ +#banner { + /* font-size: x-large; */ + /* background: #ff00ff; */ + /* height: 100px; */ +} + +#topnav { + /* background-image: url(img/grad_topnav.png); */ + /* background-repeat: repeat-y; */ + /* background-color: #af00af; */ + /* height: 25px; */ + margin-top: 5px; + margin-bottom: 10px; + padding: 3px; + font-size: 9pt; + font-family: sans-serif; + /* border-style: solid; */ + /* border-width: 1px; */ +} + +#topnav a { + padding: 8px; +} + +h1,p { margin: 0; } /* non-0 margin on firefox */ + +#mid { + /* background-image: url(img/grad_blue.png); */ + background-repeat: repeat-y; + /* background-color: #ffddaa; */ + padding-top: 20px; + padding-top: 20px; + margin-bottom: 10px; +} + +#mid img { + padding-left: 10px; + vertical-align: middle; +} + +a img { + border: 0 +} + +.twitter-share-button { + float: right; +} + +.twitter-follow-button { + padding: 5px; +} + +#nav { + background-color: #fff8f1; + margin-left: 10px; + margin-top: 20px; + margin-right: 20px; + float: left; + padding: 10px; + border-style: solid; + border-width: 2px; + font-family: sans-serif; +} + + +#nav h2 { + font-weight: bold; + font-size: 10pt; +} + +#nav h3 { + /* font-weight: bold; */ + padding-left: 5px; + /* font-style: oblique; */ + font-family: sans-serif; + font-size: 7pt; +} + +#nav div { + font-size: 9pt; + padding-left: 15px; +} + +#main { + background: #ffffff; + margin-top: 20px; + margin-left: 170px; + padding-left: 20px; + height: 100%; +} + +#main h1 { + font-family: sans-serif; +} + +.listing { + margin: 20px; + font-family: sans-serif; + font-weight: bold; +} + +.code { + padding: 10px; + margin: 10px; + font-size: 8pt; + font-weight: normal; + background: #f3f3f3; + width: 100%; + border-style: solid; + border-width: 1px; +} + +#footer { + /* background: #00ffff; */ + margin-top: 5px; + font-size: small; + font-family: sans-serif; +} + +hr { + height: 0.04em; + background: black; + margin: 0 10% 0 0; +} + +#footer { + width: 90%; +} + +#footer img { + margin-right: 5px; + float: right; +} + +#footer #support { + float: right; +} + +body { + width: 80%; +} diff --git a/c/ext/uthash/doc/userguide.txt b/c/ext/uthash/doc/userguide.txt new file mode 100644 index 0000000..693cff1 --- /dev/null +++ b/c/ext/uthash/doc/userguide.txt @@ -0,0 +1,1903 @@ +uthash User Guide +================= +Troy D. Hanson, Arthur O'Dwyer +v2.3.0, February 2021 + +To download uthash, follow this link back to the +https://github.com/troydhanson/uthash[GitHub project page]. +Back to my http://troydhanson.github.io/[other projects]. + +A hash in C +----------- +This document is written for C programmers. Since you're reading this, chances +are that you know a hash is used for looking up items using a key. In scripting +languages, hashes or "dictionaries" are used all the time. In C, hashes don't +exist in the language itself. This software provides a hash table for C +structures. + +What can it do? +~~~~~~~~~~~~~~~~~ +This software supports these operations on items in a hash table: + +1. add/replace +2. find +3. delete +4. count +5. iterate +6. sort + +Is it fast? +~~~~~~~~~~~ +Add, find and delete are normally constant-time operations. This is influenced +by your key domain and the hash function. + +This hash aims to be minimalistic and efficient. It's around 1000 lines of C. +It inlines automatically because it's implemented as macros. It's fast as long +as the hash function is suited to your keys. You can use the default hash +function, or easily compare performance and choose from among several other +<>. + +Is it a library? +~~~~~~~~~~~~~~~~ +No, it's just a single header file: `uthash.h`. All you need to do is copy +the header file into your project, and: + + #include "uthash.h" + +Since uthash is a header file only, there is no library code to link against. + +C/C++ and platforms +~~~~~~~~~~~~~~~~~~~ +This software can be used in C and C++ programs. It has been tested on: + + * Linux + * Windows using Visual Studio 2008 and 2010 + * Solaris + * OpenBSD + * FreeBSD + * Android + +Test suite +^^^^^^^^^^ +To run the test suite, enter the `tests` directory. Then, + + * on Unix platforms, run `make` + * on Windows, run the "do_tests_win32.cmd" batch file. (You may edit the + batch file if your Visual Studio is installed in a non-standard location). + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Download uthash +~~~~~~~~~~~~~~~ +Follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file. + +Getting help +~~~~~~~~~~~~ +Please use the https://groups.google.com/d/forum/uthash[uthash Google Group] to +ask questions. You can email it at uthash@googlegroups.com. + +Contributing +~~~~~~~~~~~~ +You may submit pull requests through GitHub. However, the maintainers of uthash +value keeping it unchanged, rather than adding bells and whistles. + +Extras included +~~~~~~~~~~~~~~~ +Three "extras" come with uthash. These provide lists, dynamic arrays and +strings: + + * link:utlist.html[utlist.h] provides linked list macros for C structures. + * link:utarray.html[utarray.h] implements dynamic arrays using macros. + * link:utstring.html[utstring.h] implements a basic dynamic string. + +History +~~~~~~~ +I wrote uthash in 2004-2006 for my own purposes. Originally it was hosted on +SourceForge. Uthash was downloaded around 30,000 times between 2006-2013 then +transitioned to GitHub. It's been incorporated into commercial software, +academic research, and into other open-source software. It has also been added +to the native package repositories for a number of Unix-y distros. + +When uthash was written, there were fewer options for doing generic hash tables +in C than exist today. There are faster hash tables, more memory-efficient hash +tables, with very different API's today. But, like driving a minivan, uthash is +convenient, and gets the job done for many purposes. + +As of July 2016, uthash is maintained by Arthur O'Dwyer. + +Your structure +-------------- + +In uthash, a hash table is comprised of structures. Each structure represents a +key-value association. One or more of the structure fields constitute the key. +The structure pointer itself is the value. + +.Defining a structure that can be hashed +---------------------------------------------------------------------- +#include "uthash.h" + +struct my_struct { + int id; /* key */ + char name[10]; + UT_hash_handle hh; /* makes this structure hashable */ +}; +---------------------------------------------------------------------- + +Note that, in uthash, your structure will never be moved or copied into another +location when you add it into a hash table. This means that you can keep other +data structures that safely point to your structure-- regardless of whether you +add or delete it from a hash table during your program's lifetime. + +The key +~~~~~~~ +There are no restrictions on the data type or name of the key field. The key +can also comprise multiple contiguous fields, having any names and data types. + +.Any data type... really? +***************************************************************************** +Yes, your key and structure can have any data type. Unlike function calls with +fixed prototypes, uthash consists of macros-- whose arguments are untyped-- and +thus able to work with any type of structure or key. +***************************************************************************** + +Unique keys +^^^^^^^^^^^ +As with any hash, every item must have a unique key. Your application must +enforce key uniqueness. Before you add an item to the hash table, you must +first know (if in doubt, check!) that the key is not already in use. You +can check whether a key already exists in the hash table using `HASH_FIND`. + +The hash handle +~~~~~~~~~~~~~~~ +The `UT_hash_handle` field must be present in your structure. It is used for +the internal bookkeeping that makes the hash work. It does not require +initialization. It can be named anything, but you can simplify matters by +naming it `hh`. This allows you to use the easier "convenience" macros to add, +find and delete items. + +A word about memory +~~~~~~~~~~~~~~~~~~~ + +Overhead +^^^^^^^^ +The hash handle consumes about 32 bytes per item on a 32-bit system, or 56 bytes +per item on a 64-bit system. The other overhead costs-- the buckets and the +table-- are negligible in comparison. You can use `HASH_OVERHEAD` to get the +overhead size, in bytes, for a hash table. See <>. + +How clean up occurs +^^^^^^^^^^^^^^^^^^^ +Some have asked how uthash cleans up its internal memory. The answer is simple: +'when you delete the final item' from a hash table, uthash releases all the +internal memory associated with that hash table, and sets its pointer to NULL. + + +Hash operations +--------------- + +This section introduces the uthash macros by example. For a more succinct +listing, see <>. + +.Convenience vs. general macros: +***************************************************************************** +The uthash macros fall into two categories. The 'convenience' macros can be used +with integer, pointer or string keys (and require that you chose the conventional +name `hh` for the `UT_hash_handle` field). The convenience macros take fewer +arguments than the general macros, making their usage a bit simpler for these +common types of keys. + +The 'general' macros can be used for any types of keys, or for multi-field keys, +or when the `UT_hash_handle` has been named something other than `hh`. These +macros take more arguments and offer greater flexibility in return. But if the +convenience macros suit your needs, use them-- your code will be more readable. +***************************************************************************** + +Declare the hash +~~~~~~~~~~~~~~~~ +Your hash must be declared as a `NULL`-initialized pointer to your structure. + + struct my_struct *users = NULL; /* important! initialize to NULL */ + +Add item +~~~~~~~~ +Allocate and initialize your structure as you see fit. The only aspect +of this that matters to uthash is that your key must be initialized to +a unique value. Then call `HASH_ADD`. (Here we use the convenience macro +`HASH_ADD_INT`, which offers simplified usage for keys of type `int`). + +.Add an item to a hash +---------------------------------------------------------------------- +void add_user(int user_id, char *name) { + struct my_struct *s; + + s = malloc(sizeof(struct my_struct)); + s->id = user_id; + strcpy(s->name, name); + HASH_ADD_INT(users, id, s); /* id: name of key field */ +} +---------------------------------------------------------------------- + +The first parameter to `HASH_ADD_INT` is the hash table, and the +second parameter is the 'name' of the key field. Here, this is `id`. The +last parameter is a pointer to the structure being added. + +[[validc]] +.Wait.. the field name is a parameter? +******************************************************************************* +If you find it strange that `id`, which is the 'name of a field' in the +structure, can be passed as a parameter... welcome to the world of macros. Don't +worry; the C preprocessor expands this to valid C code. +******************************************************************************* + +Key must not be modified while in-use +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Once a structure has been added to the hash, do not change the value of its key. +Instead, delete the item from the hash, change the key, and then re-add it. + +Checking uniqueness +^^^^^^^^^^^^^^^^^^^ +In the example above, we didn't check to see if `user_id` was already a key +of some existing item in the hash. *If there's any chance that duplicate keys +could be generated by your program, you must explicitly check the uniqueness* +before adding the key to the hash. If the key is already in the hash, you can +simply modify the existing structure in the hash rather than adding the item. +'It is an error to add two items with the same key to the hash table'. + +Let's rewrite the `add_user` function to check whether the id is in the hash. +Only if the id is not present in the hash, do we create the item and add it. +Otherwise we just modify the structure that already exists. + + 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 *s); + s->id = user_id; + HASH_ADD_INT(users, id, s); /* id: name of key field */ + } + strcpy(s->name, name); + } + + +Why doesn't uthash check key uniqueness for you? It saves the cost of a hash +lookup for those programs which don't need it- for example, programs whose keys +are generated by an incrementing, non-repeating counter. + +However, if replacement is a common operation, it is possible to use the +`HASH_REPLACE` macro. This macro, before adding the item, will try to find an +item with the same key and delete it first. It also returns a pointer to the +replaced item, so the user has a chance to de-allocate its memory. + +Passing the hash pointer into functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In the example above `users` is a global variable, but what if the caller wanted +to pass the hash pointer 'into' the `add_user` function? At first glance it would +appear that you could simply pass `users` as an argument, but that won't work +right. + + /* bad */ + void add_user(struct my_struct *users, int user_id, char *name) { + ... + HASH_ADD_INT(users, id, s); + } + +You really need to pass 'a pointer' to the hash pointer: + + /* good */ + void add_user(struct my_struct **users, int user_id, char *name) { ... + ... + HASH_ADD_INT(*users, id, s); + } + +Note that we dereferenced the pointer in the `HASH_ADD` also. + +The reason it's necessary to deal with a pointer to the hash pointer is simple: +the hash macros modify it (in other words, they modify the 'pointer itself' not +just what it points to). + + +Replace item +~~~~~~~~~~~~ +`HASH_REPLACE` macros are equivalent to HASH_ADD macros except they attempt +to find and delete the item first. If it finds and deletes an item, it will +also return that items pointer as an output parameter. + + +Find item +~~~~~~~~~ +To look up a structure in a hash, you need its key. Then call `HASH_FIND`. +(Here we use the convenience macro `HASH_FIND_INT` for keys of type `int`). + +.Find a structure using its key +---------------------------------------------------------------------- +struct my_struct *find_user(int user_id) { + struct my_struct *s; + + HASH_FIND_INT(users, &user_id, s); /* s: output pointer */ + return s; +} +---------------------------------------------------------------------- + +Here, the hash table is `users`, and `&user_id` points to the key (an integer +in this case). Last, `s` is the 'output' variable of `HASH_FIND_INT`. The +final result is that `s` points to the structure with the given key, or +is `NULL` if the key wasn't found in the hash. + +[NOTE] +The middle argument is a 'pointer' to the key. You can't pass a literal key +value to `HASH_FIND`. Instead assign the literal value to a variable, and pass +a pointer to the variable. + + +Delete item +~~~~~~~~~~~ +To delete a structure from a hash, you must have a pointer to it. (If you only +have the key, first do a `HASH_FIND` to get the structure pointer). + +.Delete an item from a hash +---------------------------------------------------------------------- +void delete_user(struct my_struct *user) { + HASH_DEL(users, user); /* user: pointer to deletee */ + free(user); /* optional; it's up to you! */ +} +---------------------------------------------------------------------- + +Here again, `users` is the hash table, and `user` is a pointer to the +structure we want to remove from the hash. + +uthash never frees your structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Deleting a structure just removes it from the hash table-- it doesn't `free` +it. The choice of when to free your structure is entirely up to you; uthash +will never free your structure. For example when using `HASH_REPLACE` macros, +a replaced output argument is returned back, in order to make it possible for +the user to de-allocate it. + +Delete can change the pointer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The hash table pointer (which initially points to the first item added to the +hash) can change in response to `HASH_DEL` (i.e. if you delete the first item +in the hash table). + +Iterative deletion +^^^^^^^^^^^^^^^^^^ +The `HASH_ITER` macro is a deletion-safe iteration construct which expands +to a simple 'for' loop. + +.Delete all items from a hash +---------------------------------------------------------------------- +void delete_all() { + struct my_struct *current_user, *tmp; + + HASH_ITER(hh, users, current_user, tmp) { + HASH_DEL(users, current_user); /* delete; users advances to next */ + free(current_user); /* optional- if you want to free */ + } +} +---------------------------------------------------------------------- + +All-at-once deletion +^^^^^^^^^^^^^^^^^^^^ +If you only want to delete all the items, but not free them or do any +per-element clean up, you can do this more efficiently in a single operation: + + HASH_CLEAR(hh, users); + +Afterward, the list head (here, `users`) will be set to `NULL`. + +Count items +~~~~~~~~~~~ + +The number of items in the hash table can be obtained using `HASH_COUNT`: + +.Count of items in the hash table +---------------------------------------------------------------------- +unsigned int num_users; +num_users = HASH_COUNT(users); +printf("there are %u users\n", num_users); +---------------------------------------------------------------------- + +Incidentally, this works even if the list head (here, `users`) is `NULL`, in +which case the count is 0. + +Iterating and sorting +~~~~~~~~~~~~~~~~~~~~~ + +You can loop over the items in the hash by starting from the beginning and +following the `hh.next` pointer. + +.Iterating over all the items in a hash +---------------------------------------------------------------------- +void print_users() { + struct my_struct *s; + + for (s = users; s != NULL; s = s->hh.next) { + printf("user id %d: name %s\n", s->id, s->name); + } +} +---------------------------------------------------------------------- + +There is also an `hh.prev` pointer you could use to iterate backwards through +the hash, starting from any known item. + +[[deletesafe]] +Deletion-safe iteration +^^^^^^^^^^^^^^^^^^^^^^^ +In the example above, it would not be safe to delete and free `s` in the body +of the 'for' loop, (because `s` is dereferenced each time the loop iterates). +This is easy to rewrite correctly (by copying the `s->hh.next` pointer to a +temporary variable 'before' freeing `s`), but it comes up often enough that a +deletion-safe iteration macro, `HASH_ITER`, is included. It expands to a +`for`-loop header. Here is how it could be used to rewrite the last example: + + struct my_struct *s, *tmp; + + HASH_ITER(hh, users, s, tmp) { + printf("user id %d: name %s\n", s->id, s->name); + /* ... it is safe to delete and free s here */ + } + +.A hash is also a doubly-linked list. +******************************************************************************* +Iterating backward and forward through the items in the hash is possible +because of the `hh.prev` and `hh.next` fields. All the items in the hash can +be reached by repeatedly following these pointers, thus the hash is also a +doubly-linked list. +******************************************************************************* + +If you're using uthash in a C++ program, you need an extra cast on the `for` +iterator, e.g., `s = static_cast(s->hh.next)`. + +Sorting +^^^^^^^ +The items in the hash are visited in "insertion order" when you follow the +`hh.next` pointer. You can sort the items into a new order using `HASH_SORT`. + + HASH_SORT(users, name_sort); + +The second argument is a pointer to a comparison function. It must accept two +pointer arguments (the items to compare), and must return an `int` which is +less than zero, zero, or greater than zero, if the first item sorts before, +equal to, or after the second item, respectively. (This is the same convention +used by `strcmp` or `qsort` in the standard C library). + + int sort_function(void *a, void *b) { + /* compare a to b (cast a and b appropriately) + * return (int) -1 if (a < b) + * return (int) 0 if (a == b) + * return (int) 1 if (a > b) + */ + } + +Below, `name_sort` and `id_sort` are two examples of sort functions. + +.Sorting the items in the hash +---------------------------------------------------------------------- +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); +} +---------------------------------------------------------------------- + +When the items in the hash are sorted, the first item may change position. In +the example above, `users` may point to a different structure after calling +`HASH_SORT`. + +A complete example +~~~~~~~~~~~~~~~~~~ + +We'll repeat all the code and embellish it with a `main()` function to form a +working example. + +If this code was placed in a file called `example.c` in the same directory as +`uthash.h`, it could be compiled and run like this: + + cc -o example example.c + ./example + +Follow the prompts to try the program. + +.A complete program +---------------------------------------------------------------------- +#include /* gets */ +#include /* atoi, malloc */ +#include /* 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 *s); + 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(int argc, char *argv[]) { + 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; +} +---------------------------------------------------------------------- + +This program is included in the distribution in `tests/example.c`. You can run +`make example` in that directory to compile it easily. + +Standard key types +------------------ +This section goes into specifics of how to work with different kinds of keys. +You can use nearly any type of key-- integers, strings, pointers, structures, etc. + +[NOTE] +.A note about float +================================================================================ +You can use floating point keys. This comes with the same caveats as with any +program that tests floating point equality. In other words, even the tiniest +difference in two floating point numbers makes them distinct keys. +================================================================================ + +Integer keys +~~~~~~~~~~~~ +The preceding examples demonstrated use of integer keys. To recap, use the +convenience macros `HASH_ADD_INT` and `HASH_FIND_INT` for structures with +integer keys. (The other operations such as `HASH_DELETE` and `HASH_SORT` are +the same for all types of keys). + +String keys +~~~~~~~~~~~ +If your structure has a string key, the operations to use depend on whether your +structure 'points to' the key (`char *`) or the string resides `within` the +structure (`char a[10]`). *This distinction is important*. As we'll see below, +you need to use `HASH_ADD_KEYPTR` when your structure 'points' to a key (that is, +the key itself is 'outside' of the structure); in contrast, use `HASH_ADD_STR` +for a string key that is contained *within* your structure. + +[NOTE] +.char[ ] vs. char* +================================================================================ +The string is 'within' the structure in the first example below-- `name` is a +`char[10]` field. In the second example, the key is 'outside' of the +structure-- `name` is a `char *`. So the first example uses `HASH_ADD_STR` but +the second example uses `HASH_ADD_KEYPTR`. For information on this macro, see +the <>. +================================================================================ + +String 'within' structure +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.A string-keyed hash (string within structure) +---------------------------------------------------------------------- +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + char name[10]; /* key (string is WITHIN the structure) */ + int id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + const char *names[] = { "joe", "bob", "betty", NULL }; + struct my_struct *s, *tmp, *users = NULL; + + for (int i = 0; names[i]; ++i) { + s = (struct my_struct *)malloc(sizeof *s); + strcpy(s->name, names[i]); + s->id = i; + HASH_ADD_STR(users, name, s); + } + + HASH_FIND_STR(users, "betty", s); + if (s) 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; +} +---------------------------------------------------------------------- + +This example is included in the distribution in `tests/test15.c`. It prints: + + betty's id is 2 + +String 'pointer' in structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Now, here is the same example but using a `char *` key instead of `char [ ]`: + +.A string-keyed hash (structure points to string) +---------------------------------------------------------------------- +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + const char *name; /* key */ + int id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + const char *names[] = { "joe", "bob", "betty", NULL }; + struct my_struct *s, *tmp, *users = NULL; + + for (int i = 0; names[i]; ++i) { + s = (struct my_struct *)malloc(sizeof *s); + s->name = names[i]; + s->id = i; + HASH_ADD_KEYPTR(hh, users, s->name, strlen(s->name), s); + } + + HASH_FIND_STR(users, "betty", s); + if (s) 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; +} +---------------------------------------------------------------------- + +This example is included in `tests/test40.c`. + +Pointer keys +~~~~~~~~~~~~ +Your key can be a pointer. To be very clear, this means the 'pointer itself' +can be the key (in contrast, if the thing 'pointed to' is the key, this is a +different use case handled by `HASH_ADD_KEYPTR`). + +Here is a simple example where a structure has a pointer member, called `key`. + +.A pointer key +---------------------------------------------------------------------- +#include +#include +#include "uthash.h" + +typedef struct { + void *key; + int i; + UT_hash_handle hh; +} el_t; + +el_t *hash = NULL; +char *someaddr = NULL; + +int main() { + el_t *d; + el_t *e = (el_t *)malloc(sizeof *e); + 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) printf("found\n"); + + /* release memory */ + HASH_DEL(hash, e); + free(e); + return 0; +} +---------------------------------------------------------------------- + +This example is included in `tests/test57.c`. Note that the end of the program +deletes the element out of the hash, (and since no more elements remain in the +hash), uthash releases its internal memory. + +Structure keys +~~~~~~~~~~~~~~ +Your key field can have any data type. To uthash, it is just a sequence of +bytes. Therefore, even a nested structure can be used as a key. We'll use the +general macros `HASH_ADD` and `HASH_FIND` to demonstrate. + +NOTE: Structures contain padding (wasted internal space used to fulfill +alignment requirements for the members of the structure). These padding bytes +'must be zeroed' before adding an item to the hash or looking up an item. +Therefore always zero the whole structure before setting the members of +interest. The example below does this-- see the two calls to `memset`. + +.A key which is a structure +---------------------------------------------------------------------- +#include +#include +#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(int argc, char *argv[]) { + record_t l, *p, *r, *tmp, *records = NULL; + + r = (record_t *)malloc(sizeof *r); + memset(r, 0, sizeof *r); + 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) 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; +} + +---------------------------------------------------------------------- + +This usage is nearly the same as use of a compound key explained below. + +Note that the general macros require the name of the `UT_hash_handle` to be +passed as the first argument (here, this is `hh`). The general macros are +documented in <>. + +Advanced Topics +--------------- + +Compound keys +~~~~~~~~~~~~~ +Your key can even comprise multiple contiguous fields. + +.A multi-field key +---------------------------------------------------------------------- +#include /* malloc */ +#include /* offsetof */ +#include /* printf */ +#include /* memset */ +#include "uthash.h" + +#define UTF32 1 + +typedef struct { + UT_hash_handle hh; + int len; + char encoding; /* these two fields */ + int text[]; /* comprise the key */ +} msg_t; + +typedef struct { + char encoding; + int text[]; +} lookup_key_t; + +int main(int argc, char *argv[]) { + 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)); + 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)); + 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) printf("found \n"); + free(lookup_key); + + HASH_ITER(hh, msgs, msg, tmp) { + HASH_DEL(msgs, msg); + free(msg); + } + return 0; +} +---------------------------------------------------------------------- + +This example is included in the distribution in `tests/test22.c`. + +If you use multi-field keys, recognize that the compiler pads adjacent fields +(by inserting unused space between them) in order to fulfill the alignment +requirement of each field. For example a structure containing a `char` followed +by an `int` will normally have 3 "wasted" bytes of padding after the char, in +order to make the `int` field start on a multiple-of-4 address (4 is the length +of the int). + +[[multifield_note]] +.Calculating the length of a multi-field key: +******************************************************************************* +To determine the key length when using a multi-field key, you must include any +intervening structure padding the compiler adds for alignment purposes. + +An easy way to calculate the key length is to use the `offsetof` macro from +``. The formula is: + + key length = offsetof(last_key_field) + + sizeof(last_key_field) + - offsetof(first_key_field) + +In the example above, the `keylen` variable is set using this formula. +******************************************************************************* + +When dealing with a multi-field key, you must zero-fill your structure before +`HASH_ADD`'ing it to a hash table, or using its fields in a `HASH_FIND` key. + +In the previous example, `memset` is used to initialize the structure by +zero-filling it. This zeroes out any padding between the key fields. If we +didn't zero-fill the structure, this padding would contain random values. The +random values would lead to `HASH_FIND` failures; as two "identical" keys will +appear to mismatch if there are any differences within their padding. + +Alternatively, you can customize the global <> +and <> to ignore the padding in your key. +See <>. + +[[multilevel]] +Multi-level hash tables +~~~~~~~~~~~~~~~~~~~~~~~ +A multi-level hash table arises when each element of a hash table contains its +own secondary hash table. There can be any number of levels. In a scripting +language you might see: + + $items{bob}{age}=37 + +The C program below builds this example in uthash: the hash table is called +`items`. It contains one element (`bob`) whose own hash table contains one +element (`age`) with value 37. No special functions are necessary to build +a multi-level hash table. + +While this example represents both levels (`bob` and `age`) using the same +structure, it would also be fine to use two different structure definitions. +It would also be fine if there were three or more levels instead of two. + +.Multi-level hash table +---------------------------------------------------------------------- +#include +#include +#include +#include "uthash.h" + +/* hash of hashes */ +typedef struct item { + char name[10]; + struct item *sub; + int val; + UT_hash_handle hh; +} item_t; + +item_t *items = NULL; + +int main(int argc, char *argvp[]) { + item_t *item1, *item2, *tmp1, *tmp2; + + /* make initial element */ + item_t *i = malloc(sizeof(*i)); + 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 = malloc(sizeof(*s)); + 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; +} +---------------------------------------------------------------------- +The example above is included in `tests/test59.c`. + +[[multihash]] +Items in several hash tables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A structure can be added to more than one hash table. A few reasons you might do +this include: + +- each hash table may use a different key; +- each hash table may have its own sort order; +- or you might simply use multiple hash tables for grouping purposes. E.g., + you could have users in an `admin_users` and a `users` hash table. + +Your structure needs to have a `UT_hash_handle` field for each hash table to +which it might be added. You can name them anything. E.g., + + UT_hash_handle hh1, hh2; + +Items with multiple keys +~~~~~~~~~~~~~~~~~~~~~~~~ +You might create a hash table keyed on an ID field, and another hash table keyed +on username (if usernames are unique). You can add the same user structure to +both hash tables (without duplication of the structure), allowing lookup of a +user structure by their name or ID. The way to achieve this is to have a +separate `UT_hash_handle` for each hash to which the structure may be added. + +.A structure with two different keys +---------------------------------------------------------------------- +struct my_struct { + int id; /* first key */ + char username[10]; /* second key */ + UT_hash_handle hh1; /* handle for first hash table */ + UT_hash_handle hh2; /* handle for second hash table */ +}; +---------------------------------------------------------------------- + +In the example above, the structure can now be added to two separate hash +tables. In one hash, `id` is its key, while in the other hash, `username` is +its key. (There is no requirement that the two hashes have different key +fields. They could both use the same key, such as `id`). + +Notice the structure has two hash handles (`hh1` and `hh2`). In the code +below, notice that each hash handle is used exclusively with a particular hash +table. (`hh1` is always used with the `users_by_id` hash, while `hh2` is +always used with the `users_by_name` hash table). + +.Two keys on a structure +---------------------------------------------------------------------- + struct my_struct *users_by_id = NULL, *users_by_name = NULL, *s; + int i; + char *name; + + s = malloc(sizeof(struct my_struct)); + s->id = 1; + strcpy(s->username, "thanson"); + + /* add the structure to both hash tables */ + HASH_ADD(hh1, users_by_id, id, sizeof(int), s); + HASH_ADD(hh2, users_by_name, username, strlen(s->username), s); + + /* find user by ID in the "users_by_id" hash table */ + i = 1; + HASH_FIND(hh1, users_by_id, &i, sizeof(int), s); + if (s) printf("found id %d: %s\n", i, s->username); + + /* find user by username in the "users_by_name" hash table */ + name = "thanson"; + HASH_FIND(hh2, users_by_name, name, strlen(name), s); + if (s) printf("found user %s: %d\n", name, s->id); +---------------------------------------------------------------------- + + +Sorted insertion of new items +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you would like to maintain a sorted hash you have two options. The first +option is to use the HASH_SRT() macro, which will sort any unordered list in +'O(n log(n))'. This is the best strategy if you're just filling up a hash +table with items in random order with a single final HASH_SRT() operation +when all is done. Obviously, this won't do what you want if you need +the list to be in an ordered state at times between insertion of +items. You can use HASH_SRT() after every insertion operation, but that will +yield a computational complexity of 'O(n^2 log n)'. + +The second route you can take is via the in-order add and replace macros. +The `HASH_ADD_INORDER*` macros work just like their `HASH_ADD*` counterparts, but +with an additional comparison-function argument: + + int name_sort(struct my_struct *a, struct my_struct *b) { + return strcmp(a->name, b->name); + } + + HASH_ADD_KEYPTR_INORDER(hh, items, &item->name, strlen(item->name), item, name_sort); + +New items are sorted at insertion time in 'O(n)', thus resulting in a +total computational complexity of 'O(n^2)' for the creation of the hash +table with all items. +For in-order add to work, the list must be in an ordered state before +insertion of the new item. + +Several sort orders +~~~~~~~~~~~~~~~~~~~ +It comes as no surprise that two hash tables can have different sort orders, but +this fact can also be used advantageously to sort the 'same items' in several +ways. This is based on the ability to store a structure in several hash tables. + +Extending the previous example, suppose we have many users. We have added each +user structure to the `users_by_id` hash table and the `users_by_name` hash table. +(To reiterate, this is done without the need to have two copies of each structure.) +Now we can define two sort functions, then use `HASH_SRT`. + + int sort_by_id(struct my_struct *a, struct my_struct *b) { + if (a->id == b->id) return 0; + return (a->id < b->id) ? -1 : 1; + } + + int sort_by_name(struct my_struct *a, struct my_struct *b) { + return strcmp(a->username, b->username); + } + + HASH_SRT(hh1, users_by_id, sort_by_id); + HASH_SRT(hh2, users_by_name, sort_by_name); + +Now iterating over the items in `users_by_id` will traverse them in id-order +while, naturally, iterating over `users_by_name` will traverse them in +name-order. The items are fully forward-and-backward linked in each order. +So even for one set of users, we might store them in two hash tables to provide +easy iteration in two different sort orders. + +Bloom filter (faster misses) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Programs that generate a fair miss rate (`HASH_FIND` that result in `NULL`) may +benefit from the built-in Bloom filter support. This is disabled by default, +because programs that generate only hits would incur a slight penalty from it. +Also, programs that do deletes should not use the Bloom filter. While the +program would operate correctly, deletes diminish the benefit of the filter. +To enable the Bloom filter, simply compile with `-DHASH_BLOOM=n` like: + + -DHASH_BLOOM=27 + +where the number can be any value up to 32 which determines the amount of memory +used by the filter, as shown below. Using more memory makes the filter more +accurate and has the potential to speed up your program by making misses bail +out faster. + +.Bloom filter sizes for selected values of n +[width="50%",cols="10m,30",grid="none",options="header"] +|===================================================================== +| n | Bloom filter size (per hash table) +| 16 | 8 kilobytes +| 20 | 128 kilobytes +| 24 | 2 megabytes +| 28 | 32 megabytes +| 32 | 512 megabytes +|===================================================================== + +Bloom filters are only a performance feature; they do not change the results of +hash operations in any way. The only way to gauge whether or not a Bloom filter +is right for your program is to test it. Reasonable values for the size of the +Bloom filter are 16-32 bits. + +Select +~~~~~~ +An experimental 'select' operation is provided that inserts those items from a +source hash that satisfy a given condition into a destination hash. This +insertion is done with somewhat more efficiency than if this were using +`HASH_ADD`, namely because the hash function is not recalculated for keys of the +selected items. This operation does not remove any items from the source hash. +Rather the selected items obtain dual presence in both hashes. The destination +hash may already have items in it; the selected items are added to it. In order +for a structure to be usable with `HASH_SELECT`, it must have two or more hash +handles. (As described <>, a structure can exist in many +hash tables at the same time; it must have a separate hash handle for each one). + + user_t *users = NULL; /* hash table of users */ + user_t *admins = NULL; /* hash table of admins */ + + typedef struct { + int id; + UT_hash_handle hh; /* handle for users hash */ + UT_hash_handle ah; /* handle for admins hash */ + } user_t; + +Now suppose we have added some users, and want to select just the administrator +users who have id's less than 1024. + + #define is_admin(x) (((user_t*)x)->id < 1024) + HASH_SELECT(ah, admins, hh, users, is_admin); + +The first two parameters are the 'destination' hash handle and hash table, the +second two parameters are the 'source' hash handle and hash table, and the last +parameter is the 'select condition'. Here we used a macro `is_admin(x)` but we +could just as well have used a function. + + int is_admin(const void *userv) { + user_t *user = (const user_t*)userv; + return (user->id < 1024) ? 1 : 0; + } + +If the select condition always evaluates to true, this operation is +essentially a 'merge' of the source hash into the destination hash. + +`HASH_SELECT` adds items to the destination without removing them from +the source; the source hash table remains unchanged. The destination hash table +must not be the same as the source hash table. + +An example of using `HASH_SELECT` is included in `tests/test36.c`. + +[[hash_keycompare]] +Specifying an alternate key comparison function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you call `HASH_FIND(hh, head, intfield, sizeof(int), out)`, uthash will +first call <>`(intfield, sizeof(int), hashvalue)` to +determine the bucket `b` in which to search, and then, for each element `elt` +of bucket `b`, uthash will evaluate +`elt->hh.hashv == hashvalue && elt.hh.keylen == sizeof(int) && HASH_KEYCMP(intfield, elt->hh.key, sizeof(int)) == 0`. +`HASH_KEYCMP` should return `0` to indicate that `elt` is a match and should be +returned, and any non-zero value to indicate that the search for a matching +element should continue. + +By default, uthash defines `HASH_KEYCMP` as an alias for `memcmp`. On platforms +that do not provide `memcmp`, you can substitute your own implementation. + +---------------------------------------------------------------------------- +#undef HASH_KEYCMP +#define HASH_KEYCMP(a,b,len) bcmp(a, b, len) +---------------------------------------------------------------------------- + +Another reason to substitute your own key comparison function is if your "key" is not +trivially comparable. In this case you will also need to substitute your own `HASH_FUNCTION`. + +---------------------------------------------------------------------------- +struct Key { + short s; + /* 2 bytes of padding */ + float f; +}; +/* do not compare the padding bytes; do not use memcmp on floats */ +unsigned key_hash(struct Key *s) { return s + (unsigned)f; } +bool key_equal(struct Key *a, struct Key *b) { return a.s == b.s && a.f == b.f; } + +#define HASH_FUNCTION(s,len,hashv) (hashv) = key_hash((struct Key *)s) +#define HASH_KEYCMP(a,b,len) (!key_equal((struct Key *)a, (struct Key *)b)) +---------------------------------------------------------------------------- + +Another reason to substitute your own key comparison function is to trade off +correctness for raw speed. During its linear search of a bucket, uthash always +compares the 32-bit `hashv` first, and calls `HASH_KEYCMP` only if the `hashv` +compares equal. This means that `HASH_KEYCMP` is called at least once per +successful find. Given a good hash function, we expect the `hashv` comparison to +produce a "false positive" equality only once in four billion times. Therefore, +we expect `HASH_KEYCMP` to produce `0` most of the time. If we expect many +successful finds, and our application doesn't mind the occasional false positive, +we might substitute a no-op comparison function: + +---------------------------------------------------------------------------- +#undef HASH_KEYCMP +#define HASH_KEYCMP(a,b,len) 0 /* occasionally wrong, but very fast */ +---------------------------------------------------------------------------- + +Note: The global equality-comparison function `HASH_KEYCMP` has no relationship +at all to the lessthan-comparison function passed as a parameter to `HASH_ADD_INORDER`. + +[[hash_functions]] +Built-in hash functions +~~~~~~~~~~~~~~~~~~~~~~~ +Internally, a hash function transforms a key into a bucket number. You don't +have to take any action to use the default hash function, currently Jenkins. + +Some programs may benefit from using another of the built-in hash functions. +There is a simple analysis utility included with uthash to help you determine +if another hash function will give you better performance. + +You can use a different hash function by compiling your program with +`-DHASH_FUNCTION=HASH_xyz` where `xyz` is one of the symbolic names listed +below. E.g., + + cc -DHASH_FUNCTION=HASH_BER -o program program.c + +.Built-in hash functions +[width="50%",cols="^5m,20",grid="none",options="header"] +|=============================================================================== +|Symbol | Name +|JEN | Jenkins (default) +|BER | Bernstein +|SAX | Shift-Add-Xor +|OAT | One-at-a-time +|FNV | Fowler/Noll/Vo +|SFH | Paul Hsieh +|=============================================================================== + +Which hash function is best? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You can easily determine the best hash function for your key domain. To do so, +you'll need to run your program once in a data-collection pass, and then run +the collected data through an included analysis utility. + +First you must build the analysis utility. From the top-level directory, + + cd tests/ + make + +We'll use `test14.c` to demonstrate the data-collection and analysis steps +(here using `sh` syntax to redirect file descriptor 3 to a file): + +.Using keystats +-------------------------------------------------------------------------------- +% cc -DHASH_EMIT_KEYS=3 -I../src -o test14 test14.c +% ./test14 3>test14.keys +% ./keystats test14.keys +fcn ideal% #items #buckets dup% fl add_usec find_usec del-all usec +--- ------ ---------- ---------- ----- -- ---------- ---------- ------------ +SFH 91.6% 1219 256 0% ok 92 131 25 +FNV 90.3% 1219 512 0% ok 107 97 31 +SAX 88.7% 1219 512 0% ok 111 109 32 +OAT 87.2% 1219 256 0% ok 99 138 26 +JEN 86.7% 1219 256 0% ok 87 130 27 +BER 86.2% 1219 256 0% ok 121 129 27 +-------------------------------------------------------------------------------- + +[NOTE] +The number 3 in `-DHASH_EMIT_KEYS=3` is a file descriptor. Any file descriptor +that your program doesn't use for its own purposes can be used instead of 3. +The data-collection mode enabled by `-DHASH_EMIT_KEYS=x` should not be used in +production code. + +Usually, you should just pick the first hash function that is listed. Here, this +is `SFH`. This is the function that provides the most even distribution for +your keys. If several have the same `ideal%`, then choose the fastest one +according to the `find_usec` column. + +keystats column reference +^^^^^^^^^^^^^^^^^^^^^^^^^ +fcn:: + symbolic name of hash function +ideal%:: + The percentage of items in the hash table which can be looked up within an + ideal number of steps. (Further explained below). +#items:: + the number of keys that were read in from the emitted key file +#buckets:: + the number of buckets in the hash after all the keys were added +dup%:: + the percent of duplicate keys encountered in the emitted key file. + Duplicates keys are filtered out to maintain key uniqueness. (Duplicates + are normal. For example, if the application adds an item to a hash, + deletes it, then re-adds it, the key is written twice to the emitted file.) +flags:: + this is either `ok`, or `nx` (noexpand) if the expansion inhibited flag is + set, described in <>. It is not recommended + to use a hash function that has the `noexpand` flag set. +add_usec:: + the clock time in microseconds required to add all the keys to a hash +find_usec:: + the clock time in microseconds required to look up every key in the hash +del-all usec:: + the clock time in microseconds required to delete every item in the hash + +[[ideal]] +ideal% +^^^^^^ + +.What is ideal%? +***************************************************************************** +The 'n' items in a hash are distributed into 'k' buckets. Ideally each bucket +would contain an equal share '(n/k)' of the items. In other words, the maximum +linear position of any item in a bucket chain would be 'n/k' if every bucket is +equally used. If some buckets are overused and others are underused, the +overused buckets will contain items whose linear position surpasses 'n/k'. +Such items are considered non-ideal. + +As you might guess, `ideal%` is the percentage of ideal items in the hash. These +items have favorable linear positions in their bucket chains. As `ideal%` +approaches 100%, the hash table approaches constant-time lookup performance. +***************************************************************************** + +[[hashscan]] +hashscan +~~~~~~~~ +NOTE: This utility is only available on Linux, and on FreeBSD (8.1 and up). + +A utility called `hashscan` is included in the `tests/` directory. It +is built automatically when you run `make` in that directory. This tool +examines a running process and reports on the uthash tables that it finds in +that program's memory. It can also save the keys from each table in a format +that can be fed into `keystats`. + +Here is an example of using `hashscan`. First ensure that it is built: + + cd tests/ + make + +Since `hashscan` needs a running program to inspect, we'll start up a simple +program that makes a hash table and then sleeps as our test subject: + + ./test_sleep & + pid: 9711 + +Now that we have a test program, let's run `hashscan` on it: + + ./hashscan 9711 + Address ideal items buckets mc fl bloom/sat fcn keys saved to + ------------------ ----- -------- -------- -- -- --------- --- ------------- + 0x862e038 81% 10000 4096 11 ok 16 14% JEN + +If we wanted to copy out all its keys for external analysis using `keystats`, +add the `-k` flag: + + ./hashscan -k 9711 + Address ideal items buckets mc fl bloom/sat fcn keys saved to + ------------------ ----- -------- -------- -- -- --------- --- ------------- + 0x862e038 81% 10000 4096 11 ok 16 14% JEN /tmp/9711-0.key + +Now we could run `./keystats /tmp/9711-0.key` to analyze which hash function +has the best characteristics on this set of keys. + +hashscan column reference +^^^^^^^^^^^^^^^^^^^^^^^^^ +Address:: + virtual address of the hash table +ideal:: + The percentage of items in the table which can be looked up within an ideal + number of steps. See <> in the `keystats` section. +items:: + number of items in the hash table +buckets:: + number of buckets in the hash table +mc:: + the maximum chain length found in the hash table (uthash usually tries to + keep fewer than 10 items in each bucket, or in some cases a multiple of 10) +fl:: + flags (either `ok`, or `NX` if the expansion-inhibited flag is set) +bloom/sat:: + if the hash table uses a Bloom filter, this is the size (as a power of two) + of the filter (e.g. 16 means the filter is 2^16 bits in size). The second + number is the "saturation" of the bits expressed as a percentage. The lower + the percentage, the more potential benefit to identify cache misses quickly. +fcn:: + symbolic name of hash function +keys saved to:: + file to which keys were saved, if any + +.How hashscan works +***************************************************************************** +When hashscan runs, it attaches itself to the target process, which suspends +the target process momentarily. During this brief suspension, it scans the +target's virtual memory for the signature of a uthash hash table. It then +checks if a valid hash table structure accompanies the signature and reports +what it finds. When it detaches, the target process resumes running normally. +The hashscan is performed "read-only"-- the target process is not modified. +Since hashscan is analyzing a momentary snapshot of a running process, it may +return different results from one run to another. +***************************************************************************** + +[[expansion]] +Expansion internals +~~~~~~~~~~~~~~~~~~~ +Internally this hash manages the number of buckets, with the goal of having +enough buckets so that each one contains only a small number of items. + +.Why does the number of buckets matter? +******************************************************************************** +When looking up an item by its key, this hash scans linearly through the items +in the appropriate bucket. In order for the linear scan to run in constant +time, the number of items in each bucket must be bounded. This is accomplished +by increasing the number of buckets as needed. +******************************************************************************** + +Normal expansion +^^^^^^^^^^^^^^^^ +This hash attempts to keep fewer than 10 items in each bucket. When an item is +added that would cause a bucket to exceed this number, the number of buckets in +the hash is doubled and the items are redistributed into the new buckets. In an +ideal world, each bucket will then contain half as many items as it did before. + +Bucket expansion occurs automatically and invisibly as needed. There is +no need for the application to know when it occurs. + +Per-bucket expansion threshold +++++++++++++++++++++++++++++++ +Normally all buckets share the same threshold (10 items) at which point bucket +expansion is triggered. During the process of bucket expansion, uthash can +adjust this expansion-trigger threshold on a per-bucket basis if it sees that +certain buckets are over-utilized. + +When this threshold is adjusted, it goes from 10 to a multiple of 10 (for that +particular bucket). The multiple is based on how many times greater the actual +chain length is than the ideal length. It is a practical measure to reduce +excess bucket expansion in the case where a hash function over-utilizes a few +buckets but has good overall distribution. However, if the overall distribution +gets too bad, uthash changes tactics. + +Inhibited expansion +^^^^^^^^^^^^^^^^^^^ +You usually don't need to know or worry about this, particularly if you used +the `keystats` utility during development to select a good hash for your keys. + +A hash function may yield an uneven distribution of items across the buckets. +In moderation this is not a problem. Normal bucket expansion takes place as +the chain lengths grow. But when significant imbalance occurs (because the hash +function is not well suited to the key domain), bucket expansion may be +ineffective at reducing the chain lengths. + +Imagine a very bad hash function which always puts every item in bucket 0. No +matter how many times the number of buckets is doubled, the chain length of +bucket 0 stays the same. In a situation like this, the best behavior is to +stop expanding, and accept 'O(n)' lookup performance. This is what uthash +does. It degrades gracefully if the hash function is ill-suited to the keys. + +If two consecutive bucket expansions yield `ideal%` values below 50%, uthash +inhibits expansion for that hash table. Once set, the 'bucket expansion +inhibited' flag remains in effect as long as the hash has items in it. +Inhibited expansion may cause `HASH_FIND` to exhibit worse than constant-time +performance. + +Diagnostic hooks +^^^^^^^^^^^^^^^^ + +There are two "notification" hooks which get executed if uthash is +expanding buckets, or setting the 'bucket expansion inhibited' flag. +There is no need for the application to set these hooks or take action in +response to these events. They are mainly for diagnostic purposes. +Normally both of these hooks are undefined and thus compile away to nothing. + +The `uthash_expand_fyi` hook can be defined to execute code whenever +uthash performs a bucket expansion. + +---------------------------------------------------------------------------- +#undef uthash_expand_fyi +#define uthash_expand_fyi(tbl) printf("expanded to %u buckets\n", tbl->num_buckets) +---------------------------------------------------------------------------- + +The `uthash_noexpand_fyi` hook can be defined to execute code whenever +uthash sets the 'bucket expansion inhibited' flag. + +---------------------------------------------------------------------------- +#undef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) printf("warning: bucket expansion inhibited\n") +---------------------------------------------------------------------------- + +Hooks +~~~~~ +You don't need to use these hooks -- they are only here if you want to modify +the behavior of uthash. Hooks can be used to replace standard library functions +that might be unavailable on some platforms, to change how uthash allocates +memory, or to run code in response to certain internal events. + +The `uthash.h` header will define these hooks to default values, unless they +are already defined. It is safe either to `#undef` and redefine them +after including `uthash.h`, or to define them before inclusion; for +example, by passing `-Duthash_malloc=my_malloc` on the command line. + +Specifying alternate memory management functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +By default, uthash uses `malloc` and `free` to manage memory. +If your application uses its own custom allocator, uthash can use them too. + +---------------------------------------------------------------------------- +#include "uthash.h" + +/* undefine the defaults */ +#undef uthash_malloc +#undef uthash_free + +/* re-define, specifying alternate functions */ +#define uthash_malloc(sz) my_malloc(sz) +#define uthash_free(ptr, sz) my_free(ptr) + +... +---------------------------------------------------------------------------- + +Notice that `uthash_free` receives two parameters. The `sz` parameter is for +convenience on embedded platforms that manage their own memory. + +Specifying alternate standard library functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Uthash also uses `strlen` (in the `HASH_FIND_STR` convenience macro, for +example) and `memset` (used only for zeroing memory). On platforms that do not +provide these functions, you can substitute your own implementations. + +---------------------------------------------------------------------------- +#undef uthash_bzero +#define uthash_bzero(a, len) my_bzero(a, len) + +#undef uthash_strlen +#define uthash_strlen(s) my_strlen(s) +---------------------------------------------------------------------------- + +Out of memory +^^^^^^^^^^^^^ +If memory allocation fails (i.e., the `uthash_malloc` function returns `NULL`), +the default behavior is to terminate the process by calling `exit(-1)`. This +can be modified by re-defining the `uthash_fatal` macro. + +---------------------------------------------------------------------------- +#undef uthash_fatal +#define uthash_fatal(msg) my_fatal_function(msg) +---------------------------------------------------------------------------- + +The fatal function should terminate the process or `longjmp` back to a safe +place. Note that an allocation failure may leave allocated memory that cannot +be recovered. After `uthash_fatal`, the hash table object should be considered +unusable; it might not be safe even to run `HASH_CLEAR` on the hash table +when it is in this state. + +To enable "returning a failure" if memory cannot be allocated, define the +macro `HASH_NONFATAL_OOM` before including the `uthash.h` header file. In this +case, `uthash_fatal` is not used; instead, each allocation failure results in +a single call to `uthash_nonfatal_oom(elt)` where `elt` is the address of the +element whose insertion triggered the failure. The default behavior of +`uthash_nonfatal_oom` is a no-op. + +---------------------------------------------------------------------------- +#undef uthash_nonfatal_oom +#define uthash_nonfatal_oom(elt) perhaps_recover((element_t *) elt) +---------------------------------------------------------------------------- + +Before the call to `uthash_nonfatal_oom`, the hash table is rolled back +to the state it was in prior to the problematic insertion; no memory is +leaked. It is safe to `throw` or `longjmp` out of the `uthash_nonfatal_oom` +handler. + +The `elt` argument will be of the correct pointer-to-element type, unless +`uthash_nonfatal_oom` is invoked from `HASH_SELECT`, in which case it will +be of `void*` type and must be cast before using. In any case, `elt->hh.tbl` +will be `NULL`. + +Allocation failure is possible only when adding elements to the hash table +(including the `ADD`, `REPLACE`, and `SELECT` operations). +`uthash_free` is not allowed to fail. + +Debug mode +~~~~~~~~~~ +If a program that uses this hash is compiled with `-DHASH_DEBUG=1`, a special +internal consistency-checking mode is activated. In this mode, the integrity +of the whole hash is checked following every add or delete operation. This is +for debugging the uthash software only, not for use in production code. + +In the `tests/` directory, running `make debug` will run all the tests in +this mode. + +In this mode, any internal errors in the hash data structure will cause a +message to be printed to `stderr` and the program to exit. + +The `UT_hash_handle` data structure includes `next`, `prev`, `hh_next` and +`hh_prev` fields. The former two fields determine the "application" ordering +(that is, insertion order-- the order the items were added). The latter two +fields determine the "bucket chain" order. These link the `UT_hash_handles` +together in a doubly-linked list that is a bucket chain. + +Checks performed in `-DHASH_DEBUG=1` mode: + +- the hash is walked in its entirety twice: once in 'bucket' order and a + second time in 'application' order +- the total number of items encountered in both walks is checked against the + stored number +- during the walk in 'bucket' order, each item's `hh_prev` pointer is compared + for equality with the last visited item +- during the walk in 'application' order, each item's `prev` pointer is compared + for equality with the last visited item + +.Macro debugging: +******************************************************************************** +Sometimes it's difficult to interpret a compiler warning on a line which +contains a macro call. In the case of uthash, one macro can expand to dozens of +lines. In this case, it is helpful to expand the macros and then recompile. +By doing so, the warning message will refer to the exact line within the macro. + +Here is an example of how to expand the macros and then recompile. This uses the +`test1.c` program in the `tests/` subdirectory. + + gcc -E -I../src test1.c > /tmp/a.c + egrep -v '^#' /tmp/a.c > /tmp/b.c + indent /tmp/b.c + gcc -o /tmp/b /tmp/b.c + +The last line compiles the original program (test1.c) with all macros expanded. +If there was a warning, the referenced line number can be checked in `/tmp/b.c`. +******************************************************************************** + +Thread safety +~~~~~~~~~~~~~ +You can use uthash in a threaded program. But you must do the locking. Use a +read-write lock to protect against concurrent writes. It is ok to have +concurrent readers (since uthash 1.5). + +For example using pthreads you can create an rwlock like this: + + pthread_rwlock_t lock; + if (pthread_rwlock_init(&lock, NULL) != 0) fatal("can't create rwlock"); + +Then, readers must acquire the read lock before doing any `HASH_FIND` calls or +before iterating over the hash elements: + + if (pthread_rwlock_rdlock(&lock) != 0) fatal("can't get rdlock"); + HASH_FIND_INT(elts, &i, e); + pthread_rwlock_unlock(&lock); + +Writers must acquire the exclusive write lock before doing any update. Add, +delete, and sort are all updates that must be locked. + + if (pthread_rwlock_wrlock(&lock) != 0) fatal("can't get wrlock"); + HASH_DEL(elts, e); + pthread_rwlock_unlock(&lock); + +If you prefer, you can use a mutex instead of a read-write lock, but this will +reduce reader concurrency to a single thread at a time. + +An example program using uthash with a read-write lock is included in +`tests/threads/test1.c`. + +[[Macro_reference]] +Macro reference +--------------- + +Convenience macros +~~~~~~~~~~~~~~~~~~ +The convenience macros do the same thing as the generalized macros, but +require fewer arguments. + +In order to use the convenience macros, + +1. the structure's `UT_hash_handle` field must be named `hh`, and +2. for add or find, the key field must be of type `int` or `char[]` or pointer + +.Convenience macros +[width="90%",cols="10m,30m",grid="none",options="header"] +|=============================================================================== +|macro | arguments +|HASH_ADD_INT | (head, keyfield_name, item_ptr) +|HASH_REPLACE_INT | (head, keyfield_name, item_ptr, replaced_item_ptr) +|HASH_FIND_INT | (head, key_ptr, item_ptr) +|HASH_ADD_STR | (head, keyfield_name, item_ptr) +|HASH_REPLACE_STR | (head, keyfield_name, item_ptr, replaced_item_ptr) +|HASH_FIND_STR | (head, key_ptr, item_ptr) +|HASH_ADD_PTR | (head, keyfield_name, item_ptr) +|HASH_REPLACE_PTR | (head, keyfield_name, item_ptr, replaced_item_ptr) +|HASH_FIND_PTR | (head, key_ptr, item_ptr) +|HASH_DEL | (head, item_ptr) +|HASH_SORT | (head, cmp) +|HASH_COUNT | (head) +|=============================================================================== + +General macros +~~~~~~~~~~~~~~ + +These macros add, find, delete and sort the items in a hash. You need to +use the general macros if your `UT_hash_handle` is named something other +than `hh`, or if your key's data type isn't `int` or `char[]`. + +.General macros +[width="90%",cols="10m,30m",grid="none",options="header"] +|=============================================================================== +|macro | arguments +|HASH_ADD | (hh_name, head, keyfield_name, key_len, item_ptr) +|HASH_ADD_BYHASHVALUE | (hh_name, head, keyfield_name, key_len, hashv, item_ptr) +|HASH_ADD_KEYPTR | (hh_name, head, key_ptr, key_len, item_ptr) +|HASH_ADD_KEYPTR_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr) +|HASH_ADD_INORDER | (hh_name, head, keyfield_name, key_len, item_ptr, cmp) +|HASH_ADD_BYHASHVALUE_INORDER | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, cmp) +|HASH_ADD_KEYPTR_INORDER | (hh_name, head, key_ptr, key_len, item_ptr, cmp) +|HASH_ADD_KEYPTR_BYHASHVALUE_INORDER | (hh_name, head, key_ptr, key_len, hashv, item_ptr, cmp) +|HASH_REPLACE | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr) +|HASH_REPLACE_BYHASHVALUE | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr) +|HASH_REPLACE_INORDER | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr, cmp) +|HASH_REPLACE_BYHASHVALUE_INORDER | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr, cmp) +|HASH_FIND | (hh_name, head, key_ptr, key_len, item_ptr) +|HASH_FIND_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr) +|HASH_DELETE | (hh_name, head, item_ptr) +|HASH_VALUE | (key_ptr, key_len, hashv) +|HASH_SRT | (hh_name, head, cmp) +|HASH_CNT | (hh_name, head) +|HASH_CLEAR | (hh_name, head) +|HASH_SELECT | (dst_hh_name, dst_head, src_hh_name, src_head, condition) +|HASH_ITER | (hh_name, head, item_ptr, tmp_item_ptr) +|HASH_OVERHEAD | (hh_name, head) +|=============================================================================== + +[NOTE] +`HASH_ADD_KEYPTR` is used when the structure contains a pointer to the +key, rather than the key itself. + +The `HASH_VALUE` and `..._BYHASHVALUE` macros are a performance mechanism mainly for the +special case of having different structures, in different hash tables, having +identical keys. It allows the hash value to be obtained once and then passed +in to the `..._BYHASHVALUE` macros, saving the expense of re-computing the hash value. + + +Argument descriptions +^^^^^^^^^^^^^^^^^^^^^ +hh_name:: + name of the `UT_hash_handle` field in the structure. Conventionally called + `hh`. +head:: + the structure pointer variable which acts as the "head" of the hash. So + named because it initially points to the first item that is added to the hash. +keyfield_name:: + the name of the key field in the structure. (In the case of a multi-field + key, this is the first field of the key). If you're new to macros, it + might seem strange to pass the name of a field as a parameter. See + <>. +key_len:: + the length of the key field in bytes. E.g. for an integer key, this is + `sizeof(int)`, while for a string key it's `strlen(key)`. (For a + multi-field key, see <>.) +key_ptr:: + for `HASH_FIND`, this is a pointer to the key to look up in the hash + (since it's a pointer, you can't directly pass a literal value here). For + `HASH_ADD_KEYPTR`, this is the address of the key of the item being added. +hashv:: + the hash value of the provided key. This is an input parameter for the + `..._BYHASHVALUE` macros, and an output parameter for `HASH_VALUE`. + Reusing a cached hash value can be a performance optimization if + you're going to do repeated lookups for the same key. +item_ptr:: + pointer to the structure being added, deleted, replaced, or looked up, or the current + pointer during iteration. This is an input parameter for the `HASH_ADD`, + `HASH_DELETE`, and `HASH_REPLACE` macros, and an output parameter for `HASH_FIND` + and `HASH_ITER`. (When using `HASH_ITER` to iterate, `tmp_item_ptr` + is another variable of the same type as `item_ptr`, used internally). +replaced_item_ptr:: + used in `HASH_REPLACE` macros. This is an output parameter that is set to point + to the replaced item (if no item is replaced it is set to NULL). +cmp:: + pointer to comparison function which accepts two arguments (pointers to + items to compare) and returns an int specifying whether the first item + should sort before, equal to, or after the second item (like `strcmp`). +condition:: + a function or macro which accepts a single argument (a void pointer to a + structure, which needs to be cast to the appropriate structure type). The + function or macro should evaluate to a non-zero value if the + structure should be "selected" for addition to the destination hash. + +// vim: set tw=80 wm=2 syntax=asciidoc: diff --git a/c/ext/uthash/doc/utarray.txt b/c/ext/uthash/doc/utarray.txt new file mode 100644 index 0000000..8ef940b --- /dev/null +++ b/c/ext/uthash/doc/utarray.txt @@ -0,0 +1,383 @@ +utarray: dynamic array macros for C +=================================== +Troy D. Hanson +v2.3.0, February 2021 + +Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page]. + +Introduction +------------ +A set of general-purpose dynamic array macros for C structures are included with +uthash in `utarray.h`. To use these macros in your own C program, just +copy `utarray.h` into your source directory and use it in your programs. + + #include "utarray.h" + +The dynamic array supports basic operations such as push, pop, and erase on the +array elements. These array elements can be any simple datatype or structure. +The array <> are based loosely on the C++ STL vector methods. + +Internally the dynamic array contains a contiguous memory region into which +the elements are copied. This buffer is grown as needed using `realloc` to +accommodate all the data that is pushed into it. + +Download +~~~~~~~~ +To download the `utarray.h` header file, +follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file, +then look in the src/ sub-directory. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utarray' macros have been tested on: + + * Linux, + * Mac OS X, + * Windows, using Visual Studio 2008 and Visual Studio 2010 + +Usage +----- + +Declaration +~~~~~~~~~~~ + +The array itself has the data type `UT_array`, regardless of the type of +elements to be stored in it. It is declared like, + + UT_array *nums; + +New and free +~~~~~~~~~~~~ +The next step is to create the array using `utarray_new`. Later when you're +done with the array, `utarray_free` will free it and all its elements. + +Push, pop, etc +~~~~~~~~~~~~~~ +The central features of the utarray involve putting elements into it, taking +them out, and iterating over them. There are several <> +to pick from that deal with either single elements or ranges of elements at a +time. In the examples below we will use only the push operation to insert +elements. + +Elements +-------- + +Support for dynamic arrays of integers or strings is especially easy. These are +best shown by example: + +Integers +~~~~~~~~ +This example makes a utarray of integers, pushes 0-9 into it, then prints it. +Lastly it frees it. + +.Integer elements +------------------------------------------------------------------------------- +#include +#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; +} +------------------------------------------------------------------------------- + +The second argument to `utarray_push_back` is always a 'pointer' to the type +(so a literal cannot be used). So for integers, it is an `int*`. + +Strings +~~~~~~~ +In this example we make a utarray of strings, push two strings into it, print +it and free it. + +.String elements +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +int main() { + UT_array *strs; + 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=(char**)utarray_next(strs,p))) { + printf("%s\n",*p); + } + + utarray_free(strs); + + return 0; +} +------------------------------------------------------------------------------- + +In this example, since the element is a `char*`, we pass a pointer to it +(`char**`) as the second argument to `utarray_push_back`. Note that "push" makes +a copy of the source string and pushes that copy into the array. + +About UT_icd +~~~~~~~~~~~~ + +Arrays be made of any type of element, not just integers and strings. The +elements can be basic types or structures. Unless you're dealing with integers +and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need +to define a `UT_icd` helper structure. This structure contains everything that +utarray needs to initialize, copy or destruct elements. + + typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; + } UT_icd; + +The three function pointers `init`, `copy`, and `dtor` have these prototypes: + + typedef void (ctor_f)(void *dst, const void *src); + typedef void (dtor_f)(void *elt); + typedef void (init_f)(void *elt); + +The `sz` is just the size of the element being stored in the array. + +The `init` function will be invoked whenever utarray needs to initialize an +empty element. This only happens as a byproduct of `utarray_resize` or +`utarray_extend_back`. If `init` is `NULL`, it defaults to zero filling the +new element using memset. + +The `copy` function is used whenever an element is copied into the array. +It is invoked during `utarray_push_back`, `utarray_insert`, `utarray_inserta`, +or `utarray_concat`. If `copy` is `NULL`, it defaults to a bitwise copy using +memcpy. + +The `dtor` function is used to clean up an element that is being removed from +the array. It may be invoked due to `utarray_resize`, `utarray_pop_back`, +`utarray_erase`, `utarray_clear`, `utarray_done` or `utarray_free`. If the +elements need no cleanup upon destruction, `dtor` may be `NULL`. + +Scalar types +~~~~~~~~~~~~ + +The next example uses `UT_icd` with all its defaults to make a utarray of +`long` elements. This example pushes two longs, prints them, and frees the +array. + +.long elements +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +UT_icd long_icd = {sizeof(long), NULL, NULL, NULL }; + +int main() { + UT_array *nums; + long l, *p; + 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))) printf("%ld\n", *p); + + utarray_free(nums); + return 0; +} +------------------------------------------------------------------------------- + +Structures +~~~~~~~~~~ + +Structures can be used as utarray elements. If the structure requires no +special effort to initialize, copy or destruct, we can use `UT_icd` with all +its defaults. This example shows a structure that consists of two integers. Here +we push two values, print them and free the array. + +.Structure (simple) +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +typedef struct { + int a; + int b; +} intpair_t; + +UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL}; + +int main() { + + UT_array *pairs; + intpair_t ip, *p; + 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; +} +------------------------------------------------------------------------------- + +The real utility of `UT_icd` is apparent when the elements of the utarray are +structures that require special work to initialize, copy or destruct. + +For example, when a structure contains pointers to related memory areas that +need to be copied when the structure is copied (and freed when the structure is +freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`. + +Here we take an example of a structure that contains an integer and a string. +When this element is copied (such as when an element is pushed into the array), +we want to "deep copy" the `s` pointer (so the original element and the new +element point to their own copies of `s`). When an element is destructed, we +want to "deep free" its copy of `s`. Lastly, this example is written to work +even if `s` has the value `NULL`. + +.Structure (complex) +------------------------------------------------------------------------------- +#include +#include +#include "utarray.h" + +typedef struct { + int a; + char *s; +} intchar_t; + +void intchar_copy(void *_dst, const void *_src) { + intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src; + dst->a = src->a; + dst->s = src->s ? strdup(src->s) : NULL; +} + +void intchar_dtor(void *_elt) { + intchar_t *elt = (intchar_t*)_elt; + if (elt->s) free(elt->s); +} + +UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor}; + +int main() { + UT_array *intchars; + intchar_t ic, *p; + utarray_new(intchars, &intchar_icd); + + ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic); + ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic); + + p=NULL; + while( (p=(intchar_t*)utarray_next(intchars,p))) { + printf("%d %s\n", p->a, (p->s ? p->s : "null")); + } + + utarray_free(intchars); + return 0; +} + +------------------------------------------------------------------------------- + +[[operations]] +Reference +--------- +This table lists all the utarray operations. These are loosely based on the C++ +vector class. + +Operations +~~~~~~~~~~ + +[width="100%",cols="50 _b); + } + +4. `utarray_find` uses a binary search to locate an element having a certain value + according to the given comparison function. The utarray must be first sorted + using the same comparison function. An example of using `utarray_find` with + a utarray of strings is included in `tests/test61.c`. + +5. A 'pointer' to a particular element (obtained using `utarray_eltptr` or + `utarray_front`, `utarray_next`, `utarray_prev`, `utarray_back`) becomes invalid whenever + another element is inserted into the utarray. This is because the internal + memory management may need to `realloc` the element storage to a new address. + For this reason, it's usually better to refer to an element by its integer + 'index' in code whose duration may include element insertion. + +6. To override the default out-of-memory handling behavior (which calls `exit(-1)`), + override the `utarray_oom()` macro before including `utarray.h`. + For example, + + #define utarray_oom() do { longjmp(error_handling_location); } while (0) + ... + #include "utarray.h" + +// vim: set nowrap syntax=asciidoc: diff --git a/c/ext/uthash/doc/uthash-mini.png b/c/ext/uthash/doc/uthash-mini.png new file mode 100644 index 0000000..9536b2a Binary files /dev/null and b/c/ext/uthash/doc/uthash-mini.png differ diff --git a/c/ext/uthash/doc/uthash-mini.svg b/c/ext/uthash/doc/uthash-mini.svg new file mode 100644 index 0000000..ea2d074 --- /dev/null +++ b/c/ext/uthash/doc/uthash-mini.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ut hash + + + + + + + + + + + + + + + + + + + + + + + diff --git a/c/ext/uthash/doc/uthash.png b/c/ext/uthash/doc/uthash.png new file mode 100644 index 0000000..20df5a7 Binary files /dev/null and b/c/ext/uthash/doc/uthash.png differ diff --git a/c/ext/uthash/doc/utlist.txt b/c/ext/uthash/doc/utlist.txt new file mode 100644 index 0000000..47dc625 --- /dev/null +++ b/c/ext/uthash/doc/utlist.txt @@ -0,0 +1,293 @@ +utlist: linked list macros for C structures +=========================================== +Troy D. Hanson +v2.3.0, February 2021 + +Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page]. + +Introduction +------------ +A set of general-purpose 'linked list' macros for C structures are included with +uthash in `utlist.h`. To use these macros in your own C program, just +copy `utlist.h` into your source directory and use it in your programs. + + #include "utlist.h" + +These macros support the basic linked list operations: adding and deleting +elements, sorting them and iterating over them. + +Download +~~~~~~~~ +To download the `utlist.h` header file, +follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file, +then look in the src/ sub-directory. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utlist' macros have been tested on: + + * Linux, + * Mac OS X, and + * Windows, using Visual Studio 2008, Visual Studio 2010, or Cygwin/MinGW. + +Using utlist +------------ + +Types of lists +~~~~~~~~~~~~~~ +Three types of linked lists are supported: + +- *singly-linked* lists, +- *doubly-linked* lists, and +- *circular, doubly-linked* lists + +Efficiency +^^^^^^^^^^ +Prepending elements:: + Constant-time on all list types. +Appending:: + 'O(n)' on singly-linked lists; constant-time on doubly-linked list. + (The utlist implementation of the doubly-linked list keeps a tail pointer in + `head->prev` so that append can be done in constant time). +Deleting elements:: + 'O(n)' on singly-linked lists; constant-time on doubly-linked list. +Sorting:: + 'O(n log(n))' for all list types. +Insertion in order (for sorted lists):: + 'O(n)' for all list types. +Iteration, counting and searching:: + 'O(n)' for all list types. + +List elements +~~~~~~~~~~~~~ +You can use any structure with these macros, as long as the structure +contains a `next` pointer. If you want to make a doubly-linked list, +the element also needs to have a `prev` pointer. + + typedef struct element { + char *name; + struct element *prev; /* needed for a doubly-linked list only */ + struct element *next; /* needed for singly- or doubly-linked lists */ + } element; + +You can name your structure anything. In the example above it is called `element`. +Within a particular list, all elements must be of the same type. + +Flexible prev/next naming +^^^^^^^^^^^^^^^^^^^^^^^^^ +You can name your `prev` and `next` pointers something else. If you do, there is +a <> that work identically but take these names as +extra arguments. + +List head +~~~~~~~~~ +The list head is simply a pointer to your element structure. You can name it +anything. *It must be initialized to `NULL`*. + + element *head = NULL; + +List operations +~~~~~~~~~~~~~~~ +The lists support inserting or deleting elements, sorting the elements and +iterating over them. + +[width="100%",cols="10next`. + +The 'count' operation iterates over the list and increments a supplied counter. + +The parameters shown in the table above are explained here: + +head:: + The list head (a pointer to your list element structure). +add:: + A pointer to the list element structure you are adding to the list. +del:: + A pointer to the list element structure you are replacing or + deleting from the list. +elt:: + A pointer that will be assigned to each list element in succession (see + example) in the case of iteration macros; or, the output pointer from + the search macros. +ref:: + Reference element for prepend and append operations that will be + prepended before or appended after. + If `ref` is a pointer with value NULL, the new element will be appended to the + list for _PREPEND_ELEM() operations and prepended for _APPEND_ELEM() operations. + `ref` must be the name of a pointer variable and cannot be literally NULL, + use _PREPEND() and _APPEND() macro family instead. +like:: + An element pointer, having the same type as `elt`, for which the search macro + seeks a match (if found, the match is stored in `elt`). A match is determined + by the given `cmp` function. +cmp:: + pointer to comparison function which accepts two arguments-- these are + pointers to two element structures to be compared. The comparison function + must return an `int` that is negative, zero, or positive, which specifies + whether the first item should sort before, equal to, or after the second item, + respectively. (In other words, the same convention that is used by `strcmp`). + Note that under Visual Studio 2008 you may need to declare the two arguments + as `void *` and then cast them back to their actual types. +tmp:: + A pointer of the same type as `elt`. Used internally. Need not be initialized. +mbr:: + In the scalar search macro, the name of a member within the `elt` structure which + will be tested (using `==`) for equality with the value `val`. +val:: + In the scalar search macro, specifies the value of (of structure member + `field`) of the element being sought. +count:: + integer which will be set to the length of the list + +Example +~~~~~~~ +This example program reads names from a text file (one name per line), and +appends each name to a doubly-linked list. Then it sorts and prints them. + +.A doubly-linked list +-------------------------------------------------------------------------------- +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(el *a, el *b) { + return strcmp(a->bname,b->bname); +} + +el *head = NULL; /* important- initialize to NULL! */ + +int main(int argc, char *argv[]) { + el *name, *elt, *tmp, etmp; + + char linebuf[BUFLEN]; + int count; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el *)malloc(sizeof *name)) == NULL) exit(-1); + strcpy(name->bname, linebuf); + DL_APPEND(head, name); + } + DL_SORT(head, namecmp); + DL_FOREACH(head,elt) printf("%s", elt->bname); + DL_COUNT(head, elt, count); + printf("%d number of elements in list\n", count); + + memcpy(&etmp.bname, "WES\n", 5); + DL_SEARCH(head,elt,&etmp,namecmp); + if (elt) printf("found %s\n", elt->bname); + + /* now delete each element, use the safe iterator */ + DL_FOREACH_SAFE(head,elt,tmp) { + DL_DELETE(head,elt); + free(elt); + } + + fclose(file); + + return 0; +} +-------------------------------------------------------------------------------- + +[[flex_names]] +Other names for prev and next +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If the `prev` and `next` fields are named something else, a separate group of +macros must be used. These work the same as the regular macros, but take the +field names as extra parameters. + +These "flexible field name" macros are shown below. They all end with `2`. Each +operates the same as its counterpart without the `2`, but they take the name of +the `prev` and `next` fields (as applicable) as trailing arguments. + +[width="100%",cols="10 +v2.3.0, February 2021 + +Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page]. + +Introduction +------------ +The functions in `utringbuffer.h` are based on the general-purpose array macros +provided in `utarray.h`, so before reading this page you should read +link:utarray.html[that page] first. + +To use these macros in your own C program, copy both `utarray.h` and `utringbuffer.h` +into your source directory and use `utringbuffer.h` in your program. + + #include "utringbuffer.h" + +The provided <> are based loosely on the C++ STL vector methods. +The ring-buffer data type supports construction (with a specified capacity), +destruction, iteration, and push, but not pop; once the ring-buffer reaches full +capacity, pushing a new element automatically pops and destroys the oldest element. +The elements contained in the ring-buffer can be any simple datatype or structure. + +Internally the ring-buffer contains a pre-allocated memory region into which the +elements are copied, starting at position 0. When the ring-buffer reaches full +capacity, the next element to be pushed is pushed at position 0, overwriting the +oldest element, and the internal index representing the "start" of the ring-buffer +is incremented. A ring-buffer, once full, can never become un-full. + + +Download +~~~~~~~~ +To download the `utringbuffer.h` header file, +follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file, +then look in the src/ sub-directory. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utringbuffer' macros have been tested on: + + * Linux, + * Mac OS X, + * Windows, using Visual Studio 2008 and Visual Studio 2010 + +Usage +----- + +Declaration +~~~~~~~~~~~ + +The ring-buffer itself has the data type `UT_ringbuffer`, regardless of the type of +elements to be stored in it. It is declared like, + + UT_ringbuffer *history; + +New and free +~~~~~~~~~~~~ +The next step is to create the ring-buffer using `utringbuffer_new`. Later when you're +done with the ring-buffer, `utringbuffer_free` will free it and all its elements. + +Push, etc +~~~~~~~~~ +The central features of the ring-buffer involve putting elements into it +and iterating over them. There are several <> +that deal with either single elements or ranges of elements at a +time. In the examples below we will use only the push operation to insert +elements. + +Elements +-------- + +Support for dynamic arrays of integers or strings is especially easy. These are +best shown by example: + +Integers +~~~~~~~~ +This example makes a ring-buffer of integers, pushes 0-9 into it, then prints it +two different ways. Lastly it frees it. + +.Integer elements +------------------------------------------------------------------------------- +#include +#include "utringbuffer.h" + +int main() { + UT_ringbuffer *history; + int i, *p; + + utringbuffer_new(history, 7, &ut_int_icd); + for(i=0; i < 10; i++) utringbuffer_push_back(history, &i); + + for (p = (int*)utringbuffer_front(history); + p != NULL; + p = (int*)utringbuffer_next(history, p)) { + printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */ + } + + for (i=0; i < utringbuffer_len(history); i++) { + p = utringbuffer_eltptr(history, i); + printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */ + } + + utringbuffer_free(history); + + return 0; +} +------------------------------------------------------------------------------- + +The second argument to `utringbuffer_push_back` is always a 'pointer' to the type +(so a literal cannot be used). So for integers, it is an `int*`. + +Strings +~~~~~~~ +In this example we make a ring-buffer of strings, push two strings into it, print +it and free it. + +.String elements +------------------------------------------------------------------------------- +#include +#include "utringbuffer.h" + +int main() { + UT_ringbuffer *strs; + char *s, **p; + + utringbuffer_new(strs, 7, &ut_str_icd); + + s = "hello"; utringbuffer_push_back(strs, &s); + s = "world"; utringbuffer_push_back(strs, &s); + p = NULL; + while ( (p=(char**)utringbuffer_next(strs,p))) { + printf("%s\n",*p); + } + + utringbuffer_free(strs); + + return 0; +} +------------------------------------------------------------------------------- + +In this example, since the element is a `char*`, we pass a pointer to it +(`char**`) as the second argument to `utringbuffer_push_back`. Note that "push" makes +a copy of the source string and pushes that copy into the array. + +About UT_icd +~~~~~~~~~~~~ + +Arrays can be made of any type of element, not just integers and strings. The +elements can be basic types or structures. Unless you're dealing with integers +and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need +to define a `UT_icd` helper structure. This structure contains everything that +utringbuffer (or utarray) needs to initialize, copy or destruct elements. + + typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; + } UT_icd; + +The three function pointers `init`, `copy`, and `dtor` have these prototypes: + + typedef void (ctor_f)(void *dst, const void *src); + typedef void (dtor_f)(void *elt); + typedef void (init_f)(void *elt); + +The `sz` is just the size of the element being stored in the array. + +The `init` function is used by utarray but is never used by utringbuffer; +you may safely set it to any value you want. + +The `copy` function is used whenever an element is copied into the buffer. +It is invoked during `utringbuffer_push_back`. +If `copy` is `NULL`, it defaults to a bitwise copy using memcpy. + +The `dtor` function is used to clean up an element that is being removed from +the buffer. It may be invoked due to `utringbuffer_push_back` (on the oldest +element in the buffer), `utringbuffer_clear`, `utringbuffer_done`, or +`utringbuffer_free`. +If the elements need no cleanup upon destruction, `dtor` may be `NULL`. + +Scalar types +~~~~~~~~~~~~ + +The next example uses `UT_icd` with all its defaults to make a ring-buffer of +`long` elements. This example pushes two longs into a buffer of capacity 1, +prints the contents of the buffer (which is to say, the most recent value +pushed), and then frees the buffer. + +.long elements +------------------------------------------------------------------------------- +#include +#include "utringbuffer.h" + +UT_icd long_icd = {sizeof(long), NULL, NULL, NULL }; + +int main() { + UT_ringbuffer *nums; + long l, *p; + utringbuffer_new(nums, 1, &long_icd); + + l=1; utringbuffer_push_back(nums, &l); + l=2; utringbuffer_push_back(nums, &l); + + p=NULL; + while((p = (long*)utringbuffer_next(nums,p))) printf("%ld\n", *p); + + utringbuffer_free(nums); + return 0; +} +------------------------------------------------------------------------------- + +Structures +~~~~~~~~~~ + +Structures can be used as utringbuffer elements. If the structure requires no +special effort to initialize, copy or destruct, we can use `UT_icd` with all +its defaults. This example shows a structure that consists of two integers. Here +we push two values, print them and free the buffer. + +.Structure (simple) +------------------------------------------------------------------------------- +#include +#include "utringbuffer.h" + +typedef struct { + int a; + int b; +} intpair_t; + +UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL}; + +int main() { + + UT_ringbuffer *pairs; + intpair_t ip, *p; + utringbuffer_new(pairs, 7, &intpair_icd); + + ip.a=1; ip.b=2; utringbuffer_push_back(pairs, &ip); + ip.a=10; ip.b=20; utringbuffer_push_back(pairs, &ip); + + for(p=(intpair_t*)utringbuffer_front(pairs); + p!=NULL; + p=(intpair_t*)utringbuffer_next(pairs,p)) { + printf("%d %d\n", p->a, p->b); + } + + utringbuffer_free(pairs); + return 0; +} +------------------------------------------------------------------------------- + +The real utility of `UT_icd` is apparent when the elements stored in the +ring-buffer are structures that require special work to initialize, copy or +destruct. + +For example, when a structure contains pointers to related memory areas that +need to be copied when the structure is copied (and freed when the structure is +freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`. + +Here we take an example of a structure that contains an integer and a string. +When this element is copied (such as when an element is pushed), +we want to "deep copy" the `s` pointer (so the original element and the new +element point to their own copies of `s`). When an element is destructed, we +want to "deep free" its copy of `s`. Lastly, this example is written to work +even if `s` has the value `NULL`. + +.Structure (complex) +------------------------------------------------------------------------------- +#include +#include +#include "utringbuffer.h" + +typedef struct { + int a; + char *s; +} intchar_t; + +void intchar_copy(void *_dst, const void *_src) { + intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src; + dst->a = src->a; + dst->s = src->s ? strdup(src->s) : NULL; +} + +void intchar_dtor(void *_elt) { + intchar_t *elt = (intchar_t*)_elt; + free(elt->s); +} + +UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor}; + +int main() { + UT_ringbuffer *intchars; + intchar_t ic, *p; + utringbuffer_new(intchars, 2, &intchar_icd); + + ic.a=1; ic.s="hello"; utringbuffer_push_back(intchars, &ic); + ic.a=2; ic.s="world"; utringbuffer_push_back(intchars, &ic); + ic.a=3; ic.s="peace"; utringbuffer_push_back(intchars, &ic); + + p=NULL; + while( (p=(intchar_t*)utringbuffer_next(intchars,p))) { + printf("%d %s\n", p->a, (p->s ? p->s : "null")); + /* prints "2 world 3 peace" */ + } + + utringbuffer_free(intchars); + return 0; +} + +------------------------------------------------------------------------------- + +[[operations]] +Reference +--------- +This table lists all the utringbuffer operations. These are loosely based on the C++ +vector class. + +Operations +~~~~~~~~~~ + +[width="100%",cols="50 +v2.3.0, February 2021 + +Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page]. + +Introduction +------------ +A set of very simple stack macros for C structures are included with +uthash in `utstack.h`. To use these macros in your own C program, just +copy `utstack.h` into your source directory and use it in your programs. + + #include "utstack.h" + +These macros support the basic operations of a stack, implemented as +an intrusive linked list. A stack supports the "push", "pop", and "count" +operations, as well as the trivial operation of getting the top element +of the stack. + +Download +~~~~~~~~ +To download the `utstack.h` header file, +follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file, +then look in the src/ sub-directory. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utstack' macros have been tested on: + + * Linux, + * Mac OS X, + * Windows, using Visual Studio 2008 and Visual Studio 2010 + +Usage +----- + +Stack (list) head +~~~~~~~~~~~~~~~~~ +The stack head is simply a pointer to your element structure. You can name it +anything. *It must be initialized to `NULL`*. It doubles as a pointer to the +top element of your stack. + + element *stack = NULL; + +Stack operations +~~~~~~~~~~~~~~~ +The only operations on a stack are O(1) pushing, O(1) popping, and +O(n) counting the number of elements on the stack. None of the provided +macros permit directly accessing stack elements other than the top element. + +To increase the readability of your code, you can use the macro +`STACK_EMPTY(head)` as a more readable alternative to `head == NULL`, +and `STACK_TOP(head)` as a more readable alternative to `head`. + +[width="100%",cols="50 +#include +#include +#include "utstack.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next; +} el; + +el *head = NULL; /* important- initialize to NULL! */ + +int main(int argc, char *argv[]) { + el *elt, *tmp; + + char linebuf[sizeof el->bname]; + int count; + FILE *file = fopen("test11.dat", "r"); + if (file == NULL) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf, sizeof linebuf, file) != NULL) { + el *name = malloc(sizeof *name); + if (name == NULL) exit(-1); + strcpy(name->bname, linebuf); + STACK_PUSH(head, name); + } + fclose(file); + + STACK_COUNT(head, elt, count); + printf("%d elements were read into the stack\n", count); + + /* now pop, print, and delete each element */ + while (!STACK_EMPTY(head)) { + printf("%s\n", STACK_TOP(head)->bname); + STACK_POP(head, elt); + free(elt); + } + + return 0; +} +-------------------------------------------------------------------------------- + +[[flex_names]] +Other names for next +~~~~~~~~~~~~~~~~~~~~ +If the element structure's `next` field is named something else, a separate group +of macros must be used. These work the same as the regular macros, but take the +field name as an extra parameter. + +These "flexible field name" macros are shown below. They all end with `2`. Each +operates the same as its counterpart without the `2`, but they take the name of +the `next` field as a trailing argument. + +[width="100%",cols="50 +v2.3.0, February 2021 + +Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page]. + +Introduction +------------ +A set of basic dynamic string macros for C programs are included with +uthash in `utstring.h`. To use these in your own C program, just copy +`utstring.h` into your source directory and use it in your programs. + + #include "utstring.h" + +The dynamic string supports operations such as inserting data, concatenation, +getting the length and content, substring search, and clear. It's ok to put +binary data into a utstring too. The string <> are +listed below. + +Some utstring operations are implemented as functions rather than macros. + +Download +~~~~~~~~ +To download the `utstring.h` header file, +follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file, +then look in the src/ sub-directory. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utstring' macros have been tested on: + + * Linux, + * Windows, using Visual Studio 2008 and Visual Studio 2010 + +Usage +----- + +Declaration +~~~~~~~~~~~ + +The dynamic string itself has the data type `UT_string`. It is declared like, + + UT_string *str; + +New and free +~~~~~~~~~~~~ +The next step is to create the string using `utstring_new`. Later when you're +done with it, `utstring_free` will free it and all its content. + +Manipulation +~~~~~~~~~~~~ +The `utstring_printf` or `utstring_bincpy` operations insert (copy) data into +the string. To concatenate one utstring to another, use `utstring_concat`. To +clear the content of the string, use `utstring_clear`. The length of the string +is available from `utstring_len`, and its content from `utstring_body`. This +evaluates to a `char*`. The buffer it points to is always null-terminated. +So, it can be used directly with external functions that expect a string. +This automatic null terminator is not counted in the length of the string. + +Samples +~~~~~~~ + +These examples show how to use utstring. + +.Sample 1 +------------------------------------------------------------------------------- +#include +#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; +} +------------------------------------------------------------------------------- + +The next example demonstrates that `utstring_printf` 'appends' to the string. +It also shows concatenation. + +.Sample 2 +------------------------------------------------------------------------------- +#include +#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", utstring_len(s)); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + utstring_free(t); + return 0; +} +------------------------------------------------------------------------------- + +The next example shows how binary data can be inserted into the string. It also +clears the string and prints new data into it. + +.Sample 3 +------------------------------------------------------------------------------- +#include +#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", utstring_len(s)); + + utstring_clear(s); + utstring_printf(s,"number %d", 10); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + return 0; +} +------------------------------------------------------------------------------- + +[[operations]] +Reference +--------- +These are the utstring operations. + +Operations +~~~~~~~~~~ + +[width="100%",cols="50 /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#ifdef __GNUC__ +#define UTARRAY_UNUSED __attribute__((__unused__)) +#else +#define UTARRAY_UNUSED +#endif + +#ifdef oom +#error "The name of macro 'oom' has been changed to 'utarray_oom'. Please update your code." +#define utarray_oom() oom() +#endif + +#ifndef utarray_oom +#define utarray_oom() exit(-1) +#endif + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd = *(_icd); \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + (a) = (UT_array*)malloc(sizeof(UT_array)); \ + if ((a) == NULL) { \ + utarray_oom(); \ + } \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+(by)) > (a)->n) { \ + char *utarray_tmp; \ + while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ + if (utarray_tmp == NULL) { \ + utarray_oom(); \ + } \ + (a)->d=utarray_tmp; \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) + +#define utarray_insert(a,p,j) do { \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,1); \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + unsigned _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + unsigned _ut_i; \ + if ((dst)->i > (unsigned)(num)) { \ + if ((dst)->icd.dtor) { \ + for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ + (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ + } \ + } \ + } else if ((dst)->i < (unsigned)(num)) { \ + utarray_reserve(dst, (num) - (dst)->i); \ + if ((dst)->icd.init) { \ + for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ + (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ + } \ + } \ + (dst)->i = (num); \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta(dst, src, utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ + } \ + } \ + if ((a)->i > ((pos) + (len))) { \ + memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ + ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new(a, u); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((a)->i != utarray_eltidx(a,e)+1) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) != 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) - (a)->d) / (a)->icd.sz) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char *const *srcc = (char *const *)src; + char **dstc = (char**)dst; + *dstc = (*srcc == NULL) ? NULL : strdup(*srcc); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc != NULL) free(*eltc); +} +static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/c/ext/uthash/src/uthash.h b/c/ext/uthash/src/uthash.h new file mode 100644 index 0000000..ac78fda --- /dev/null +++ b/c/ext/uthash/src/uthash.h @@ -0,0 +1,1136 @@ +/* +Copyright (c) 2003-2021, 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. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT +/* This codepath is provided for backward compatibility, but I plan to remove it. */ +#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead" +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/c/ext/uthash/src/utlist.h b/c/ext/uthash/src/utlist.h new file mode 100644 index 0000000..6230a67 --- /dev/null +++ b/c/ext/uthash/src/utlist.h @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2007-2021, 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. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.3.0 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ diff --git a/c/ext/uthash/src/utringbuffer.h b/c/ext/uthash/src/utringbuffer.h new file mode 100644 index 0000000..cf48131 --- /dev/null +++ b/c/ext/uthash/src/utringbuffer.h @@ -0,0 +1,108 @@ +/* +Copyright (c) 2015-2021, 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. +*/ + +/* a ring-buffer implementation using macros + */ +#ifndef UTRINGBUFFER_H +#define UTRINGBUFFER_H + +#define UTRINGBUFFER_VERSION 2.3.0 + +#include +#include +#include "utarray.h" // for "UT_icd" + +typedef struct { + unsigned i; /* index of next available slot; wraps at n */ + unsigned n; /* capacity */ + unsigned char f; /* full */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz */ +} UT_ringbuffer; + +#define utringbuffer_init(a, _n, _icd) do { \ + memset(a, 0, sizeof(UT_ringbuffer)); \ + (a)->icd = *(_icd); \ + (a)->n = (_n); \ + if ((a)->n) { (a)->d = (char*)malloc((a)->n * (_icd)->sz); } \ +} while(0) + +#define utringbuffer_clear(a) do { \ + if ((a)->icd.dtor) { \ + if ((a)->f) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (a)->n; ++_ut_i) { \ + (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ + } \ + } else { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (a)->i; ++_ut_i) { \ + (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ + } \ + } \ + } \ + (a)->i = 0; \ + (a)->f = 0; \ +} while(0) + +#define utringbuffer_done(a) do { \ + utringbuffer_clear(a); \ + free((a)->d); (a)->d = NULL; \ + (a)->n = 0; \ +} while(0) + +#define utringbuffer_new(a,n,_icd) do { \ + a = (UT_ringbuffer*)malloc(sizeof(UT_ringbuffer)); \ + utringbuffer_init(a, n, _icd); \ +} while(0) + +#define utringbuffer_free(a) do { \ + utringbuffer_done(a); \ + free(a); \ +} while(0) + +#define utringbuffer_push_back(a,p) do { \ + if ((a)->icd.dtor && (a)->f) { (a)->icd.dtor(_utringbuffer_internalptr(a,(a)->i)); } \ + if ((a)->icd.copy) { (a)->icd.copy( _utringbuffer_internalptr(a,(a)->i), p); } \ + else { memcpy(_utringbuffer_internalptr(a,(a)->i), p, (a)->icd.sz); }; \ + if (++(a)->i == (a)->n) { (a)->i = 0; (a)->f = 1; } \ +} while(0) + +#define utringbuffer_len(a) ((a)->f ? (a)->n : (a)->i) +#define utringbuffer_empty(a) ((a)->i == 0 && !(a)->f) +#define utringbuffer_full(a) ((a)->f != 0) + +#define _utringbuffer_real_idx(a,j) ((a)->f ? ((j) + (a)->i) % (a)->n : (j)) +#define _utringbuffer_internalptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) +#define utringbuffer_eltptr(a,j) ((0 <= (j) && (j) < utringbuffer_len(a)) ? _utringbuffer_internalptr(a,_utringbuffer_real_idx(a,j)) : NULL) + +#define _utringbuffer_fake_idx(a,j) ((a)->f ? ((j) + (a)->n - (a)->i) % (a)->n : (j)) +#define _utringbuffer_internalidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) +#define utringbuffer_eltidx(a,e) _utringbuffer_fake_idx(a, _utringbuffer_internalidx(a,e)) + +#define utringbuffer_front(a) utringbuffer_eltptr(a,0) +#define utringbuffer_next(a,e) ((e)==NULL ? utringbuffer_front(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)+1)) +#define utringbuffer_prev(a,e) ((e)==NULL ? utringbuffer_back(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)-1)) +#define utringbuffer_back(a) (utringbuffer_empty(a) ? NULL : utringbuffer_eltptr(a, utringbuffer_len(a) - 1)) + +#endif /* UTRINGBUFFER_H */ diff --git a/c/ext/uthash/src/utstack.h b/c/ext/uthash/src/utstack.h new file mode 100644 index 0000000..2dbfec8 --- /dev/null +++ b/c/ext/uthash/src/utstack.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2018-2021, 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. +*/ + +#ifndef UTSTACK_H +#define UTSTACK_H + +#define UTSTACK_VERSION 2.3.0 + +/* + * This file contains macros to manipulate a singly-linked list as a stack. + * + * To use utstack, your structure must have a "next" pointer. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *next; + * } + * + * struct item *stack = NULL: + * + * int main() { + * int count; + * struct item *tmp; + * struct item *item = malloc(sizeof *item); + * item->id = 42; + * STACK_COUNT(stack, tmp, count); assert(count == 0); + * STACK_PUSH(stack, item); + * STACK_COUNT(stack, tmp, count); assert(count == 1); + * STACK_POP(stack, item); + * free(item); + * STACK_COUNT(stack, tmp, count); assert(count == 0); + * } + * -------------------------------------------------- + */ + +#define STACK_TOP(head) (head) + +#define STACK_EMPTY(head) (!(head)) + +#define STACK_PUSH(head,add) \ + STACK_PUSH2(head,add,next) + +#define STACK_PUSH2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define STACK_POP(head,result) \ + STACK_POP2(head,result,next) + +#define STACK_POP2(head,result,next) \ +do { \ + (result) = (head); \ + (head) = (head)->next; \ +} while (0) + +#define STACK_COUNT(head,el,counter) \ + STACK_COUNT2(head,el,counter,next) \ + +#define STACK_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + for ((el) = (head); el; (el) = (el)->next) { ++(counter); } \ +} while (0) + +#endif /* UTSTACK_H */ diff --git a/c/ext/uthash/src/utstring.h b/c/ext/uthash/src/utstring.h new file mode 100644 index 0000000..009b4c8 --- /dev/null +++ b/c/ext/uthash/src/utstring.h @@ -0,0 +1,407 @@ +/* +Copyright (c) 2008-2021, 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. +*/ + +/* a dynamic string implementation using macros + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 2.3.0 + +#include +#include +#include +#include + +#ifdef __GNUC__ +#define UTSTRING_UNUSED __attribute__((__unused__)) +#else +#define UTSTRING_UNUSED +#endif + +#ifdef oom +#error "The name of macro 'oom' has been changed to 'utstring_oom'. Please update your code." +#define utstring_oom() oom() +#endif + +#ifndef utstring_oom +#define utstring_oom() exit(-1) +#endif + +typedef struct { + char *d; /* pointer to allocated buffer */ + size_t n; /* allocated capacity */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + char *utstring_tmp = (char*)realloc( \ + (s)->d, (s)->n + (amt)); \ + if (!utstring_tmp) { \ + utstring_oom(); \ + } \ + (s)->d = utstring_tmp; \ + (s)->n += (amt); \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + (s) = (UT_string*)malloc(sizeof(UT_string)); \ + if (!(s)) { \ + utstring_oom(); \ + } \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += (l); \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((s)->i) + +#define utstring_body(s) ((s)->d) + +UTSTRING_UNUSED static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + for (;;) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && ((size_t) n < (s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +#ifdef __GNUC__ +/* support printf format checking (2=the format string, 3=start of varargs) */ +static void utstring_printf(UT_string *s, const char *fmt, ...) + __attribute__ (( format( printf, 2, 3) )); +#endif +UTSTRING_UNUSED static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +/******************************************************************************* + * begin substring search functions * + ******************************************************************************/ +/* Build KMP table from left to right. */ +UTSTRING_UNUSED static void _utstring_BuildTable( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = 0; + j = i - 1; + P_KMP_Table[i] = j; + while (i < (long) P_NeedleLen) + { + while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j]; + } + i++; + j++; + if (i < (long) P_NeedleLen) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i] = P_KMP_Table[j]; + } + else + { + P_KMP_Table[i] = j; + } + } + else + { + P_KMP_Table[i] = j; + } + } + + return; +} + + +/* Build KMP table from right to left. */ +UTSTRING_UNUSED static void _utstring_BuildTableR( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = P_NeedleLen - 1; + j = i + 1; + P_KMP_Table[i + 1] = j; + while (i >= 0) + { + while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j + 1]; + } + i--; + j--; + if (i >= 0) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; + } + else + { + P_KMP_Table[i + 1] = j; + } + } + else + { + P_KMP_Table[i + 1] = j; + } + } + + return; +} + + +/* Search data from left to right. ( Multiple search mode. ) */ +UTSTRING_UNUSED static long _utstring_find( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from left to right. */ + i = j = 0; + while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) + { + while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i]; + } + i++; + j++; + if (i >= (int)P_NeedleLen) + { + /* Found. */ + V_FindPosition = j - i; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( Multiple search mode. ) */ +UTSTRING_UNUSED static long _utstring_findR( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from right to left. */ + j = (P_HaystackLen - 1); + i = (P_NeedleLen - 1); + while ( (j >= 0) && (j >= i) ) + { + while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i + 1]; + } + i--; + j--; + if (i < 0) + { + /* Found. */ + V_FindPosition = j + 1; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from left to right. ( One time search mode. ) */ +UTSTRING_UNUSED static long utstring_find( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = s->i - V_StartPosition; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_find(s->d + V_StartPosition, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + if (V_FindPosition >= 0) + { + V_FindPosition += V_StartPosition; + } + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( One time search mode. ) */ +UTSTRING_UNUSED static long utstring_findR( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = V_StartPosition + 1; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_findR(s->d, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} +/******************************************************************************* + * end substring search functions * + ******************************************************************************/ + +#endif /* UTSTRING_H */ diff --git a/c/ext/uthash/tests/Makefile b/c/ext/uthash/tests/Makefile new file mode 100644 index 0000000..31f0cc2 --- /dev/null +++ b/c/ext/uthash/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 test96 +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/c/ext/uthash/tests/README b/c/ext/uthash/tests/README new file mode 100644 index 0000000..a287de6 --- /dev/null +++ b/c/ext/uthash/tests/README @@ -0,0 +1,132 @@ +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 key-comparison 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 key-comparison and strlen macros +test89: test code from the tinydtls project +test90: regression-test HASH_ADD_KEYPTR_INORDER (IronBug) +test91: test LL_INSERT_INORDER etc. +test92: HASH_NONFATAL_OOM +test93: alt_fatal +test94: utlist with fields named other than 'next' and 'prev' +test95: utstack +test96: HASH_FUNCTION + HASH_KEYCMP + +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/c/ext/uthash/tests/all_funcs b/c/ext/uthash/tests/all_funcs new file mode 100755 index 0000000..f61a410 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/bloom_perf.c b/c/ext/uthash/tests/bloom_perf.c new file mode 100644 index 0000000..6a9abf1 --- /dev/null +++ b/c/ext/uthash/tests/bloom_perf.c @@ -0,0 +1,82 @@ +#include /* malloc */ +#include /* gettimeofday */ +#include /* perror */ +#include /* 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/c/ext/uthash/tests/bloom_perf.sh b/c/ext/uthash/tests/bloom_perf.sh new file mode 100755 index 0000000..0a04f23 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/do_tests b/c/ext/uthash/tests/do_tests new file mode 100755 index 0000000..574403f --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/do_tests.cygwin b/c/ext/uthash/tests/do_tests.cygwin new file mode 100755 index 0000000..0c01ebe --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/do_tests.mingw b/c/ext/uthash/tests/do_tests.mingw new file mode 100644 index 0000000..4dcf498 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/do_tests_win32.cmd b/c/ext/uthash/tests/do_tests_win32.cmd new file mode 100644 index 0000000..be89301 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/emit_keys.c b/c/ext/uthash/tests/emit_keys.c new file mode 100644 index 0000000..0b98689 --- /dev/null +++ b/c/ext/uthash/tests/emit_keys.c @@ -0,0 +1,48 @@ +#include /* malloc */ +#include /* perror */ +#include /* printf */ +#include /* 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/c/ext/uthash/tests/example.c b/c/ext/uthash/tests/example.c new file mode 100644 index 0000000..3248305 --- /dev/null +++ b/c/ext/uthash/tests/example.c @@ -0,0 +1,149 @@ +#include /* gets */ +#include /* atoi, malloc */ +#include /* 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/c/ext/uthash/tests/hashscan.c b/c/ext/uthash/tests/hashscan.c new file mode 100644 index 0000000..fc69924 --- /dev/null +++ b/c/ext/uthash/tests/hashscan.c @@ -0,0 +1,678 @@ +/* +Copyright (c) 2005-2021, 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 +#include +#include +#include +#include +#include +#include +#include /* on OSX, must come before ptrace.h */ +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include /* MAXPATHLEN */ +#include /* 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 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\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/c/ext/uthash/tests/keystats b/c/ext/uthash/tests/keystats new file mode 100755 index 0000000..9c6bf5b --- /dev/null +++ b/c/ext/uthash/tests/keystats @@ -0,0 +1,42 @@ +#!/usr/bin/perl + +use strict; + +use FindBin; + +sub usage { + print "usage: keystats [-v] keyfile\n"; + print "usage: keystats [-p [-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) { + $exe =~ s/\ /\\ /g; + $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/c/ext/uthash/tests/lru_cache/Makefile b/c/ext/uthash/tests/lru_cache/Makefile new file mode 100644 index 0000000..aa3a2cb --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/lru_cache/cache.c b/c/ext/uthash/tests/lru_cache/cache.c new file mode 100644 index 0000000..632c558 --- /dev/null +++ b/c/ext/uthash/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 +#include +#include +#include "cache.h" +#include "uthash.h" + +/** + * A cache entry + */ +struct foo_cache_entry { + char *key; /**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 pair into the cache + + @param cache + The cache object + + @param key + The key that identifies + + @param data + Data associated with + + @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/c/ext/uthash/tests/lru_cache/cache.h b/c/ext/uthash/tests/lru_cache/cache.h new file mode 100644 index 0000000..350576d --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/lru_cache/main.c b/c/ext/uthash/tests/lru_cache/main.c new file mode 100644 index 0000000..7f0eae2 --- /dev/null +++ b/c/ext/uthash/tests/lru_cache/main.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#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/c/ext/uthash/tests/simkeys.pl b/c/ext/uthash/tests/simkeys.pl new file mode 100755 index 0000000..acc5583 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/sleep_test.c b/c/ext/uthash/tests/sleep_test.c new file mode 100644 index 0000000..60e60d1 --- /dev/null +++ b/c/ext/uthash/tests/sleep_test.c @@ -0,0 +1,32 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ +#include /* 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/c/ext/uthash/tests/tdiff.cpp b/c/ext/uthash/tests/tdiff.cpp new file mode 100644 index 0000000..4be14fe --- /dev/null +++ b/c/ext/uthash/tests/tdiff.cpp @@ -0,0 +1,34 @@ +// Windows does not have unix diff so this is a simple replacement +#include +#include +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/c/ext/uthash/tests/test1.ans b/c/ext/uthash/tests/test1.ans new file mode 100644 index 0000000..0b08c48 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test1.c b/c/ext/uthash/tests/test1.c new file mode 100644 index 0000000..5e457ab --- /dev/null +++ b/c/ext/uthash/tests/test1.c @@ -0,0 +1,31 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test10.ans b/c/ext/uthash/tests/test10.ans new file mode 100644 index 0000000..b1a27a8 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test10.c b/c/ext/uthash/tests/test10.c new file mode 100644 index 0000000..edf80d4 --- /dev/null +++ b/c/ext/uthash/tests/test10.c @@ -0,0 +1,54 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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"); + + HASH_CLEAR(hh, users); + HASH_CLEAR(alth, altusers); + + return 0; +} diff --git a/c/ext/uthash/tests/test11.ans b/c/ext/uthash/tests/test11.ans new file mode 100644 index 0000000..2b72e98 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test11.c b/c/ext/uthash/tests/test11.c new file mode 100644 index 0000000..1460141 --- /dev/null +++ b/c/ext/uthash/tests/test11.c @@ -0,0 +1,57 @@ +#include "uthash.h" +#include /* malloc */ +#include /* perror */ +#include /* 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/c/ext/uthash/tests/test11.dat b/c/ext/uthash/tests/test11.dat new file mode 100644 index 0000000..bb6051b --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test12.ans b/c/ext/uthash/tests/test12.ans new file mode 100644 index 0000000..727f397 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test12.c b/c/ext/uthash/tests/test12.c new file mode 100644 index 0000000..1ed0d3b --- /dev/null +++ b/c/ext/uthash/tests/test12.c @@ -0,0 +1,40 @@ +#include "uthash.h" +#include +#include /* 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/c/ext/uthash/tests/test13.ans b/c/ext/uthash/tests/test13.ans new file mode 100644 index 0000000..baf3ff2 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test13.c b/c/ext/uthash/tests/test13.c new file mode 100644 index 0000000..eef7d02 --- /dev/null +++ b/c/ext/uthash/tests/test13.c @@ -0,0 +1,50 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test14.ans b/c/ext/uthash/tests/test14.ans new file mode 100644 index 0000000..77aeaeb --- /dev/null +++ b/c/ext/uthash/tests/test14.ans @@ -0,0 +1 @@ +lookup on 1219 of 1219 names succeeded diff --git a/c/ext/uthash/tests/test14.c b/c/ext/uthash/tests/test14.c new file mode 100644 index 0000000..cb26067 --- /dev/null +++ b/c/ext/uthash/tests/test14.c @@ -0,0 +1,54 @@ +#include "uthash.h" +#include /* malloc */ +#include /* perror */ +#include /* 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/c/ext/uthash/tests/test14.dat b/c/ext/uthash/tests/test14.dat new file mode 100644 index 0000000..ea0b4ea --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test15.ans b/c/ext/uthash/tests/test15.ans new file mode 100644 index 0000000..ad69a94 --- /dev/null +++ b/c/ext/uthash/tests/test15.ans @@ -0,0 +1 @@ +betty's id is 2 diff --git a/c/ext/uthash/tests/test15.c b/c/ext/uthash/tests/test15.c new file mode 100644 index 0000000..5614092 --- /dev/null +++ b/c/ext/uthash/tests/test15.c @@ -0,0 +1,40 @@ +#include /* strcpy */ +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test16.ans b/c/ext/uthash/tests/test16.ans new file mode 100644 index 0000000..1b4d7e1 --- /dev/null +++ b/c/ext/uthash/tests/test16.ans @@ -0,0 +1 @@ +found: user 5, unix time 157680000 diff --git a/c/ext/uthash/tests/test16.c b/c/ext/uthash/tests/test16.c new file mode 100644 index 0000000..f9e2f85 --- /dev/null +++ b/c/ext/uthash/tests/test16.c @@ -0,0 +1,53 @@ +#include /* malloc */ +#include /* offsetof */ +#include /* printf */ +#include /* 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/c/ext/uthash/tests/test17.ans b/c/ext/uthash/tests/test17.ans new file mode 100644 index 0000000..92ae3ef --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test17.c b/c/ext/uthash/tests/test17.c new file mode 100644 index 0000000..cb63001 --- /dev/null +++ b/c/ext/uthash/tests/test17.c @@ -0,0 +1,63 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test18.ans b/c/ext/uthash/tests/test18.ans new file mode 100644 index 0000000..db48dcf --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test18.c b/c/ext/uthash/tests/test18.c new file mode 100644 index 0000000..623cf56 --- /dev/null +++ b/c/ext/uthash/tests/test18.c @@ -0,0 +1,37 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test19.ans b/c/ext/uthash/tests/test19.ans new file mode 100644 index 0000000..588d0d9 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test19.c b/c/ext/uthash/tests/test19.c new file mode 100644 index 0000000..aec898f --- /dev/null +++ b/c/ext/uthash/tests/test19.c @@ -0,0 +1,66 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test2.ans b/c/ext/uthash/tests/test2.ans new file mode 100644 index 0000000..fd4e2f8 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test2.c b/c/ext/uthash/tests/test2.c new file mode 100644 index 0000000..30bbb2f --- /dev/null +++ b/c/ext/uthash/tests/test2.c @@ -0,0 +1,38 @@ +#include "uthash.h" +#include +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test20.ans b/c/ext/uthash/tests/test20.ans new file mode 100644 index 0000000..4d3bb1d --- /dev/null +++ b/c/ext/uthash/tests/test20.ans @@ -0,0 +1 @@ +found diff --git a/c/ext/uthash/tests/test20.c b/c/ext/uthash/tests/test20.c new file mode 100644 index 0000000..9ac01f0 --- /dev/null +++ b/c/ext/uthash/tests/test20.c @@ -0,0 +1,34 @@ +#include /* memcpy */ +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test21.ans b/c/ext/uthash/tests/test21.ans new file mode 100644 index 0000000..af89f48 --- /dev/null +++ b/c/ext/uthash/tests/test21.ans @@ -0,0 +1 @@ +found a 1 diff --git a/c/ext/uthash/tests/test21.c b/c/ext/uthash/tests/test21.c new file mode 100644 index 0000000..b4000df --- /dev/null +++ b/c/ext/uthash/tests/test21.c @@ -0,0 +1,44 @@ +#include +#include +#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/c/ext/uthash/tests/test22.ans b/c/ext/uthash/tests/test22.ans new file mode 100644 index 0000000..2483a24 --- /dev/null +++ b/c/ext/uthash/tests/test22.ans @@ -0,0 +1 @@ +found diff --git a/c/ext/uthash/tests/test22.c b/c/ext/uthash/tests/test22.c new file mode 100644 index 0000000..d013db9 --- /dev/null +++ b/c/ext/uthash/tests/test22.c @@ -0,0 +1,68 @@ +#include /* malloc */ +#include /* offsetof */ +#include /* printf */ +#include /* 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/c/ext/uthash/tests/test23.ans b/c/ext/uthash/tests/test23.ans new file mode 100644 index 0000000..79ed10a --- /dev/null +++ b/c/ext/uthash/tests/test23.ans @@ -0,0 +1,6 @@ +found 12345 +found 6789 +found 98765 +deleting 12345 +deleting 6789 +deleting 98765 diff --git a/c/ext/uthash/tests/test23.c b/c/ext/uthash/tests/test23.c new file mode 100644 index 0000000..132da42 --- /dev/null +++ b/c/ext/uthash/tests/test23.c @@ -0,0 +1,69 @@ +#include +#include +#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/c/ext/uthash/tests/test24.ans b/c/ext/uthash/tests/test24.ans new file mode 100644 index 0000000..4a2034a --- /dev/null +++ b/c/ext/uthash/tests/test24.ans @@ -0,0 +1 @@ +hash contains 10 items diff --git a/c/ext/uthash/tests/test24.c b/c/ext/uthash/tests/test24.c new file mode 100644 index 0000000..459d6bd --- /dev/null +++ b/c/ext/uthash/tests/test24.c @@ -0,0 +1,29 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test25.ans b/c/ext/uthash/tests/test25.ans new file mode 100644 index 0000000..9490482 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test25.c b/c/ext/uthash/tests/test25.c new file mode 100644 index 0000000..22e3761 --- /dev/null +++ b/c/ext/uthash/tests/test25.c @@ -0,0 +1,138 @@ +#include +#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/c/ext/uthash/tests/test26.ans b/c/ext/uthash/tests/test26.ans new file mode 100644 index 0000000..764b01d --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test26.c b/c/ext/uthash/tests/test26.c new file mode 100644 index 0000000..6e9d962 --- /dev/null +++ b/c/ext/uthash/tests/test26.c @@ -0,0 +1,61 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test27.ans b/c/ext/uthash/tests/test27.ans new file mode 100644 index 0000000..80b476c --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test27.c b/c/ext/uthash/tests/test27.c new file mode 100644 index 0000000..73842ee --- /dev/null +++ b/c/ext/uthash/tests/test27.c @@ -0,0 +1,130 @@ +#include +#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/c/ext/uthash/tests/test28.ans b/c/ext/uthash/tests/test28.ans new file mode 100644 index 0000000..eb88650 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test28.c b/c/ext/uthash/tests/test28.c new file mode 100644 index 0000000..da32c08 --- /dev/null +++ b/c/ext/uthash/tests/test28.c @@ -0,0 +1,153 @@ +#include +#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/c/ext/uthash/tests/test29.ans b/c/ext/uthash/tests/test29.ans new file mode 100644 index 0000000..6467b60 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test29.c b/c/ext/uthash/tests/test29.c new file mode 100644 index 0000000..e579a0e --- /dev/null +++ b/c/ext/uthash/tests/test29.c @@ -0,0 +1,57 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test3.ans b/c/ext/uthash/tests/test3.ans new file mode 100644 index 0000000..56a52a3 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test3.c b/c/ext/uthash/tests/test3.c new file mode 100644 index 0000000..630b8b3 --- /dev/null +++ b/c/ext/uthash/tests/test3.c @@ -0,0 +1,43 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test30.ans b/c/ext/uthash/tests/test30.ans new file mode 100644 index 0000000..2b72e98 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test30.c b/c/ext/uthash/tests/test30.c new file mode 100644 index 0000000..58bfb73 --- /dev/null +++ b/c/ext/uthash/tests/test30.c @@ -0,0 +1,50 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test31.ans b/c/ext/uthash/tests/test31.ans new file mode 100644 index 0000000..2b72e98 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test31.c b/c/ext/uthash/tests/test31.c new file mode 100644 index 0000000..58bfb73 --- /dev/null +++ b/c/ext/uthash/tests/test31.c @@ -0,0 +1,50 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test32.ans b/c/ext/uthash/tests/test32.ans new file mode 100644 index 0000000..2a4d7f7 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test32.c b/c/ext/uthash/tests/test32.c new file mode 100644 index 0000000..f861b2a --- /dev/null +++ b/c/ext/uthash/tests/test32.c @@ -0,0 +1,43 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test33.ans b/c/ext/uthash/tests/test33.ans new file mode 100644 index 0000000..2b72e98 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test33.c b/c/ext/uthash/tests/test33.c new file mode 100644 index 0000000..54cc311 --- /dev/null +++ b/c/ext/uthash/tests/test33.c @@ -0,0 +1,50 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test34.ans b/c/ext/uthash/tests/test34.ans new file mode 100644 index 0000000..2a4d7f7 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test34.c b/c/ext/uthash/tests/test34.c new file mode 100644 index 0000000..59377a0 --- /dev/null +++ b/c/ext/uthash/tests/test34.c @@ -0,0 +1,43 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test35.ans b/c/ext/uthash/tests/test35.ans new file mode 100644 index 0000000..b33c808 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test35.c b/c/ext/uthash/tests/test35.c new file mode 100644 index 0000000..a9c82b9 --- /dev/null +++ b/c/ext/uthash/tests/test35.c @@ -0,0 +1,37 @@ +#include "uthash.h" +#include /* strcpy */ +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test36.ans b/c/ext/uthash/tests/test36.ans new file mode 100644 index 0000000..4ea9873 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test36.c b/c/ext/uthash/tests/test36.c new file mode 100644 index 0000000..5927d94 --- /dev/null +++ b/c/ext/uthash/tests/test36.c @@ -0,0 +1,60 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test37.ans b/c/ext/uthash/tests/test37.ans new file mode 100644 index 0000000..f42a945 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test37.c b/c/ext/uthash/tests/test37.c new file mode 100644 index 0000000..ea5209f --- /dev/null +++ b/c/ext/uthash/tests/test37.c @@ -0,0 +1,56 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test38.ans b/c/ext/uthash/tests/test38.ans new file mode 100644 index 0000000..b20e04b --- /dev/null +++ b/c/ext/uthash/tests/test38.ans @@ -0,0 +1 @@ +hash count 10 diff --git a/c/ext/uthash/tests/test38.c b/c/ext/uthash/tests/test38.c new file mode 100644 index 0000000..dc9dae9 --- /dev/null +++ b/c/ext/uthash/tests/test38.c @@ -0,0 +1,31 @@ +#include "uthash.h" +#include +#include + +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/c/ext/uthash/tests/test39.ans b/c/ext/uthash/tests/test39.ans new file mode 100644 index 0000000..a7e7e6c --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test39.c b/c/ext/uthash/tests/test39.c new file mode 100644 index 0000000..18a0af8 --- /dev/null +++ b/c/ext/uthash/tests/test39.c @@ -0,0 +1,34 @@ +#include +#include +#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/c/ext/uthash/tests/test4.ans b/c/ext/uthash/tests/test4.ans new file mode 100644 index 0000000..19b148f --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test4.c b/c/ext/uthash/tests/test4.c new file mode 100644 index 0000000..fe9bcb4 --- /dev/null +++ b/c/ext/uthash/tests/test4.c @@ -0,0 +1,33 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test40.ans b/c/ext/uthash/tests/test40.ans new file mode 100644 index 0000000..ad69a94 --- /dev/null +++ b/c/ext/uthash/tests/test40.ans @@ -0,0 +1 @@ +betty's id is 2 diff --git a/c/ext/uthash/tests/test40.c b/c/ext/uthash/tests/test40.c new file mode 100644 index 0000000..af75461 --- /dev/null +++ b/c/ext/uthash/tests/test40.c @@ -0,0 +1,40 @@ +#include /* strcpy */ +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test41.ans b/c/ext/uthash/tests/test41.ans new file mode 100644 index 0000000..f19e34b --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test41.c b/c/ext/uthash/tests/test41.c new file mode 100644 index 0000000..88a1a96 --- /dev/null +++ b/c/ext/uthash/tests/test41.c @@ -0,0 +1,72 @@ +#include +#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/c/ext/uthash/tests/test42.ans b/c/ext/uthash/tests/test42.ans new file mode 100644 index 0000000..581660e --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test42.c b/c/ext/uthash/tests/test42.c new file mode 100644 index 0000000..4da8f03 --- /dev/null +++ b/c/ext/uthash/tests/test42.c @@ -0,0 +1,90 @@ +#include +#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/c/ext/uthash/tests/test43.ans b/c/ext/uthash/tests/test43.ans new file mode 100644 index 0000000..d09e9bd --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test43.c b/c/ext/uthash/tests/test43.c new file mode 100644 index 0000000..216f529 --- /dev/null +++ b/c/ext/uthash/tests/test43.c @@ -0,0 +1,130 @@ +#include +#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/c/ext/uthash/tests/test44.ans b/c/ext/uthash/tests/test44.ans new file mode 100644 index 0000000..f771df8 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test44.c b/c/ext/uthash/tests/test44.c new file mode 100644 index 0000000..042fc4c --- /dev/null +++ b/c/ext/uthash/tests/test44.c @@ -0,0 +1,66 @@ +#include +#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/c/ext/uthash/tests/test45.ans b/c/ext/uthash/tests/test45.ans new file mode 100644 index 0000000..7e744bc --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test45.c b/c/ext/uthash/tests/test45.c new file mode 100644 index 0000000..b0633f2 --- /dev/null +++ b/c/ext/uthash/tests/test45.c @@ -0,0 +1,36 @@ +#include +#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/c/ext/uthash/tests/test46.ans b/c/ext/uthash/tests/test46.ans new file mode 100644 index 0000000..be625f3 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test46.c b/c/ext/uthash/tests/test46.c new file mode 100644 index 0000000..b3ac637 --- /dev/null +++ b/c/ext/uthash/tests/test46.c @@ -0,0 +1,84 @@ +#include +#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/c/ext/uthash/tests/test47.ans b/c/ext/uthash/tests/test47.ans new file mode 100644 index 0000000..546e064 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test47.c b/c/ext/uthash/tests/test47.c new file mode 100644 index 0000000..620c46e --- /dev/null +++ b/c/ext/uthash/tests/test47.c @@ -0,0 +1,29 @@ +#include /* 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/c/ext/uthash/tests/test48.ans b/c/ext/uthash/tests/test48.ans new file mode 100644 index 0000000..8b1acc1 --- /dev/null +++ b/c/ext/uthash/tests/test48.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/c/ext/uthash/tests/test48.c b/c/ext/uthash/tests/test48.c new file mode 100644 index 0000000..9e634c8 --- /dev/null +++ b/c/ext/uthash/tests/test48.c @@ -0,0 +1,23 @@ +#include +#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/c/ext/uthash/tests/test49.ans b/c/ext/uthash/tests/test49.ans new file mode 100644 index 0000000..94954ab --- /dev/null +++ b/c/ext/uthash/tests/test49.ans @@ -0,0 +1,2 @@ +hello +world diff --git a/c/ext/uthash/tests/test49.c b/c/ext/uthash/tests/test49.c new file mode 100644 index 0000000..972f013 --- /dev/null +++ b/c/ext/uthash/tests/test49.c @@ -0,0 +1,23 @@ +#include +#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/c/ext/uthash/tests/test5.ans b/c/ext/uthash/tests/test5.ans new file mode 100644 index 0000000..2ece88f --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test5.c b/c/ext/uthash/tests/test5.c new file mode 100644 index 0000000..95acdc5 --- /dev/null +++ b/c/ext/uthash/tests/test5.c @@ -0,0 +1,40 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test50.ans b/c/ext/uthash/tests/test50.ans new file mode 100644 index 0000000..1191247 --- /dev/null +++ b/c/ext/uthash/tests/test50.ans @@ -0,0 +1,2 @@ +1 +2 diff --git a/c/ext/uthash/tests/test50.c b/c/ext/uthash/tests/test50.c new file mode 100644 index 0000000..82e6be5 --- /dev/null +++ b/c/ext/uthash/tests/test50.c @@ -0,0 +1,23 @@ +#include +#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/c/ext/uthash/tests/test51.ans b/c/ext/uthash/tests/test51.ans new file mode 100644 index 0000000..3caf269 --- /dev/null +++ b/c/ext/uthash/tests/test51.ans @@ -0,0 +1,2 @@ +1 2 +10 20 diff --git a/c/ext/uthash/tests/test51.c b/c/ext/uthash/tests/test51.c new file mode 100644 index 0000000..92e7889 --- /dev/null +++ b/c/ext/uthash/tests/test51.c @@ -0,0 +1,32 @@ +#include +#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/c/ext/uthash/tests/test52.ans b/c/ext/uthash/tests/test52.ans new file mode 100644 index 0000000..bd37407 --- /dev/null +++ b/c/ext/uthash/tests/test52.ans @@ -0,0 +1,2 @@ +1 hello +2 world diff --git a/c/ext/uthash/tests/test52.c b/c/ext/uthash/tests/test52.c new file mode 100644 index 0000000..3de12e4 --- /dev/null +++ b/c/ext/uthash/tests/test52.c @@ -0,0 +1,48 @@ +#include +#include +#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/c/ext/uthash/tests/test53.ans b/c/ext/uthash/tests/test53.ans new file mode 100644 index 0000000..a042389 --- /dev/null +++ b/c/ext/uthash/tests/test53.ans @@ -0,0 +1 @@ +hello world! diff --git a/c/ext/uthash/tests/test53.c b/c/ext/uthash/tests/test53.c new file mode 100644 index 0000000..b30ee56 --- /dev/null +++ b/c/ext/uthash/tests/test53.c @@ -0,0 +1,14 @@ +#include +#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/c/ext/uthash/tests/test54.ans b/c/ext/uthash/tests/test54.ans new file mode 100644 index 0000000..df0083e --- /dev/null +++ b/c/ext/uthash/tests/test54.ans @@ -0,0 +1,2 @@ +length: 21 +hello world hi there diff --git a/c/ext/uthash/tests/test54.c b/c/ext/uthash/tests/test54.c new file mode 100644 index 0000000..5faaa9e --- /dev/null +++ b/c/ext/uthash/tests/test54.c @@ -0,0 +1,24 @@ +#include +#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/c/ext/uthash/tests/test55.ans b/c/ext/uthash/tests/test55.ans new file mode 100644 index 0000000..a75e7be --- /dev/null +++ b/c/ext/uthash/tests/test55.ans @@ -0,0 +1,2 @@ +length is 3 +number 10 diff --git a/c/ext/uthash/tests/test55.c b/c/ext/uthash/tests/test55.c new file mode 100644 index 0000000..95d8c01 --- /dev/null +++ b/c/ext/uthash/tests/test55.c @@ -0,0 +1,19 @@ +#include +#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/c/ext/uthash/tests/test56.ans b/c/ext/uthash/tests/test56.ans new file mode 100644 index 0000000..f615d00 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test56.c b/c/ext/uthash/tests/test56.c new file mode 100644 index 0000000..e4a0dce --- /dev/null +++ b/c/ext/uthash/tests/test56.c @@ -0,0 +1,98 @@ +#include /* malloc */ +#include /* printf */ +#include +#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/c/ext/uthash/tests/test57.ans b/c/ext/uthash/tests/test57.ans new file mode 100644 index 0000000..4d3bb1d --- /dev/null +++ b/c/ext/uthash/tests/test57.ans @@ -0,0 +1 @@ +found diff --git a/c/ext/uthash/tests/test57.c b/c/ext/uthash/tests/test57.c new file mode 100644 index 0000000..3b89fb5 --- /dev/null +++ b/c/ext/uthash/tests/test57.c @@ -0,0 +1,32 @@ +#include +#include +#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/c/ext/uthash/tests/test58.ans b/c/ext/uthash/tests/test58.ans new file mode 100644 index 0000000..a23ca72 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test58.c b/c/ext/uthash/tests/test58.c new file mode 100644 index 0000000..8f5683c --- /dev/null +++ b/c/ext/uthash/tests/test58.c @@ -0,0 +1,60 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test59.ans b/c/ext/uthash/tests/test59.ans new file mode 100644 index 0000000..e19f2f6 --- /dev/null +++ b/c/ext/uthash/tests/test59.ans @@ -0,0 +1 @@ +$items{bob}{age} = 37 diff --git a/c/ext/uthash/tests/test59.c b/c/ext/uthash/tests/test59.c new file mode 100644 index 0000000..f353ae1 --- /dev/null +++ b/c/ext/uthash/tests/test59.c @@ -0,0 +1,57 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test6.ans b/c/ext/uthash/tests/test6.ans new file mode 100644 index 0000000..e69de29 diff --git a/c/ext/uthash/tests/test6.c b/c/ext/uthash/tests/test6.c new file mode 100644 index 0000000..919afa8 --- /dev/null +++ b/c/ext/uthash/tests/test6.c @@ -0,0 +1,122 @@ +#include "uthash.h" +#include +#include +#include +#include + +/* Set up macros for alternative malloc/free functions */ +#undef uthash_malloc +#undef uthash_free +#undef uthash_strlen +#undef uthash_bzero +#define uthash_malloc(sz) alt_malloc(sz) +#define uthash_free(ptr,sz) alt_free(ptr,sz) +#define uthash_strlen(s) ..fail_to_compile.. +#define uthash_bzero(a,n) alt_bzero(a,n) + +#undef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) alt_keycmp(a,b,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_keycmp_count = 0; +static int alt_keycmp(const void *a, const void *b, size_t n) +{ + ++alt_keycmp_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_keycmp_count == 10); + assert(alt_malloc_balance == 0); + return 0; +} diff --git a/c/ext/uthash/tests/test60.ans b/c/ext/uthash/tests/test60.ans new file mode 100644 index 0000000..e19f2f6 --- /dev/null +++ b/c/ext/uthash/tests/test60.ans @@ -0,0 +1 @@ +$items{bob}{age} = 37 diff --git a/c/ext/uthash/tests/test60.c b/c/ext/uthash/tests/test60.c new file mode 100644 index 0000000..138c0da --- /dev/null +++ b/c/ext/uthash/tests/test60.c @@ -0,0 +1,51 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test61.ans b/c/ext/uthash/tests/test61.ans new file mode 100644 index 0000000..39c63b4 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test61.c b/c/ext/uthash/tests/test61.c new file mode 100644 index 0000000..bbb3d10 --- /dev/null +++ b/c/ext/uthash/tests/test61.c @@ -0,0 +1,53 @@ +#include +#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/c/ext/uthash/tests/test62.ans b/c/ext/uthash/tests/test62.ans new file mode 100644 index 0000000..e69de29 diff --git a/c/ext/uthash/tests/test62.c b/c/ext/uthash/tests/test62.c new file mode 100644 index 0000000..ad8ce45 --- /dev/null +++ b/c/ext/uthash/tests/test62.c @@ -0,0 +1,80 @@ + +#include +#include + +#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/c/ext/uthash/tests/test63.ans b/c/ext/uthash/tests/test63.ans new file mode 100644 index 0000000..c570c50 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test63.c b/c/ext/uthash/tests/test63.c new file mode 100644 index 0000000..e41f695 --- /dev/null +++ b/c/ext/uthash/tests/test63.c @@ -0,0 +1,67 @@ +#include +#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/c/ext/uthash/tests/test64.ans b/c/ext/uthash/tests/test64.ans new file mode 100644 index 0000000..1640241 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test64.c b/c/ext/uthash/tests/test64.c new file mode 100644 index 0000000..ec5fba8 --- /dev/null +++ b/c/ext/uthash/tests/test64.c @@ -0,0 +1,67 @@ +#include +#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/c/ext/uthash/tests/test65.ans b/c/ext/uthash/tests/test65.ans new file mode 100644 index 0000000..5312b22 --- /dev/null +++ b/c/ext/uthash/tests/test65.ans @@ -0,0 +1,4 @@ +LRU deleting JOHN + 0 +LRU deleting WILLIAM + 1 diff --git a/c/ext/uthash/tests/test65.c b/c/ext/uthash/tests/test65.c new file mode 100644 index 0000000..c863d21 --- /dev/null +++ b/c/ext/uthash/tests/test65.c @@ -0,0 +1,65 @@ +#include +#include +#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/c/ext/uthash/tests/test65.dat b/c/ext/uthash/tests/test65.dat new file mode 100644 index 0000000..bb6051b --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test66.ans b/c/ext/uthash/tests/test66.ans new file mode 100644 index 0000000..727f397 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test66.c b/c/ext/uthash/tests/test66.c new file mode 100644 index 0000000..e7ebe43 --- /dev/null +++ b/c/ext/uthash/tests/test66.c @@ -0,0 +1,43 @@ +#include "uthash.h" +#include +#include /* 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/c/ext/uthash/tests/test67.ans b/c/ext/uthash/tests/test67.ans new file mode 100644 index 0000000..cd70769 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test67.c b/c/ext/uthash/tests/test67.c new file mode 100644 index 0000000..6602c7b --- /dev/null +++ b/c/ext/uthash/tests/test67.c @@ -0,0 +1,30 @@ +#include +#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/c/ext/uthash/tests/test68.ans b/c/ext/uthash/tests/test68.ans new file mode 100644 index 0000000..704cdc5 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test68.c b/c/ext/uthash/tests/test68.c new file mode 100644 index 0000000..090f7d0 --- /dev/null +++ b/c/ext/uthash/tests/test68.c @@ -0,0 +1,87 @@ +#include +#include +#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/c/ext/uthash/tests/test69.ans b/c/ext/uthash/tests/test69.ans new file mode 100644 index 0000000..fba8b64 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test69.c b/c/ext/uthash/tests/test69.c new file mode 100644 index 0000000..bc851a5 --- /dev/null +++ b/c/ext/uthash/tests/test69.c @@ -0,0 +1,92 @@ +#include +#include +#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/c/ext/uthash/tests/test7.ans b/c/ext/uthash/tests/test7.ans new file mode 100644 index 0000000..e69de29 diff --git a/c/ext/uthash/tests/test7.c b/c/ext/uthash/tests/test7.c new file mode 100644 index 0000000..01ce547 --- /dev/null +++ b/c/ext/uthash/tests/test7.c @@ -0,0 +1,44 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test70.ans b/c/ext/uthash/tests/test70.ans new file mode 100644 index 0000000..00298ec --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test70.c b/c/ext/uthash/tests/test70.c new file mode 100644 index 0000000..abddea1 --- /dev/null +++ b/c/ext/uthash/tests/test70.c @@ -0,0 +1,87 @@ +#include +#include +#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/c/ext/uthash/tests/test71.ans b/c/ext/uthash/tests/test71.ans new file mode 100644 index 0000000..81d6b53 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test71.c b/c/ext/uthash/tests/test71.c new file mode 100644 index 0000000..8cda499 --- /dev/null +++ b/c/ext/uthash/tests/test71.c @@ -0,0 +1,92 @@ +#include +#include +#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/c/ext/uthash/tests/test72.ans b/c/ext/uthash/tests/test72.ans new file mode 100644 index 0000000..b5ccb14 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test72.c b/c/ext/uthash/tests/test72.c new file mode 100644 index 0000000..61ae11d --- /dev/null +++ b/c/ext/uthash/tests/test72.c @@ -0,0 +1,87 @@ +#include +#include +#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/c/ext/uthash/tests/test73.ans b/c/ext/uthash/tests/test73.ans new file mode 100644 index 0000000..2aa7bdc --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test73.c b/c/ext/uthash/tests/test73.c new file mode 100644 index 0000000..ac7e785 --- /dev/null +++ b/c/ext/uthash/tests/test73.c @@ -0,0 +1,92 @@ +#include +#include +#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/c/ext/uthash/tests/test74.ans b/c/ext/uthash/tests/test74.ans new file mode 100644 index 0000000..79a16ba --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test74.c b/c/ext/uthash/tests/test74.c new file mode 100644 index 0000000..6cb0352 --- /dev/null +++ b/c/ext/uthash/tests/test74.c @@ -0,0 +1,40 @@ +#include /* 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/c/ext/uthash/tests/test75.ans b/c/ext/uthash/tests/test75.ans new file mode 100644 index 0000000..8261ba3 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test75.c b/c/ext/uthash/tests/test75.c new file mode 100644 index 0000000..fa3aded --- /dev/null +++ b/c/ext/uthash/tests/test75.c @@ -0,0 +1,40 @@ +#include /* 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/c/ext/uthash/tests/test76.ans b/c/ext/uthash/tests/test76.ans new file mode 100644 index 0000000..79a16ba --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test76.c b/c/ext/uthash/tests/test76.c new file mode 100644 index 0000000..ee890f6 --- /dev/null +++ b/c/ext/uthash/tests/test76.c @@ -0,0 +1,51 @@ +#include /* 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 = 0; + size_t V_FindCnt = 0; + + + 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); + + 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/c/ext/uthash/tests/test77.ans b/c/ext/uthash/tests/test77.ans new file mode 100644 index 0000000..3b2a109 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test77.c b/c/ext/uthash/tests/test77.c new file mode 100644 index 0000000..dc55cb2 --- /dev/null +++ b/c/ext/uthash/tests/test77.c @@ -0,0 +1,72 @@ +#include /* 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 = 0; + + + 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_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/c/ext/uthash/tests/test78.ans b/c/ext/uthash/tests/test78.ans new file mode 100644 index 0000000..5c71cdf --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test78.c b/c/ext/uthash/tests/test78.c new file mode 100644 index 0000000..02424d5 --- /dev/null +++ b/c/ext/uthash/tests/test78.c @@ -0,0 +1,130 @@ +#include +#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/c/ext/uthash/tests/test79.ans b/c/ext/uthash/tests/test79.ans new file mode 100644 index 0000000..c8737e4 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test79.c b/c/ext/uthash/tests/test79.c new file mode 100644 index 0000000..4abbe94 --- /dev/null +++ b/c/ext/uthash/tests/test79.c @@ -0,0 +1,71 @@ +#include +#include +#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/c/ext/uthash/tests/test8.ans b/c/ext/uthash/tests/test8.ans new file mode 100644 index 0000000..9d28857 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test8.c b/c/ext/uthash/tests/test8.c new file mode 100644 index 0000000..3497101 --- /dev/null +++ b/c/ext/uthash/tests/test8.c @@ -0,0 +1,40 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test80.ans b/c/ext/uthash/tests/test80.ans new file mode 100644 index 0000000..763f637 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test80.c b/c/ext/uthash/tests/test80.c new file mode 100644 index 0000000..293fac8 --- /dev/null +++ b/c/ext/uthash/tests/test80.c @@ -0,0 +1,29 @@ +#include +#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/c/ext/uthash/tests/test81.ans b/c/ext/uthash/tests/test81.ans new file mode 100644 index 0000000..24adbd9 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test81.c b/c/ext/uthash/tests/test81.c new file mode 100644 index 0000000..9049437 --- /dev/null +++ b/c/ext/uthash/tests/test81.c @@ -0,0 +1,29 @@ +#include +#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/c/ext/uthash/tests/test82.ans b/c/ext/uthash/tests/test82.ans new file mode 100644 index 0000000..ce8f5b5 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test82.c b/c/ext/uthash/tests/test82.c new file mode 100644 index 0000000..3a5d05f --- /dev/null +++ b/c/ext/uthash/tests/test82.c @@ -0,0 +1,42 @@ +#include +#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/c/ext/uthash/tests/test83.ans b/c/ext/uthash/tests/test83.ans new file mode 100644 index 0000000..ada2c44 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test83.c b/c/ext/uthash/tests/test83.c new file mode 100644 index 0000000..b2140c5 --- /dev/null +++ b/c/ext/uthash/tests/test83.c @@ -0,0 +1,61 @@ +#include "uthash.h" +#include +#include /* 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/c/ext/uthash/tests/test84.ans b/c/ext/uthash/tests/test84.ans new file mode 100644 index 0000000..ada2c44 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test84.c b/c/ext/uthash/tests/test84.c new file mode 100644 index 0000000..cbbfded --- /dev/null +++ b/c/ext/uthash/tests/test84.c @@ -0,0 +1,71 @@ +#include "uthash.h" +#include +#include /* 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/c/ext/uthash/tests/test85.ans b/c/ext/uthash/tests/test85.ans new file mode 100644 index 0000000..1a0715f --- /dev/null +++ b/c/ext/uthash/tests/test85.ans @@ -0,0 +1,2 @@ +overhead non-zero +overhead zero diff --git a/c/ext/uthash/tests/test85.c b/c/ext/uthash/tests/test85.c new file mode 100644 index 0000000..bb62ca6 --- /dev/null +++ b/c/ext/uthash/tests/test85.c @@ -0,0 +1,35 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test86.ans b/c/ext/uthash/tests/test86.ans new file mode 100644 index 0000000..6533053 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test86.c b/c/ext/uthash/tests/test86.c new file mode 100644 index 0000000..fb5723f --- /dev/null +++ b/c/ext/uthash/tests/test86.c @@ -0,0 +1,296 @@ +#include +#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/c/ext/uthash/tests/test87.ans b/c/ext/uthash/tests/test87.ans new file mode 100644 index 0000000..32fc95e --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test87.c b/c/ext/uthash/tests/test87.c new file mode 100644 index 0000000..c17275e --- /dev/null +++ b/c/ext/uthash/tests/test87.c @@ -0,0 +1,116 @@ +#include +#include +#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/c/ext/uthash/tests/test88.ans b/c/ext/uthash/tests/test88.ans new file mode 100644 index 0000000..7d01294 --- /dev/null +++ b/c/ext/uthash/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_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp +alt_strlen +alt_keycmp diff --git a/c/ext/uthash/tests/test88.c b/c/ext/uthash/tests/test88.c new file mode 100644 index 0000000..d7a454a --- /dev/null +++ b/c/ext/uthash/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 /* malloc */ +#include /* printf */ + +/* This is mostly a copy of test6.c. */ + +#undef HASH_KEYCMP +#undef uthash_strlen +#define HASH_KEYCMP(a,b,n) alt_keycmp(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_keycmp(const void *a, const void *b, size_t n) +{ + puts("alt_keycmp"); + 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/c/ext/uthash/tests/test89.ans b/c/ext/uthash/tests/test89.ans new file mode 100644 index 0000000..8e63ada --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test89.c b/c/ext/uthash/tests/test89.c new file mode 100644 index 0000000..74d7ec4 --- /dev/null +++ b/c/ext/uthash/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 +#include +#include + +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/c/ext/uthash/tests/test9.ans b/c/ext/uthash/tests/test9.ans new file mode 100644 index 0000000..13a9402 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test9.c b/c/ext/uthash/tests/test9.c new file mode 100644 index 0000000..93dd7db --- /dev/null +++ b/c/ext/uthash/tests/test9.c @@ -0,0 +1,37 @@ +#include "uthash.h" +#include /* malloc */ +#include /* 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/c/ext/uthash/tests/test90.ans b/c/ext/uthash/tests/test90.ans new file mode 100644 index 0000000..e9d17d7 --- /dev/null +++ b/c/ext/uthash/tests/test90.ans @@ -0,0 +1,2 @@ +filling in is ok +cleanup is ok diff --git a/c/ext/uthash/tests/test90.c b/c/ext/uthash/tests/test90.c new file mode 100644 index 0000000..6fce121 --- /dev/null +++ b/c/ext/uthash/tests/test90.c @@ -0,0 +1,53 @@ +#include +#include +#include +#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/c/ext/uthash/tests/test91.ans b/c/ext/uthash/tests/test91.ans new file mode 100644 index 0000000..6f21be0 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test91.c b/c/ext/uthash/tests/test91.c new file mode 100644 index 0000000..8c8f63e --- /dev/null +++ b/c/ext/uthash/tests/test91.c @@ -0,0 +1,54 @@ +#include +#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/c/ext/uthash/tests/test92.ans b/c/ext/uthash/tests/test92.ans new file mode 100644 index 0000000..528694d --- /dev/null +++ b/c/ext/uthash/tests/test92.ans @@ -0,0 +1 @@ +End diff --git a/c/ext/uthash/tests/test92.c b/c/ext/uthash/tests/test92.c new file mode 100644 index 0000000..1068194 --- /dev/null +++ b/c/ext/uthash/tests/test92.c @@ -0,0 +1,245 @@ +#include + +#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; ihh.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/c/ext/uthash/tests/test93.ans b/c/ext/uthash/tests/test93.ans new file mode 100644 index 0000000..528694d --- /dev/null +++ b/c/ext/uthash/tests/test93.ans @@ -0,0 +1 @@ +End diff --git a/c/ext/uthash/tests/test93.c b/c/ext/uthash/tests/test93.c new file mode 100644 index 0000000..81103a1 --- /dev/null +++ b/c/ext/uthash/tests/test93.c @@ -0,0 +1,116 @@ +#include +#include + +#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 void init_users(int need_malloc_cnt) { + users = NULL; + example_user_t * user = (example_user_t*)malloc(sizeof(example_user_t)); + user->id = user_id; + is_fatal = 0; + malloc_cnt = need_malloc_cnt; + if (!setjmp(j_buf)) { + HASH_ADD_INT(users, id, user); + } else { + free(user); + } +} + +int main() +{ + example_user_t *user; + + init_users(3); /* bloom filter must fail */ + if (!is_fatal) { + printf("fatal not called after bloom failure\n"); + } + + init_users(2); /* bucket creation must fail */ + if (!is_fatal) { + printf("fatal not called after bucket creation failure\n"); + } + + init_users(1); /* table creation must fail */ + if (!is_fatal) { + printf("fatal not called after table creation failure\n"); + } + + init_users(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 = NULL; + malloc_cnt = 4; + while (1) { + if (user_id++ == 1000) { + printf("there is no way 1000 iterations didn't require realloc\n"); + break; + } + user = (example_user_t*)malloc(sizeof(example_user_t)); + user->id = user_id; + if (!setjmp(j_buf)) { + HASH_ADD_INT(users, id, user); + } else { + free(user); + } + 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; + } + malloc_cnt = 0; + } + + HASH_CLEAR(hh, users); + + printf("End\n"); + return 0; +} diff --git a/c/ext/uthash/tests/test94.ans b/c/ext/uthash/tests/test94.ans new file mode 100644 index 0000000..9ce1942 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/test94.c b/c/ext/uthash/tests/test94.c new file mode 100644 index 0000000..b51acc5 --- /dev/null +++ b/c/ext/uthash/tests/test94.c @@ -0,0 +1,93 @@ +#include +#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/c/ext/uthash/tests/test95.ans b/c/ext/uthash/tests/test95.ans new file mode 100644 index 0000000..e69de29 diff --git a/c/ext/uthash/tests/test95.c b/c/ext/uthash/tests/test95.c new file mode 100644 index 0000000..7e715b5 --- /dev/null +++ b/c/ext/uthash/tests/test95.c @@ -0,0 +1,66 @@ +#include +#include /* 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/c/ext/uthash/tests/test96.ans b/c/ext/uthash/tests/test96.ans new file mode 100644 index 0000000..64556d9 --- /dev/null +++ b/c/ext/uthash/tests/test96.ans @@ -0,0 +1,40 @@ +time 56 not found, inserting it +time 7 not found, inserting it +time 10 not found, inserting it +time 39 not found, inserting it +time 82 found with value 10 +time 15 found with value 39 +time 31 found with value 7 +time 26 not found, inserting it +time 51 found with value 39 +time 83 not found, inserting it +time 46 found with value 10 +time 92 found with value 56 +time 49 not found, inserting it +time 25 found with value 49 +time 80 found with value 56 +time 54 not found, inserting it +time 97 found with value 49 +time 9 not found, inserting it +time 34 found with value 10 +time 86 found with value 26 +time 87 found with value 39 +time 28 not found, inserting it +time 13 found with value 49 +time 91 found with value 7 +time 95 found with value 83 +time 63 found with value 39 +time 71 found with value 83 +time 100 found with value 28 +time 44 found with value 56 +time 42 found with value 54 +time 16 found with value 28 +time 32 found with value 56 +time 6 found with value 54 +time 85 found with value 49 +time 40 found with value 28 +time 20 found with value 56 +time 18 found with value 54 +time 99 found with value 39 +time 22 found with value 10 +time 1 found with value 49 diff --git a/c/ext/uthash/tests/test96.c b/c/ext/uthash/tests/test96.c new file mode 100644 index 0000000..700bdcf --- /dev/null +++ b/c/ext/uthash/tests/test96.c @@ -0,0 +1,48 @@ +#include +#include + +#define HASH_FUNCTION(a,n,hv) (hv = clockface_hash(*(const int*)(a))) +#define HASH_KEYCMP(a,b,n) clockface_neq(*(const int*)(a), *(const int*)(b)) + +#include "uthash.h" + +struct clockface { + int time; + UT_hash_handle hh; +}; + +int clockface_hash(int time) +{ + return (time % 4); +} + +int clockface_neq(int t1, int t2) +{ + return ((t1 % 12) != (t2 % 12)); +} + +int main() +{ + int random_data[] = { + 56, 7, 10, 39, 82, 15, 31, 26, 51, 83, + 46, 92, 49, 25, 80, 54, 97, 9, 34, 86, + 87, 28, 13, 91, 95, 63, 71, 100, 44, 42, + 16, 32, 6, 85, 40, 20, 18, 99, 22, 1 + }; + + struct clockface *times = NULL; + for (int i=0; i < 40; ++i) { + struct clockface *elt = (struct clockface *)malloc(sizeof(*elt)); + struct clockface *found = NULL; + elt->time = random_data[i]; + HASH_FIND_INT(times, &elt->time, found); + if (found) { + printf("time %d found with value %d\n", elt->time, found->time); + } else { + printf("time %d not found, inserting it\n", elt->time); + HASH_ADD_INT(times, time, elt); + } + } + + return 0; +} diff --git a/c/ext/uthash/tests/threads/Makefile b/c/ext/uthash/tests/threads/Makefile new file mode 100644 index 0000000..0106006 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/threads/README b/c/ext/uthash/tests/threads/README new file mode 100644 index 0000000..e49a62e --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/threads/do_tests b/c/ext/uthash/tests/threads/do_tests new file mode 100755 index 0000000..e6809a1 --- /dev/null +++ b/c/ext/uthash/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/c/ext/uthash/tests/threads/test1.c b/c/ext/uthash/tests/threads/test1.c new file mode 100644 index 0000000..8d6cb67 --- /dev/null +++ b/c/ext/uthash/tests/threads/test1.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;ii = 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/c/ext/uthash/tests/threads/test2.c b/c/ext/uthash/tests/threads/test2.c new file mode 100644 index 0000000..316b7e6 --- /dev/null +++ b/c/ext/uthash/tests/threads/test2.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; ii = i; + e->v = 0; + HASH_ADD_INT(elts, i, e); + } + + for(i=0; i + +int main() { + printf("Hello, world!\n"); + + return 0; +}