d
This commit is contained in:
3
c/Memory/src/Memory.Aux.c
Normal file
3
c/Memory/src/Memory.Aux.c
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
UT_hash
|
||||||
|
|
||||||
3
c/ext/uthash/.gitignore
vendored
Normal file
3
c/ext/uthash/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*~
|
||||||
|
*.out
|
||||||
|
keystat.*
|
||||||
14
c/ext/uthash/.travis.yml
Normal file
14
c/ext/uthash/.travis.yml
Normal file
@@ -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
|
||||||
21
c/ext/uthash/LICENSE
Normal file
21
c/ext/uthash/LICENSE
Normal file
@@ -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.
|
||||||
|
|
||||||
8
c/ext/uthash/README.md
Normal file
8
c/ext/uthash/README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
[](https://travis-ci.org/troydhanson/uthash)
|
||||||
|
|
||||||
|
Documentation for uthash is available at:
|
||||||
|
|
||||||
|
https://troydhanson.github.com/uthash/
|
||||||
|
|
||||||
|
|
||||||
7
c/ext/uthash/doc/.gitignore
vendored
Normal file
7
c/ext/uthash/doc/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
ChangeLog.html
|
||||||
|
userguide.html
|
||||||
|
utarray.html
|
||||||
|
utlist.html
|
||||||
|
utringbuffer.html
|
||||||
|
utstack.html
|
||||||
|
utstring.html
|
||||||
282
c/ext/uthash/doc/ChangeLog.txt
Normal file
282
c/ext/uthash/doc/ChangeLog.txt
Normal file
@@ -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 <stdint.h>
|
||||||
|
* 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 <string.h>
|
||||||
|
* 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 <stdlib.h> (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:
|
||||||
18
c/ext/uthash/doc/Makefile
Normal file
18
c/ext/uthash/doc/Makefile
Normal file
@@ -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)
|
||||||
BIN
c/ext/uthash/doc/banner.png
Normal file
BIN
c/ext/uthash/doc/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
451
c/ext/uthash/doc/banner.svg
Normal file
451
c/ext/uthash/doc/banner.svg
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://web.resource.org/cc/"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="728px"
|
||||||
|
height="90px"
|
||||||
|
id="svg1307"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.45.1"
|
||||||
|
sodipodi:docbase="/Users/thanson/code/uthash/trunk/doc/html/img"
|
||||||
|
sodipodi:docname="banner.svg"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/banner.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||||
|
<defs
|
||||||
|
id="defs1309">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient12743">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#99e1fa;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop12745" />
|
||||||
|
<stop
|
||||||
|
id="stop12753"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#99e1fa;stop-opacity:0.49803922;" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#99e1fa;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop12747" />
|
||||||
|
</linearGradient>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Mend"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path7755"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
|
||||||
|
transform="scale(0.4) rotate(180)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Sstart"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Sstart"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7752"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||||
|
transform="scale(0.2)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Send"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Send"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path7749"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
|
||||||
|
transform="scale(0.2) rotate(180)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="StopM"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="StopM"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7651"
|
||||||
|
d="M 0.0,5.65 L 0.0,-5.65"
|
||||||
|
style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
|
||||||
|
transform="scale(0.4)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow2Mend"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path7737"
|
||||||
|
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
||||||
|
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||||
|
transform="scale(0.6) rotate(180) translate(-5,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="TriangleInM"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="TriangleInM"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7669"
|
||||||
|
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||||
|
transform="scale(-0.4)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="StopL"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="StopL"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7654"
|
||||||
|
d="M 0.0,5.65 L 0.0,-5.65"
|
||||||
|
style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
|
||||||
|
transform="scale(0.8)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="TriangleOutM"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="TriangleOutM"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7660"
|
||||||
|
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||||
|
transform="scale(0.4)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="DiamondS"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="DiamondS"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7675"
|
||||||
|
d="M -2.1579186e-005,-7.0710768 L -7.0710894,-8.9383918e-006 L -2.1579186e-005,7.0710589 L 7.0710462,-8.9383918e-006 L -2.1579186e-005,-7.0710768 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||||
|
transform="scale(0.2)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Tail"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Tail"
|
||||||
|
style="overflow:visible">
|
||||||
|
<g
|
||||||
|
id="g7716"
|
||||||
|
transform="scale(-1.2)">
|
||||||
|
<path
|
||||||
|
id="path7718"
|
||||||
|
d="M -3.8048674,-3.9585227 L 0.54352094,-0.00068114835"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||||
|
<path
|
||||||
|
id="path7720"
|
||||||
|
d="M -1.2866832,-3.9585227 L 3.0617053,-0.00068114835"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||||
|
<path
|
||||||
|
id="path7722"
|
||||||
|
d="M 1.3053582,-3.9585227 L 5.6537466,-0.00068114835"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||||
|
<path
|
||||||
|
id="path7724"
|
||||||
|
d="M -3.8048674,4.1775838 L 0.54352094,0.21974226"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||||
|
<path
|
||||||
|
id="path7726"
|
||||||
|
d="M -1.2866832,4.1775838 L 3.0617053,0.21974226"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||||
|
<path
|
||||||
|
id="path7728"
|
||||||
|
d="M 1.3053582,4.1775838 L 5.6537466,0.21974226"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||||
|
</g>
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Lstart"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Lstart"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7764"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||||
|
transform="scale(0.8)" />
|
||||||
|
</marker>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient3964">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#00eb00;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3966" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#00eb00;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3968" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3964"
|
||||||
|
id="radialGradient3996"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1,0,0,0.237347,4.901628e-13,36.5688)"
|
||||||
|
cx="176.99219"
|
||||||
|
cy="47.949429"
|
||||||
|
fx="176.99219"
|
||||||
|
fy="47.949429"
|
||||||
|
r="78.257812" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient12743"
|
||||||
|
id="radialGradient12751"
|
||||||
|
cx="165.91866"
|
||||||
|
cy="45.584854"
|
||||||
|
fx="165.91866"
|
||||||
|
fy="45.584854"
|
||||||
|
r="56.51194"
|
||||||
|
gradientTransform="matrix(1,0,0,0.603517,0,18.07364)"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.9793956"
|
||||||
|
inkscape:cx="372.32157"
|
||||||
|
inkscape:cy="45"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g2335"
|
||||||
|
inkscape:window-width="791"
|
||||||
|
inkscape:window-height="581"
|
||||||
|
inkscape:window-x="4"
|
||||||
|
inkscape:window-y="48" />
|
||||||
|
<metadata
|
||||||
|
id="metadata1312">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer">
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#393be9;fill-opacity:1;stroke:#f18a00;stroke-width:5.65522385;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect3981"
|
||||||
|
width="435.17825"
|
||||||
|
height="78.666664"
|
||||||
|
x="5.1747785"
|
||||||
|
y="6"
|
||||||
|
rx="29.141403"
|
||||||
|
ry="20"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90" />
|
||||||
|
<flowRoot
|
||||||
|
transform="matrix(1.673678,0,0,1.673678,-141.8484,-37.12273)"
|
||||||
|
style="font-size:47.99999774;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||||
|
id="flowRoot3988"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90"><flowRegion
|
||||||
|
style="fill:url(#radialGradient3996);fill-opacity:1"
|
||||||
|
id="flowRegion3990"><rect
|
||||||
|
style="font-size:47.99999774;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||||
|
y="18"
|
||||||
|
x="94.666664"
|
||||||
|
height="61.333332"
|
||||||
|
width="321.33334"
|
||||||
|
id="rect3992" /></flowRegion><flowPara
|
||||||
|
id="flowPara7831">ut hash</flowPara></flowRoot> <rect
|
||||||
|
style="opacity:1;fill:url(#radialGradient12751);fill-opacity:1.0;stroke:none;stroke-width:2.82532263;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect10995"
|
||||||
|
width="113.02388"
|
||||||
|
height="68.211792"
|
||||||
|
x="109.40672"
|
||||||
|
y="11.478957"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90" />
|
||||||
|
<g
|
||||||
|
id="g7808"
|
||||||
|
transform="matrix(0.807859,0,0,0.807859,-140.848,9.677403)"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<rect
|
||||||
|
y="37.730064"
|
||||||
|
x="382.39673"
|
||||||
|
height="18.405188"
|
||||||
|
width="23.206543"
|
||||||
|
id="rect4882"
|
||||||
|
style="opacity:1;fill:#9be5ea;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#d48c21;fill-opacity:0.97777776;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect4886"
|
||||||
|
width="23.206543"
|
||||||
|
height="18.405188"
|
||||||
|
x="416.39673"
|
||||||
|
y="37.730064" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path4890"
|
||||||
|
d="M 372.60327,46.932658 L 381.39673,46.932658"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path4892"
|
||||||
|
d="M 406.60327,46.932658 L 415.39673,46.932658"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
y="9.7300644"
|
||||||
|
x="348.39673"
|
||||||
|
height="18.405188"
|
||||||
|
width="23.206543"
|
||||||
|
id="rect4896"
|
||||||
|
style="opacity:1;fill:#79c71a;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#f5e1a2;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect4898"
|
||||||
|
width="23.206543"
|
||||||
|
height="18.405188"
|
||||||
|
x="382.39673"
|
||||||
|
y="9.7300644" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 372.60327,18.932658 L 381.39673,18.932658"
|
||||||
|
id="path4902"
|
||||||
|
inkscape:connector-type="polyline" />
|
||||||
|
<rect
|
||||||
|
y="14.207111"
|
||||||
|
x="318.45328"
|
||||||
|
height="10.1194"
|
||||||
|
width="10.1194"
|
||||||
|
id="rect4906"
|
||||||
|
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5789"
|
||||||
|
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
||||||
|
id="path5795"
|
||||||
|
inkscape:connector-type="polyline" />
|
||||||
|
<rect
|
||||||
|
y="37.789951"
|
||||||
|
x="348.20978"
|
||||||
|
height="18.405188"
|
||||||
|
width="23.206543"
|
||||||
|
id="rect5803"
|
||||||
|
style="opacity:1;fill:#e5e5e5;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
y="42.267002"
|
||||||
|
x="318.26633"
|
||||||
|
height="10.1194"
|
||||||
|
width="10.1194"
|
||||||
|
id="rect5805"
|
||||||
|
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 328.38572,47.280365 L 347.20977,47.107972"
|
||||||
|
id="path5807"
|
||||||
|
inkscape:connector-type="polyline" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#ddf9ed;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5809"
|
||||||
|
width="23.206543"
|
||||||
|
height="18.405188"
|
||||||
|
x="348.20978"
|
||||||
|
y="63.720913" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5811"
|
||||||
|
width="10.1194"
|
||||||
|
height="10.1194"
|
||||||
|
x="318.26633"
|
||||||
|
y="68.197968" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5813"
|
||||||
|
d="M 328.38572,73.211328 L 347.20977,73.038935"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5833"
|
||||||
|
d="M 323.47927,24.326511 L 323.35974,42.267002"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5835"
|
||||||
|
d="M 323.32603,52.386402 L 323.32603,68.197968"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
id="path6716"
|
||||||
|
d="M 429.08836,47.11641 L 394.37307,18.527349 L 394.37307,49.158485 L 359.65778,18.527349 L 359.65778,50.179523 L 359.65778,75.70547"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#f3bf33;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#StopM);marker-end:url(#Arrow1Send);stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g2335"
|
||||||
|
transform="translate(0,-10)"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo_tag.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:18.43119621px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||||
|
x="565.8512"
|
||||||
|
y="50.633156"
|
||||||
|
id="text2331"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2333"
|
||||||
|
x="565.85119"
|
||||||
|
y="50.633156">a hash table</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="565.8512"
|
||||||
|
y="73.672151"
|
||||||
|
id="tspan2361">for C structures</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 18 KiB |
1
c/ext/uthash/doc/google315d692c9c632ed0.html
Normal file
1
c/ext/uthash/doc/google315d692c9c632ed0.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
google-site-verification: google315d692c9c632ed0.html
|
||||||
122
c/ext/uthash/doc/index.html
Normal file
122
c/ext/uthash/doc/index.html
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XTHML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="styles.css" />
|
||||||
|
<title>uthash: a hash table for C structures</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="banner">
|
||||||
|
<img src="banner.png" alt="uthash: a hash table for C structures" />
|
||||||
|
</div> <!-- banner -->
|
||||||
|
|
||||||
|
<div id="topnav">
|
||||||
|
<a href="http://github.com/troydhanson/uthash">GitHub page</a> >
|
||||||
|
uthash home <!-- http://troydhanson.github.com/uthash/ -->
|
||||||
|
|
||||||
|
<a href="https://twitter.com/share" class="twitter-share-button" data-via="troydhanson">Tweet</a>
|
||||||
|
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div id="mid">
|
||||||
|
|
||||||
|
<div id="nav">
|
||||||
|
|
||||||
|
<h2>documentation</h2>
|
||||||
|
<div><a href="userguide.html">uthash</a></div>
|
||||||
|
<div><a href="utlist.html">utlist</a></div>
|
||||||
|
<div><a href="utarray.html">utarray</a></div>
|
||||||
|
<div><a href="utringbuffer.html">utringbuffer</a></div>
|
||||||
|
<div><a href="utstack.html">utstack</a></div>
|
||||||
|
<div><a href="utstring.html">utstring</a></div>
|
||||||
|
|
||||||
|
<h2>download</h2>
|
||||||
|
<h3>GNU/Linux, Windows</h3>
|
||||||
|
<div><a href=https://github.com/troydhanson/uthash/archive/master.zip>uthash-master.zip</a></div>
|
||||||
|
<div><a href=https://github.com/troydhanson/uthash>git clone</a></div>
|
||||||
|
|
||||||
|
<h2>license</h2>
|
||||||
|
<div><a href="license.html">BSD revised</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>developer</h2>
|
||||||
|
<div><a href="http://troydhanson.github.io/">Troy D. Hanson</a></div>
|
||||||
|
|
||||||
|
<h2>maintainer</h2>
|
||||||
|
<div><a href="https://github.com/Quuxplusone">Arthur O'Dwyer</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
Any C structure can be stored in a hash table using uthash. Just add a
|
||||||
|
<em>UT_hash_handle</em> 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.
|
||||||
|
|
||||||
|
<div class="listing">
|
||||||
|
Example 1. Adding an item to a hash.
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
</div> <!-- code -->
|
||||||
|
</div> <!-- listing -->
|
||||||
|
|
||||||
|
<div class="listing">
|
||||||
|
Example 2. Looking up an item in a hash.
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
struct my_struct *find_user(int user_id) {
|
||||||
|
struct my_struct *s;
|
||||||
|
|
||||||
|
HASH_FIND_INT(users, &user_id, s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
</div> <!-- code -->
|
||||||
|
</div> <!-- listing -->
|
||||||
|
|
||||||
|
<div class="listing">
|
||||||
|
Example 3. Deleting an item from a hash.
|
||||||
|
<div class="code">
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
void delete_user(struct my_struct *user) {
|
||||||
|
HASH_DEL(users, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
</div> <!-- code -->
|
||||||
|
</div> <!-- listing -->
|
||||||
|
|
||||||
|
For more information and examples, please see the <a href="userguide.html">User Guide.</a>
|
||||||
|
|
||||||
|
</div> <!-- main -->
|
||||||
|
</div> <!-- mid -->
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div id="footer">
|
||||||
|
</div> <!-- footer -->
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
55
c/ext/uthash/doc/license.html
Normal file
55
c/ext/uthash/doc/license.html
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XTHML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="styles.css" />
|
||||||
|
<title>uthash: a hash table for C structures</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="banner">
|
||||||
|
<img src="banner.png" alt="uthash: a hash table for C structures" />
|
||||||
|
</div> <!-- banner -->
|
||||||
|
|
||||||
|
<div id="topnav">
|
||||||
|
<a href="http://troydhanson.github.com/uthash/">uthash home</a> >
|
||||||
|
BSD license
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div id="mid">
|
||||||
|
<div id="main">
|
||||||
|
<pre>
|
||||||
|
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.
|
||||||
|
</pre>
|
||||||
|
</div> <!-- mid -->
|
||||||
|
</div> <!-- main -->
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div id="footer">
|
||||||
|
</div> <!-- footer -->
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
BIN
c/ext/uthash/doc/rss.png
Normal file
BIN
c/ext/uthash/doc/rss.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 689 B |
141
c/ext/uthash/doc/styles.css
Normal file
141
c/ext/uthash/doc/styles.css
Normal file
@@ -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%;
|
||||||
|
}
|
||||||
1903
c/ext/uthash/doc/userguide.txt
Normal file
1903
c/ext/uthash/doc/userguide.txt
Normal file
File diff suppressed because it is too large
Load Diff
383
c/ext/uthash/doc/utarray.txt
Normal file
383
c/ext/uthash/doc/utarray.txt
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
utarray: dynamic array macros for C
|
||||||
|
===================================
|
||||||
|
Troy D. Hanson <tdh@tkhanson.net>
|
||||||
|
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 <<operations,operations>> 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 <<operations,operations>>
|
||||||
|
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 <stdio.h>
|
||||||
|
#include "utarray.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
UT_array *nums;
|
||||||
|
int i, *p;
|
||||||
|
|
||||||
|
utarray_new(nums,&ut_int_icd);
|
||||||
|
for(i=0; i < 10; i++) utarray_push_back(nums,&i);
|
||||||
|
|
||||||
|
for(p=(int*)utarray_front(nums);
|
||||||
|
p!=NULL;
|
||||||
|
p=(int*)utarray_next(nums,p)) {
|
||||||
|
printf("%d\n",*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
utarray_free(nums);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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<m,40<",grid="none",options="none"]
|
||||||
|
|===============================================================================
|
||||||
|
| utarray_new(UT_array *a, UT_icd *icd)| allocate a new array
|
||||||
|
| utarray_free(UT_array *a) | free an allocated array
|
||||||
|
| utarray_init(UT_array *a,UT_icd *icd)| init an array (non-alloc)
|
||||||
|
| utarray_done(UT_array *a) | dispose of an array (non-allocd)
|
||||||
|
| utarray_reserve(UT_array *a,int n) | ensure space available for 'n' more elements
|
||||||
|
| utarray_push_back(UT_array *a,void *p) | push element p onto a
|
||||||
|
| utarray_pop_back(UT_array *a) | pop last element from a
|
||||||
|
| utarray_extend_back(UT_array *a) | push empty element onto a
|
||||||
|
| utarray_len(UT_array *a) | get length of a
|
||||||
|
| utarray_eltptr(UT_array *a,int j) | get pointer of element from index
|
||||||
|
| utarray_eltidx(UT_array *a,void *e) | get index of element from pointer
|
||||||
|
| utarray_insert(UT_array *a,void *p, int j) | insert element p to index j
|
||||||
|
| utarray_inserta(UT_array *a,UT_array *w, int j) | insert array w into array a at index j
|
||||||
|
| utarray_resize(UT_array *dst,int num) | extend or shrink array to num elements
|
||||||
|
| utarray_concat(UT_array *dst,UT_array *src) | copy src to end of dst array
|
||||||
|
| utarray_erase(UT_array *a,int pos,int len) | remove len elements from a[pos]..a[pos+len-1]
|
||||||
|
| utarray_clear(UT_array *a) | clear all elements from a, setting its length to zero
|
||||||
|
| utarray_sort(UT_array *a,cmpfcn *cmp) | sort elements of a using comparison function
|
||||||
|
| utarray_find(UT_array *a,void *v, cmpfcn *cmp) | find element v in utarray (must be sorted)
|
||||||
|
| utarray_front(UT_array *a) | get first element of a
|
||||||
|
| utarray_next(UT_array *a,void *e) | get element of a following e (front if e is NULL)
|
||||||
|
| utarray_prev(UT_array *a,void *e) | get element of a before e (back if e is NULL)
|
||||||
|
| utarray_back(UT_array *a) | get last element of a
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
1. `utarray_new` and `utarray_free` are used to allocate a new array and free it,
|
||||||
|
while `utarray_init` and `utarray_done` can be used if the UT_array is already
|
||||||
|
allocated and just needs to be initialized or have its internal resources
|
||||||
|
freed.
|
||||||
|
|
||||||
|
2. `utarray_reserve` takes the "delta" of elements to reserve, not the total
|
||||||
|
desired capacity of the array. This differs from the C++ STL "reserve" notion.
|
||||||
|
|
||||||
|
3. `utarray_sort` expects a comparison function having the usual `strcmp`-like
|
||||||
|
convention where it accepts two elements (a and b) and returns a negative
|
||||||
|
value if a precedes b, 0 if a and b sort equally, and positive if b precedes a.
|
||||||
|
This is an example of a comparison function:
|
||||||
|
|
||||||
|
int intsort(const void *a, const void *b) {
|
||||||
|
int _a = *(const int *)a;
|
||||||
|
int _b = *(const int *)b;
|
||||||
|
return (_a < _b) ? -1 : (_a > _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:
|
||||||
BIN
c/ext/uthash/doc/uthash-mini.png
Normal file
BIN
c/ext/uthash/doc/uthash-mini.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
288
c/ext/uthash/doc/uthash-mini.svg
Normal file
288
c/ext/uthash/doc/uthash-mini.svg
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://web.resource.org/cc/"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="118.44112"
|
||||||
|
height="22.655222"
|
||||||
|
id="svg2018"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.44"
|
||||||
|
version="1.0"
|
||||||
|
sodipodi:docbase="/home/thanson/code/uthash/trunk/doc/html/img"
|
||||||
|
sodipodi:docname="uthash-mini.svg">
|
||||||
|
<defs
|
||||||
|
id="defs3">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient3964">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#00eb00;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3966" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#00eb00;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3968" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3964"
|
||||||
|
id="radialGradient3996"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1,0,0,0.237347,0,36.5688)"
|
||||||
|
cx="176.99219"
|
||||||
|
cy="47.949429"
|
||||||
|
fx="176.99219"
|
||||||
|
fy="47.949429"
|
||||||
|
r="78.257812" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient12743">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#99e1fa;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop12745" />
|
||||||
|
<stop
|
||||||
|
id="stop12753"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#99e1fa;stop-opacity:0.49803922;" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#99e1fa;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop12747" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient12743"
|
||||||
|
id="radialGradient12751"
|
||||||
|
cx="165.91866"
|
||||||
|
cy="45.584854"
|
||||||
|
fx="165.91866"
|
||||||
|
fy="45.584854"
|
||||||
|
r="56.51194"
|
||||||
|
gradientTransform="matrix(0.268675,0,0,0.16215,17.28599,40.67469)"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Send"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Send"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7749"
|
||||||
|
d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:black;stroke-width:1pt;marker-start:none"
|
||||||
|
transform="scale(-0.2,-0.2)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="StopM"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="StopM"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path7651"
|
||||||
|
d="M 0,5.65 L 0,-5.65"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1pt"
|
||||||
|
transform="scale(0.4,0.4)" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.2"
|
||||||
|
inkscape:cx="160"
|
||||||
|
inkscape:cy="90"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:window-width="916"
|
||||||
|
inkscape:window-height="626"
|
||||||
|
inkscape:window-x="5"
|
||||||
|
inkscape:window-y="73" />
|
||||||
|
<metadata
|
||||||
|
id="metadata2022">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-17.9166,-36.67108)">
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#393be9;fill-opacity:1;stroke:#f18a00;stroke-width:1.51941979;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect3981"
|
||||||
|
width="116.92171"
|
||||||
|
height="21.135801"
|
||||||
|
x="18.67631"
|
||||||
|
y="37.430794"
|
||||||
|
rx="7.8295798"
|
||||||
|
ry="5.3735089"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90" />
|
||||||
|
<flowRoot
|
||||||
|
transform="matrix(0.449676,0,0,0.449676,-20.8252,25.84477)"
|
||||||
|
style="font-size:47.99999619px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||||
|
id="flowRoot3988"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90"><flowRegion
|
||||||
|
style="fill:url(#radialGradient3996);fill-opacity:1"
|
||||||
|
id="flowRegion3990"><rect
|
||||||
|
style="font-size:47.99999619px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#faf599;fill-opacity:1;stroke:#f3bf33;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||||
|
y="18"
|
||||||
|
x="94.666664"
|
||||||
|
height="61.333332"
|
||||||
|
width="321.33334"
|
||||||
|
id="rect3992" /></flowRegion><flowPara
|
||||||
|
id="flowPara7831">ut hash</flowPara></flowRoot> <rect
|
||||||
|
style="opacity:1;fill:url(#radialGradient12751);fill-opacity:1;stroke:none;stroke-width:2.82532263;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect10995"
|
||||||
|
width="30.366741"
|
||||||
|
height="18.326834"
|
||||||
|
x="46.68087"
|
||||||
|
y="38.902855"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90" />
|
||||||
|
<g
|
||||||
|
id="g7808"
|
||||||
|
transform="matrix(0.217052,0,0,0.217052,-20.55641,38.41883)"
|
||||||
|
inkscape:export-filename="/home/thanson/code/uthash/doc/html/img/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<rect
|
||||||
|
y="37.730064"
|
||||||
|
x="382.39673"
|
||||||
|
height="18.405188"
|
||||||
|
width="23.206543"
|
||||||
|
id="rect4882"
|
||||||
|
style="opacity:1;fill:#9be5ea;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#d48c21;fill-opacity:0.97777776;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect4886"
|
||||||
|
width="23.206543"
|
||||||
|
height="18.405188"
|
||||||
|
x="416.39673"
|
||||||
|
y="37.730064" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path4890"
|
||||||
|
d="M 372.60327,46.932658 L 381.39673,46.932658"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path4892"
|
||||||
|
d="M 406.60327,46.932658 L 415.39673,46.932658"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
y="9.7300644"
|
||||||
|
x="348.39673"
|
||||||
|
height="18.405188"
|
||||||
|
width="23.206543"
|
||||||
|
id="rect4896"
|
||||||
|
style="opacity:1;fill:#79c71a;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#f5e1a2;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect4898"
|
||||||
|
width="23.206543"
|
||||||
|
height="18.405188"
|
||||||
|
x="382.39673"
|
||||||
|
y="9.7300644" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 372.60327,18.932658 L 381.39673,18.932658"
|
||||||
|
id="path4902"
|
||||||
|
inkscape:connector-type="polyline" />
|
||||||
|
<rect
|
||||||
|
y="14.207111"
|
||||||
|
x="318.45328"
|
||||||
|
height="10.1194"
|
||||||
|
width="10.1194"
|
||||||
|
id="rect4906"
|
||||||
|
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5789"
|
||||||
|
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 328.57268,19.220474 L 347.39673,19.048081"
|
||||||
|
id="path5795"
|
||||||
|
inkscape:connector-type="polyline" />
|
||||||
|
<rect
|
||||||
|
y="37.789951"
|
||||||
|
x="348.20978"
|
||||||
|
height="18.405188"
|
||||||
|
width="23.206543"
|
||||||
|
id="rect5803"
|
||||||
|
style="opacity:1;fill:#e5e5e5;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
y="42.267002"
|
||||||
|
x="318.26633"
|
||||||
|
height="10.1194"
|
||||||
|
width="10.1194"
|
||||||
|
id="rect5805"
|
||||||
|
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 328.38572,47.280365 L 347.20977,47.107972"
|
||||||
|
id="path5807"
|
||||||
|
inkscape:connector-type="polyline" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#ddf9ed;fill-opacity:1;stroke:black;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5809"
|
||||||
|
width="23.206543"
|
||||||
|
height="18.405188"
|
||||||
|
x="348.20978"
|
||||||
|
y="63.720913" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#1336e6;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5811"
|
||||||
|
width="10.1194"
|
||||||
|
height="10.1194"
|
||||||
|
x="318.26633"
|
||||||
|
y="68.197968" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5813"
|
||||||
|
d="M 328.38572,73.211328 L 347.20977,73.038935"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5833"
|
||||||
|
d="M 323.47927,24.326511 L 323.35974,42.267002"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-type="polyline"
|
||||||
|
id="path5835"
|
||||||
|
d="M 323.32603,52.386402 L 323.32603,68.197968"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#2f29df;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
id="path6716"
|
||||||
|
d="M 429.08836,47.11641 L 394.37307,18.527349 L 394.37307,49.158485 L 359.65778,18.527349 L 359.65778,50.179523 L 359.65778,75.70547"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#f3bf33;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#StopM);marker-end:url(#Arrow1Send);stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
BIN
c/ext/uthash/doc/uthash.png
Normal file
BIN
c/ext/uthash/doc/uthash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
293
c/ext/uthash/doc/utlist.txt
Normal file
293
c/ext/uthash/doc/utlist.txt
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
utlist: linked list macros for C structures
|
||||||
|
===========================================
|
||||||
|
Troy D. Hanson <tdh@tkhanson.net>
|
||||||
|
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 <<flex_names,family of macros>> 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="10<m,10<m,10<m",grid="cols",options="header"]
|
||||||
|
|===============================================================================
|
||||||
|
|Singly-linked | Doubly-linked | Circular, doubly-linked
|
||||||
|
|LL_PREPEND(head,add); | DL_PREPEND(head,add); | CDL_PREPEND(head,add);
|
||||||
|
|LL_PREPEND_ELEM(head,ref,add); | DL_PREPEND_ELEM(head,ref,add); | CDL_PREPEND_ELEM(head,ref,add);
|
||||||
|
|LL_APPEND_ELEM(head,ref,add); | DL_APPEND_ELEM(head,ref,add); | CDL_APPEND_ELEM(head,ref,add);
|
||||||
|
|LL_REPLACE_ELEM(head,del,add); | DL_REPLACE_ELEM(head,del,add); | CDL_REPLACE_ELEM(head,del,add);
|
||||||
|
|LL_APPEND(head,add); | DL_APPEND(head,add); | CDL_APPEND(head,add);
|
||||||
|
|LL_INSERT_INORDER(head,add,cmp); | DL_INSERT_INORDER(head,add,cmp); | CDL_INSERT_INORDER(head,add,cmp);
|
||||||
|
|LL_CONCAT(head1,head2); | DL_CONCAT(head1,head2); |
|
||||||
|
|LL_DELETE(head,del); | DL_DELETE(head,del); | CDL_DELETE(head,del);
|
||||||
|
|LL_SORT(head,cmp); | DL_SORT(head,cmp); | CDL_SORT(head,cmp);
|
||||||
|
|LL_FOREACH(head,elt) {...}| DL_FOREACH(head,elt) {...} | CDL_FOREACH(head,elt) {...}
|
||||||
|
|LL_FOREACH_SAFE(head,elt,tmp) {...}| DL_FOREACH_SAFE(head,elt,tmp) {...} | CDL_FOREACH_SAFE(head,elt,tmp1,tmp2) {...}
|
||||||
|
|LL_SEARCH_SCALAR(head,elt,mbr,val);| DL_SEARCH_SCALAR(head,elt,mbr,val); | CDL_SEARCH_SCALAR(head,elt,mbr,val);
|
||||||
|
|LL_SEARCH(head,elt,like,cmp);| DL_SEARCH(head,elt,like,cmp); | CDL_SEARCH(head,elt,like,cmp);
|
||||||
|
|LL_LOWER_BOUND(head,elt,like,cmp); | DL_LOWER_BOUND(head,elt,like,cmp); | CDL_LOWER_BOUND(head,elt,like,cmp);
|
||||||
|
|LL_COUNT(head,elt,count); | DL_COUNT(head,elt,count); | CDL_COUNT(head,elt,count);
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
'Prepend' means to insert an element in front of the existing list head (if any),
|
||||||
|
changing the list head to the new element. 'Append' means to add an element at the
|
||||||
|
end of the list, so it becomes the new tail element. 'Concatenate' takes two
|
||||||
|
properly constructed lists and appends the second list to the first. (Visual
|
||||||
|
Studio 2008 does not support `LL_CONCAT` and `DL_CONCAT`, but VS2010 is ok.)
|
||||||
|
To prepend before an arbitrary element instead of the list head, use the
|
||||||
|
`_PREPEND_ELEM` macro family.
|
||||||
|
To append after an arbitrary element element instead of the list head, use the
|
||||||
|
`_APPEND_ELEM` macro family.
|
||||||
|
To 'replace' an arbitrary list element with another element use the `_REPLACE_ELEM`
|
||||||
|
family of macros.
|
||||||
|
|
||||||
|
The 'sort' operation never moves the elements in memory; rather it only adjusts
|
||||||
|
the list order by altering the `prev` and `next` pointers in each element. Also
|
||||||
|
the sort operation can change the list head to point to a new element.
|
||||||
|
|
||||||
|
The 'foreach' operation is for easy iteration over the list from the head to the
|
||||||
|
tail. A usage example is shown below. You can of course just use the `prev` and
|
||||||
|
`next` pointers directly instead of using the 'foreach' macros.
|
||||||
|
The 'foreach_safe' operation should be used if you plan to delete any of the list
|
||||||
|
elements while iterating.
|
||||||
|
|
||||||
|
The 'search' operation is a shortcut for iteration in search of a particular
|
||||||
|
element. It is not any faster than manually iterating and testing each element.
|
||||||
|
There are two forms: the "scalar" version searches for an element using a
|
||||||
|
simple equality test on a given structure member, while the general version takes an
|
||||||
|
element to which all others in the list will be compared using a `cmp` function.
|
||||||
|
|
||||||
|
The 'lower_bound' operation finds the first element of the list which is no greater
|
||||||
|
than the provided `like` element, according to the provided `cmp` function.
|
||||||
|
The 'lower_bound' operation sets `elt` to a suitable value for passing to
|
||||||
|
`LL_APPEND_ELEM`; i.e., `elt=NULL` if the proper insertion point is at the front of
|
||||||
|
the list, and `elt=p` if the proper insertion point is between `p` and `p->next`.
|
||||||
|
|
||||||
|
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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "utlist.h"
|
||||||
|
|
||||||
|
#define BUFLEN 20
|
||||||
|
|
||||||
|
typedef struct el {
|
||||||
|
char bname[BUFLEN];
|
||||||
|
struct el *next, *prev;
|
||||||
|
} el;
|
||||||
|
|
||||||
|
int 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<m,10<m,10<m",grid="cols",options="header"]
|
||||||
|
|===============================================================================
|
||||||
|
|Singly-linked | Doubly-linked | Circular, doubly-linked
|
||||||
|
|LL_PREPEND2(head,add,next); | DL_PREPEND2(head,add,prev,next); | CDL_PREPEND2(head,add,prev,next);
|
||||||
|
|LL_PREPEND_ELEM2(head,ref,add,next); | DL_PREPEND_ELEM2(head,ref,add,prev,next); | CDL_PREPEND_ELEM2(head,ref,add,prev,next);
|
||||||
|
|LL_APPEND_ELEM2(head,ref,add,next); | DL_APPEND_ELEM2(head,ref,add,prev,next); | CDL_APPEND_ELEM2(head,ref,add,prev,next);
|
||||||
|
|LL_REPLACE_ELEM2(head,del,add,next); | DL_REPLACE_ELEM2(head,del,add,prev,next); | CDL_REPLACE_ELEM2(head,del,add,prev,next);
|
||||||
|
|LL_APPEND2(head,add,next); | DL_APPEND2(head,add,prev,next); | CDL_APPEND2(head,add,prev,next);
|
||||||
|
|LL_INSERT_INORDER2(head,add,cmp,next); | DL_INSERT_INORDER2(head,add,cmp,prev,next); | CDL_INSERT_INORDER2(head,add,cmp,prev,next);
|
||||||
|
|LL_CONCAT2(head1,head2,next); | DL_CONCAT2(head1,head2,prev,next); |
|
||||||
|
|LL_DELETE2(head,del,next); | DL_DELETE2(head,del,prev,next); | CDL_DELETE2(head,del,prev,next);
|
||||||
|
|LL_SORT2(head,cmp,next); | DL_SORT2(head,cmp,prev,next); | CDL_SORT2(head,cmp,prev,next);
|
||||||
|
|LL_FOREACH2(head,elt,next) {...} | DL_FOREACH2(head,elt,next) {...} | CDL_FOREACH2(head,elt,next) {...}
|
||||||
|
|LL_FOREACH_SAFE2(head,elt,tmp,next) {...} | DL_FOREACH_SAFE2(head,elt,tmp,next) {...} | CDL_FOREACH_SAFE2(head,elt,tmp1,tmp2,prev,next) {...}
|
||||||
|
|LL_SEARCH_SCALAR2(head,elt,mbr,val,next); | DL_SEARCH_SCALAR2(head,elt,mbr,val,next); | CDL_SEARCH_SCALAR2(head,elt,mbr,val,next);
|
||||||
|
|LL_SEARCH2(head,elt,like,cmp,next); | DL_SEARCH2(head,elt,like,cmp,next); | CDL_SEARCH2(head,elt,like,cmp,next);
|
||||||
|
|LL_LOWER_BOUND2(head,elt,like,cmp,next); | DL_LOWER_BOUND2(head,elt,like,cmp,next); | CDL_LOWER_BOUND2(head,elt,like,cmp,next);
|
||||||
|
|LL_COUNT2(head,elt,count,next); | DL_COUNT2(head,elt,count,next); | CDL_COUNT2(head,elt,count,next);
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
// vim: set tw=80 wm=2 syntax=asciidoc:
|
||||||
374
c/ext/uthash/doc/utringbuffer.txt
Normal file
374
c/ext/uthash/doc/utringbuffer.txt
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
utringbuffer: dynamic ring-buffer macros for C
|
||||||
|
==============================================
|
||||||
|
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
|
||||||
|
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 <<operations,operations>> 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 <<operations,operations>>
|
||||||
|
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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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<m,40<",grid="none",options="none"]
|
||||||
|
|===============================================================================
|
||||||
|
| utringbuffer_new(UT_ringbuffer *a, int n, UT_icd *icd) | allocate a new ringbuffer
|
||||||
|
| utringbuffer_free(UT_ringbuffer *a) | free an allocated ringbuffer
|
||||||
|
| utringbuffer_init(UT_ringbuffer *a, int n, UT_icd *icd) | init a ringbuffer (non-alloc)
|
||||||
|
| utringbuffer_done(UT_ringbuffer *a) | dispose of a ringbuffer (non-alloc)
|
||||||
|
| utringbuffer_clear(UT_ringbuffer *a) | clear all elements from a, making it empty
|
||||||
|
| utringbuffer_push_back(UT_ringbuffer *a, element *p) | push element p onto a
|
||||||
|
| utringbuffer_len(UT_ringbuffer *a) | get length of a
|
||||||
|
| utringbuffer_empty(UT_ringbuffer *a) | get whether a is empty
|
||||||
|
| utringbuffer_full(UT_ringbuffer *a) | get whether a is full
|
||||||
|
| utringbuffer_eltptr(UT_ringbuffer *a, int j) | get pointer of element from index
|
||||||
|
| utringbuffer_eltidx(UT_ringbuffer *a, element *e) | get index of element from pointer
|
||||||
|
| utringbuffer_front(UT_ringbuffer *a) | get oldest element of a
|
||||||
|
| utringbuffer_next(UT_ringbuffer *a, element *e) | get element of a following e (front if e is NULL)
|
||||||
|
| utringbuffer_prev(UT_ringbuffer *a, element *e) | get element of a before e (back if e is NULL)
|
||||||
|
| utringbuffer_back(UT_ringbuffer *a) | get newest element of a
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
1. `utringbuffer_new` and `utringbuffer_free` are used to allocate a new ring-buffer
|
||||||
|
and to free it,
|
||||||
|
while `utringbuffer_init` and `utringbuffer_done` can be used if the UT_ringbuffer
|
||||||
|
is already allocated and just needs to be initialized or have its internal resources
|
||||||
|
freed.
|
||||||
|
2. Both `utringbuffer_new` and `utringbuffer_init` take a second parameter `n` indicating
|
||||||
|
the capacity of the ring-buffer, that is, the size at which the ring-buffer is considered
|
||||||
|
"full" and begins to overwrite old elements with newly pushed ones.
|
||||||
|
3. Once a ring-buffer has become full, it will never again become un-full except by
|
||||||
|
means of `utringbuffer_clear`. There is no way to "pop" a single old item from the
|
||||||
|
front of the ring-buffer. You can simulate this ability by maintaining a separate
|
||||||
|
integer count of the number of "logically popped elements", and starting your iteration
|
||||||
|
with `utringbuffer_eltptr(a, popped_count)` instead of with `utringbuffer_front(a)`.
|
||||||
|
4. Pointers to elements (obtained using `utringbuffer_eltptr`, `utringbuffer_front`,
|
||||||
|
`utringbuffer_next`, etc.) are not generally invalidated by `utringbuffer_push_back`,
|
||||||
|
because utringbuffer does not perform reallocation; however, a pointer to the oldest
|
||||||
|
element may suddenly turn into a pointer to the 'newest' element if
|
||||||
|
`utringbuffer_push_back` is called while the buffer is full.
|
||||||
|
5. The elements of a ring-buffer are stored in contiguous memory, but once the ring-buffer
|
||||||
|
has become full, it is no longer true that the elements are contiguously in order from
|
||||||
|
oldest to newest; i.e., `(element *)utringbuffer_front(a) + utringbuffer_len(a)-1` is
|
||||||
|
not generally equal to `(element *)utringbuffer_back(a)`.
|
||||||
|
|
||||||
|
// vim: set nowrap syntax=asciidoc:
|
||||||
158
c/ext/uthash/doc/utstack.txt
Normal file
158
c/ext/uthash/doc/utstack.txt
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
utstack: intrusive stack macros for C
|
||||||
|
=====================================
|
||||||
|
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
|
||||||
|
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<m,40<",grid="none",options="none"]
|
||||||
|
|===============================================================================
|
||||||
|
|STACK_PUSH(stack,add); | push `add` onto `stack`
|
||||||
|
|STACK_POP(stack,elt); | pop `stack` and save previous top as `elt`
|
||||||
|
|STACK_COUNT(stack,tmp,count); | store number of elements into `count`
|
||||||
|
|STACK_TOP(stack) | return `stack`
|
||||||
|
|STACK_EMPTY(stack) | return `stack == NULL`
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
The parameters shown in the table above are explained here:
|
||||||
|
|
||||||
|
stack::
|
||||||
|
The stack head (a pointer to your element structure).
|
||||||
|
add::
|
||||||
|
A pointer to the element structure you are adding to the stack.
|
||||||
|
elt::
|
||||||
|
A pointer that will be assigned the address of the popped element. Need not be initialized.
|
||||||
|
tmp::
|
||||||
|
A pointer of the same type as `elt`. Used internally. Need not be initialized.
|
||||||
|
count::
|
||||||
|
An integer that will be assigned the size of the stack. Need not be initialized.
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
~~~~~~~
|
||||||
|
This example program reads names from a text file (one name per line), and
|
||||||
|
pushes each name on the stack; then pops and prints them in reverse order.
|
||||||
|
|
||||||
|
.A stack of names
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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<m,40<",grid="none",options="none"]
|
||||||
|
|===============================================================================
|
||||||
|
|STACK_PUSH2(stack,add,next); | push `add` onto `stack`
|
||||||
|
|STACK_POP2(stack,elt,next); | pop `stack` and save previous top as `elt`
|
||||||
|
|STACK_COUNT2(stack,tmp,count,next); | store number of elements into `count`
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
// vim: set nowrap syntax=asciidoc:
|
||||||
239
c/ext/uthash/doc/utstring.txt
Normal file
239
c/ext/uthash/doc/utstring.txt
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
utstring: dynamic string macros for C
|
||||||
|
=====================================
|
||||||
|
Troy D. Hanson <tdh@tkhanson.net>
|
||||||
|
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 <<operations,operations>> 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 <stdio.h>
|
||||||
|
#include "utstring.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
UT_string *s;
|
||||||
|
|
||||||
|
utstring_new(s);
|
||||||
|
utstring_printf(s, "hello world!" );
|
||||||
|
printf("%s\n", utstring_body(s));
|
||||||
|
|
||||||
|
utstring_free(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The next example demonstrates that `utstring_printf` 'appends' to the string.
|
||||||
|
It also shows concatenation.
|
||||||
|
|
||||||
|
.Sample 2
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "utstring.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
UT_string *s, *t;
|
||||||
|
|
||||||
|
utstring_new(s);
|
||||||
|
utstring_new(t);
|
||||||
|
|
||||||
|
utstring_printf(s, "hello " );
|
||||||
|
utstring_printf(s, "world " );
|
||||||
|
|
||||||
|
utstring_printf(t, "hi " );
|
||||||
|
utstring_printf(t, "there " );
|
||||||
|
|
||||||
|
utstring_concat(s, t);
|
||||||
|
printf("length: %u\n", 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 <stdio.h>
|
||||||
|
#include "utstring.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
UT_string *s;
|
||||||
|
char binary[] = "\xff\xff";
|
||||||
|
|
||||||
|
utstring_new(s);
|
||||||
|
utstring_bincpy(s, binary, sizeof(binary));
|
||||||
|
printf("length is %u\n", 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<m,40<",grid="none",options="none"]
|
||||||
|
|===============================================================================
|
||||||
|
| utstring_new(s) | allocate a new utstring
|
||||||
|
| utstring_renew(s) | allocate a new utstring (if s is `NULL`) otherwise clears it
|
||||||
|
| utstring_free(s) | free an allocated utstring
|
||||||
|
| utstring_init(s) | init a utstring (non-alloc)
|
||||||
|
| utstring_done(s) | dispose of a utstring (non-alloc)
|
||||||
|
| utstring_printf(s,fmt,...) | printf into a utstring (appends)
|
||||||
|
| utstring_bincpy(s,bin,len) | insert binary data of length len (appends)
|
||||||
|
| utstring_concat(dst,src) | concatenate src utstring to end of dst utstring
|
||||||
|
| utstring_clear(s) | clear the content of s (setting its length to 0)
|
||||||
|
| utstring_len(s) | obtain the length of s as an unsigned integer
|
||||||
|
| utstring_body(s) | get `char*` to body of s (buffer is always null-terminated)
|
||||||
|
| utstring_find(s,pos,str,len) | forward search from pos for a substring
|
||||||
|
| utstring_findR(s,pos,str,len) | reverse search from pos for a substring
|
||||||
|
|===============================================================================
|
||||||
|
|
||||||
|
New/free vs. init/done
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Use `utstring_new` and `utstring_free` to allocate a new string or free it. If
|
||||||
|
the UT_string is statically allocated, use `utstring_init` and `utstring_done`
|
||||||
|
to initialize or free its internal memory.
|
||||||
|
|
||||||
|
Substring search
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
Use `utstring_find` and `utstring_findR` to search for a substring in a utstring.
|
||||||
|
It comes in forward and reverse varieties. The reverse search scans from the end of
|
||||||
|
the string backward. These take a position to start searching from, measured from 0
|
||||||
|
(the start of the utstring). A negative position is counted from the end of
|
||||||
|
the string, so, -1 is the last position. Note that in the reverse search, the
|
||||||
|
initial position anchors to the 'end' of the substring being searched for;
|
||||||
|
e.g., the 't' in 'cat'. The return value always refers to the offset where the
|
||||||
|
substring 'starts' in the utstring. When no substring match is found, -1 is
|
||||||
|
returned.
|
||||||
|
|
||||||
|
For example if a utstring called `s` contains:
|
||||||
|
|
||||||
|
ABC ABCDAB ABCDABCDABDE
|
||||||
|
|
||||||
|
Then these forward and reverse substring searches for `ABC` produce these results:
|
||||||
|
|
||||||
|
utstring_find( s, -9, "ABC", 3 ) = 15
|
||||||
|
utstring_find( s, 3, "ABC", 3 ) = 4
|
||||||
|
utstring_find( s, 16, "ABC", 3 ) = -1
|
||||||
|
utstring_findR( s, -9, "ABC", 3 ) = 11
|
||||||
|
utstring_findR( s, 12, "ABC", 3 ) = 4
|
||||||
|
utstring_findR( s, 2, "ABC", 3 ) = 0
|
||||||
|
|
||||||
|
"Multiple use" substring search
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
The preceding examples show "single use" versions of substring matching, where
|
||||||
|
the internal Knuth-Morris-Pratt (KMP) table is internally built and then freed
|
||||||
|
after the search. If your program needs to run many searches for a given
|
||||||
|
substring, it is more efficient to save the KMP table and reuse it.
|
||||||
|
|
||||||
|
To reuse the KMP table, build it manually and then pass it into the internal
|
||||||
|
search functions. The functions involved are:
|
||||||
|
|
||||||
|
_utstring_BuildTable (build the KMP table for a forward search)
|
||||||
|
_utstring_BuildTableR (build the KMP table for a reverse search)
|
||||||
|
_utstring_find (forward search using a prebuilt KMP table)
|
||||||
|
_utstring_findR (reverse search using a prebuilt KMP table)
|
||||||
|
|
||||||
|
This is an example of building a forward KMP table for the substring "ABC", and
|
||||||
|
then using it in a search:
|
||||||
|
|
||||||
|
long *KPM_TABLE, offset;
|
||||||
|
KPM_TABLE = (long *)malloc( sizeof(long) * (strlen("ABC")) + 1));
|
||||||
|
_utstring_BuildTable("ABC", 3, KPM_TABLE);
|
||||||
|
offset = _utstring_find(utstring_body(s), utstring_len(s), "ABC", 3, KPM_TABLE );
|
||||||
|
free(KPM_TABLE);
|
||||||
|
|
||||||
|
Note that the internal `_utstring_find` has the length of the UT_string as its
|
||||||
|
second argument, rather than the start position. You can emulate the position
|
||||||
|
parameter by adding to the string start address and subtracting from its length.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
1. To override the default out-of-memory handling behavior (which calls `exit(-1)`),
|
||||||
|
override the `utstring_oom()` macro before including `utstring.h`.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
#define utstring_oom() do { longjmp(error_handling_location); } while (0)
|
||||||
|
...
|
||||||
|
#include "utstring.h"
|
||||||
|
|
||||||
|
// vim: set nowrap syntax=asciidoc:
|
||||||
1
c/ext/uthash/include
Symbolic link
1
c/ext/uthash/include
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
src
|
||||||
28
c/ext/uthash/package.json
Normal file
28
c/ext/uthash/package.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"description": "C macros for hash tables and more",
|
||||||
|
|
||||||
|
"keywords": [
|
||||||
|
"array",
|
||||||
|
"data",
|
||||||
|
"hash",
|
||||||
|
"list",
|
||||||
|
"macro",
|
||||||
|
"string",
|
||||||
|
"structure",
|
||||||
|
"uthash"
|
||||||
|
],
|
||||||
|
|
||||||
|
"name": "uthash",
|
||||||
|
"repo": "troydhanson/uthash",
|
||||||
|
|
||||||
|
"src": [
|
||||||
|
"src/utarray.h",
|
||||||
|
"src/uthash.h",
|
||||||
|
"src/utlist.h",
|
||||||
|
"src/utringbuffer.h",
|
||||||
|
"src/utstack.h",
|
||||||
|
"src/utstring.h"
|
||||||
|
],
|
||||||
|
|
||||||
|
"version": "2.3.0"
|
||||||
|
}
|
||||||
248
c/ext/uthash/src/utarray.h
Normal file
248
c/ext/uthash/src/utarray.h
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
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 array implementation using macros
|
||||||
|
*/
|
||||||
|
#ifndef UTARRAY_H
|
||||||
|
#define UTARRAY_H
|
||||||
|
|
||||||
|
#define UTARRAY_VERSION 2.3.0
|
||||||
|
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
#include <string.h> /* memset, etc */
|
||||||
|
#include <stdlib.h> /* 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 */
|
||||||
1136
c/ext/uthash/src/uthash.h
Normal file
1136
c/ext/uthash/src/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
1073
c/ext/uthash/src/utlist.h
Normal file
1073
c/ext/uthash/src/utlist.h
Normal file
File diff suppressed because it is too large
Load Diff
108
c/ext/uthash/src/utringbuffer.h
Normal file
108
c/ext/uthash/src/utringbuffer.h
Normal file
@@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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 */
|
||||||
88
c/ext/uthash/src/utstack.h
Normal file
88
c/ext/uthash/src/utstack.h
Normal file
@@ -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 */
|
||||||
407
c/ext/uthash/src/utstring.h
Normal file
407
c/ext/uthash/src/utstring.h
Normal file
@@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#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 */
|
||||||
120
c/ext/uthash/tests/Makefile
Normal file
120
c/ext/uthash/tests/Makefile
Normal file
@@ -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
|
||||||
132
c/ext/uthash/tests/README
Normal file
132
c/ext/uthash/tests/README
Normal file
@@ -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
|
||||||
|
|
||||||
13
c/ext/uthash/tests/all_funcs
Executable file
13
c/ext/uthash/tests/all_funcs
Executable file
@@ -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
|
||||||
82
c/ext/uthash/tests/bloom_perf.c
Normal file
82
c/ext/uthash/tests/bloom_perf.c
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <sys/time.h> /* gettimeofday */
|
||||||
|
#include <errno.h> /* perror */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
#define BUFLEN 20
|
||||||
|
#if 0
|
||||||
|
#undef uthash_expand_fyi
|
||||||
|
#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct name_rec {
|
||||||
|
char boy_name[BUFLEN];
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} name_rec;
|
||||||
|
|
||||||
|
int main(int argc,char *argv[])
|
||||||
|
{
|
||||||
|
name_rec *name, *names=NULL;
|
||||||
|
char linebuf[BUFLEN];
|
||||||
|
FILE *file;
|
||||||
|
int i=0,j,nloops=3,loopnum=0,miss;
|
||||||
|
struct timeval tv1,tv2;
|
||||||
|
long elapsed_usec;
|
||||||
|
if (argc > 1) {
|
||||||
|
nloops = atoi(argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (file = fopen( "test14.dat", "r" )) == NULL ) {
|
||||||
|
perror("can't open: ");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
i++;
|
||||||
|
if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(name->boy_name, linebuf);
|
||||||
|
HASH_ADD_STR(names,boy_name,name);
|
||||||
|
}
|
||||||
|
|
||||||
|
again:
|
||||||
|
if (fseek(file,0,SEEK_SET) == -1) {
|
||||||
|
fprintf(stderr,"fseek failed: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
j=0;
|
||||||
|
|
||||||
|
if (gettimeofday(&tv1,NULL) == -1) {
|
||||||
|
perror("gettimeofday: ");
|
||||||
|
}
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
/* if we do 10 loops, the first has a 0% miss rate,
|
||||||
|
* the second has a 10% miss rate, etc */
|
||||||
|
miss = ((rand()*1.0/RAND_MAX) < (loopnum*1.0/nloops)) ? 1 : 0;
|
||||||
|
/* generate a miss if we want one */
|
||||||
|
if (miss) {
|
||||||
|
linebuf[0]++;
|
||||||
|
if (linebuf[1] != '\0') {
|
||||||
|
linebuf[1]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HASH_FIND_STR(names,linebuf,name);
|
||||||
|
if (name) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gettimeofday(&tv2,NULL) == -1) {
|
||||||
|
perror("gettimeofday: ");
|
||||||
|
}
|
||||||
|
elapsed_usec = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
|
||||||
|
printf("lookup on %d of %d (%.2f%%) names succeeded (%.2f usec)\n", j, i,
|
||||||
|
j*100.0/i, (double)(elapsed_usec));
|
||||||
|
if (++loopnum < nloops) {
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
17
c/ext/uthash/tests/bloom_perf.sh
Executable file
17
c/ext/uthash/tests/bloom_perf.sh
Executable file
@@ -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
|
||||||
|
|
||||||
21
c/ext/uthash/tests/do_tests
Executable file
21
c/ext/uthash/tests/do_tests
Executable file
@@ -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;
|
||||||
22
c/ext/uthash/tests/do_tests.cygwin
Executable file
22
c/ext/uthash/tests/do_tests.cygwin
Executable file
@@ -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;
|
||||||
20
c/ext/uthash/tests/do_tests.mingw
Normal file
20
c/ext/uthash/tests/do_tests.mingw
Normal file
@@ -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"
|
||||||
16
c/ext/uthash/tests/do_tests_win32.cmd
Normal file
16
c/ext/uthash/tests/do_tests_win32.cmd
Normal file
@@ -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
|
||||||
48
c/ext/uthash/tests/emit_keys.c
Normal file
48
c/ext/uthash/tests/emit_keys.c
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <errno.h> /* perror */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include <unistd.h> /* write */
|
||||||
|
|
||||||
|
/* this define must precede uthash.h */
|
||||||
|
#define HASH_EMIT_KEYS 1
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
#define BUFLEN 30
|
||||||
|
|
||||||
|
typedef struct name_rec {
|
||||||
|
char boy_name[BUFLEN];
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} name_rec;
|
||||||
|
|
||||||
|
int main(int argc,char *argv[])
|
||||||
|
{
|
||||||
|
name_rec *name, *names=NULL;
|
||||||
|
char linebuf[BUFLEN];
|
||||||
|
FILE *file;
|
||||||
|
int i=0;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr,"usage: %s file\n", argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (file = fopen( argv[1], "r" )) == NULL ) {
|
||||||
|
perror("can't open: ");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
name = (name_rec*)malloc(sizeof(name_rec));
|
||||||
|
if (name == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(name->boy_name, linebuf);
|
||||||
|
HASH_ADD_STR(names,boy_name,name);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr,"%d keys emitted.\n", i);
|
||||||
|
fclose(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
149
c/ext/uthash/tests/example.c
Normal file
149
c/ext/uthash/tests/example.c
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#include <stdio.h> /* gets */
|
||||||
|
#include <stdlib.h> /* atoi, malloc */
|
||||||
|
#include <string.h> /* strcpy */
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
struct my_struct {
|
||||||
|
int id; /* key */
|
||||||
|
char name[10];
|
||||||
|
UT_hash_handle hh; /* makes this structure hashable */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct my_struct *users = NULL;
|
||||||
|
|
||||||
|
void add_user(int user_id, char *name)
|
||||||
|
{
|
||||||
|
struct my_struct *s;
|
||||||
|
|
||||||
|
HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */
|
||||||
|
if (s == NULL) {
|
||||||
|
s = (struct my_struct*)malloc(sizeof(struct my_struct));
|
||||||
|
s->id = user_id;
|
||||||
|
HASH_ADD_INT(users, id, s); /* id: name of key field */
|
||||||
|
}
|
||||||
|
strcpy(s->name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct my_struct *find_user(int user_id)
|
||||||
|
{
|
||||||
|
struct my_struct *s;
|
||||||
|
|
||||||
|
HASH_FIND_INT(users, &user_id, s); /* s: output pointer */
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_user(struct my_struct *user)
|
||||||
|
{
|
||||||
|
HASH_DEL(users, user); /* user: pointer to deletee */
|
||||||
|
free(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_all()
|
||||||
|
{
|
||||||
|
struct my_struct *current_user, *tmp;
|
||||||
|
|
||||||
|
HASH_ITER(hh, users, current_user, tmp) {
|
||||||
|
HASH_DEL(users, current_user); /* delete it (users advances to next) */
|
||||||
|
free(current_user); /* free it */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_users()
|
||||||
|
{
|
||||||
|
struct my_struct *s;
|
||||||
|
|
||||||
|
for (s = users; s != NULL; s = (struct my_struct*)(s->hh.next)) {
|
||||||
|
printf("user id %d: name %s\n", s->id, s->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int name_sort(struct my_struct *a, struct my_struct *b)
|
||||||
|
{
|
||||||
|
return strcmp(a->name, b->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int id_sort(struct my_struct *a, struct my_struct *b)
|
||||||
|
{
|
||||||
|
return (a->id - b->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort_by_name()
|
||||||
|
{
|
||||||
|
HASH_SORT(users, name_sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort_by_id()
|
||||||
|
{
|
||||||
|
HASH_SORT(users, id_sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char in[10];
|
||||||
|
int id = 1, running = 1;
|
||||||
|
struct my_struct *s;
|
||||||
|
unsigned num_users;
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
printf(" 1. add user\n");
|
||||||
|
printf(" 2. add/rename user by id\n");
|
||||||
|
printf(" 3. find user\n");
|
||||||
|
printf(" 4. delete user\n");
|
||||||
|
printf(" 5. delete all users\n");
|
||||||
|
printf(" 6. sort items by name\n");
|
||||||
|
printf(" 7. sort items by id\n");
|
||||||
|
printf(" 8. print users\n");
|
||||||
|
printf(" 9. count users\n");
|
||||||
|
printf("10. quit\n");
|
||||||
|
gets(in);
|
||||||
|
switch(atoi(in)) {
|
||||||
|
case 1:
|
||||||
|
printf("name?\n");
|
||||||
|
add_user(id++, gets(in));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
printf("id?\n");
|
||||||
|
gets(in);
|
||||||
|
id = atoi(in);
|
||||||
|
printf("name?\n");
|
||||||
|
add_user(id, gets(in));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
printf("id?\n");
|
||||||
|
s = find_user(atoi(gets(in)));
|
||||||
|
printf("user: %s\n", s ? s->name : "unknown");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
printf("id?\n");
|
||||||
|
s = find_user(atoi(gets(in)));
|
||||||
|
if (s) {
|
||||||
|
delete_user(s);
|
||||||
|
} else {
|
||||||
|
printf("id unknown\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
delete_all();
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
sort_by_name();
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
sort_by_id();
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
print_users();
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
num_users = HASH_COUNT(users);
|
||||||
|
printf("there are %u users\n", num_users);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
running = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_all(); /* free any structures */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
678
c/ext/uthash/tests/hashscan.c
Normal file
678
c/ext/uthash/tests/hashscan.c
Normal file
@@ -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 <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/types.h> /* on OSX, must come before ptrace.h */
|
||||||
|
#include <sys/ptrace.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <sys/param.h> /* MAXPATHLEN */
|
||||||
|
#include <vm/vm.h> /* VM_PROT_* flags */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PT_ATTACH) && !defined(PTRACE_ATTACH)
|
||||||
|
#define PTRACE_ATTACH PT_ATTACH
|
||||||
|
#define PTRACE_DETACH PT_DETACH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* need this defined so offsetof can give us bloom offsets in UT_hash_table */
|
||||||
|
#define HASH_BLOOM 16
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
typedef struct {
|
||||||
|
void *start;
|
||||||
|
void *end;
|
||||||
|
} vma_t;
|
||||||
|
#else
|
||||||
|
typedef struct {
|
||||||
|
off_t start;
|
||||||
|
off_t end;
|
||||||
|
char perms[4]; /* rwxp */
|
||||||
|
char device[5]; /* fd:01 or 00:00 */
|
||||||
|
} vma_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const uint32_t sig = HASH_SIGNATURE;
|
||||||
|
int verbose=0;
|
||||||
|
int getkeys=0;
|
||||||
|
|
||||||
|
#define vv(...) do {if (verbose>0) printf(__VA_ARGS__);} while(0)
|
||||||
|
#define vvv(...) do {if (verbose>1) printf(__VA_ARGS__);} while(0)
|
||||||
|
|
||||||
|
/* these id's are arbitrary, only meaningful within this file */
|
||||||
|
#define JEN 1
|
||||||
|
#define BER 2
|
||||||
|
#define SFH 3
|
||||||
|
#define SAX 4
|
||||||
|
#define FNV 5
|
||||||
|
#define OAT 6
|
||||||
|
#define NUM_HASH_FUNCS 7 /* includes id 0, the non-function */
|
||||||
|
const char *hash_fcns[] = {"???","JEN","BER","SFH","SAX","FNV","OAT"};
|
||||||
|
|
||||||
|
/* given a peer key/len/hashv, reverse engineer its hash function */
|
||||||
|
static int infer_hash_function(char *key, size_t keylen, uint32_t hashv)
|
||||||
|
{
|
||||||
|
uint32_t ohashv;
|
||||||
|
/* BER SAX FNV OAT JEN SFH */
|
||||||
|
HASH_JEN(key,keylen,ohashv);
|
||||||
|
if (ohashv == hashv) {
|
||||||
|
return JEN;
|
||||||
|
}
|
||||||
|
HASH_BER(key,keylen,ohashv);
|
||||||
|
if (ohashv == hashv) {
|
||||||
|
return BER;
|
||||||
|
}
|
||||||
|
HASH_SFH(key,keylen,ohashv);
|
||||||
|
if (ohashv == hashv) {
|
||||||
|
return SFH;
|
||||||
|
}
|
||||||
|
HASH_SAX(key,keylen,ohashv);
|
||||||
|
if (ohashv == hashv) {
|
||||||
|
return SAX;
|
||||||
|
}
|
||||||
|
HASH_FNV(key,keylen,ohashv);
|
||||||
|
if (ohashv == hashv) {
|
||||||
|
return FNV;
|
||||||
|
}
|
||||||
|
HASH_OAT(key,keylen,ohashv);
|
||||||
|
if (ohashv == hashv) {
|
||||||
|
return OAT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read peer's memory from addr for len bytes, store into our dst */
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
static int read_mem(void *dst, pid_t pid, void *start, size_t len)
|
||||||
|
{
|
||||||
|
struct ptrace_io_desc io_desc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
io_desc.piod_op = PIOD_READ_D;
|
||||||
|
io_desc.piod_offs = start;
|
||||||
|
io_desc.piod_addr = dst;
|
||||||
|
io_desc.piod_len = len;
|
||||||
|
|
||||||
|
ret = ptrace(PT_IO, pid, (void *) &io_desc, 0);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
vv("read_mem: ptrace failed: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
} else if (io_desc.piod_len != len) {
|
||||||
|
vv("read_mem: short read!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int read_mem(void *dst, int fd, off_t start, size_t len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
size_t bytes_read=0;
|
||||||
|
if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
|
||||||
|
fprintf(stderr, "lseek failed: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while ( len && ((rc=read(fd, (char*)dst+bytes_read, len)) > 0)) {
|
||||||
|
len -= rc;
|
||||||
|
bytes_read += rc;
|
||||||
|
}
|
||||||
|
if (rc==-1) {
|
||||||
|
vv("read_mem failed (%s)\n",strerror(errno));
|
||||||
|
}
|
||||||
|
if ((len != 0 && rc >= 0)) {
|
||||||
|
vv("INTERNAL ERROR\n");
|
||||||
|
}
|
||||||
|
return (rc == -1) ? -1 : 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* later compensate for possible presence of bloom filter */
|
||||||
|
static char *tbl_from_sig_addr(char *sig)
|
||||||
|
{
|
||||||
|
return (sig - offsetof(UT_hash_table,signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HS_BIT_TEST(v,i) (v[i/8] & (1U << (i%8)))
|
||||||
|
static void found(int fd, char* peer_sig, pid_t pid)
|
||||||
|
{
|
||||||
|
UT_hash_table *tbl=NULL;
|
||||||
|
UT_hash_bucket *bkts=NULL;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
size_t i, bloom_len, bloom_bitlen, bloom_on_bits=0,bloom_off_bits=0;
|
||||||
|
char *peer_tbl, *peer_bloom_sig, *peer_bloom_nbits, *peer_bloombv_ptr,
|
||||||
|
*peer_bloombv, *peer_bkts, *peer_hh, *key=NULL;
|
||||||
|
const char *peer_key;
|
||||||
|
const char *hash_fcn = NULL;
|
||||||
|
unsigned char *bloombv=NULL;
|
||||||
|
static int fileno=0;
|
||||||
|
char keyfile[50];
|
||||||
|
unsigned char bloom_nbits=0;
|
||||||
|
int keyfd=-1, mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
|
||||||
|
hash_fcn_hits[NUM_HASH_FUNCS], hash_fcn_winner;
|
||||||
|
unsigned max_chain=0;
|
||||||
|
uint32_t bloomsig;
|
||||||
|
int has_bloom_filter_fields = 0;
|
||||||
|
|
||||||
|
for(i=0; i < NUM_HASH_FUNCS; i++) {
|
||||||
|
hash_fcn_hits[i]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getkeys) {
|
||||||
|
snprintf(keyfile, sizeof(keyfile), "/tmp/%u-%u.key", (unsigned)pid,fileno++);
|
||||||
|
if ( (keyfd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
|
||||||
|
fprintf(stderr, "can't open %s: %s\n", keyfile, strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vv("found signature at peer %p\n", (void*)peer_sig);
|
||||||
|
peer_tbl = tbl_from_sig_addr(peer_sig);
|
||||||
|
vvv("reading table at peer %p\n", (void*)peer_tbl);
|
||||||
|
|
||||||
|
if ( (tbl = (UT_hash_table*)malloc(sizeof(UT_hash_table))) == NULL) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if (read_mem(tbl, pid, (void *)peer_tbl, sizeof(UT_hash_table)) != 0) {
|
||||||
|
#else
|
||||||
|
if (read_mem(tbl, fd, (off_t)peer_tbl, sizeof(UT_hash_table)) != 0) {
|
||||||
|
#endif
|
||||||
|
fprintf(stderr, "failed to read peer memory\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* got the table. how about the buckets */
|
||||||
|
peer_bkts = (char*)tbl->buckets;
|
||||||
|
vvv("reading %u buckets at peer %p\n", tbl->num_buckets, (void*)peer_bkts);
|
||||||
|
bkts = (UT_hash_bucket*)malloc(sizeof(UT_hash_bucket)*tbl->num_buckets);
|
||||||
|
if (bkts == NULL) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if (read_mem(bkts, pid, (void *)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
|
||||||
|
#else
|
||||||
|
if (read_mem(bkts, fd, (off_t)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
|
||||||
|
#endif
|
||||||
|
fprintf(stderr, "failed to read peer memory\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
vvv("scanning %u peer buckets\n", tbl->num_buckets);
|
||||||
|
for(i=0; i < tbl->num_buckets; i++) {
|
||||||
|
vvv("bucket %u has %u items\n", (unsigned)i, (unsigned)(bkts[i].count));
|
||||||
|
if (bkts[i].count > max_chain) {
|
||||||
|
max_chain = bkts[i].count;
|
||||||
|
}
|
||||||
|
if (bkts[i].expand_mult) {
|
||||||
|
vvv(" bucket %u has expand_mult %u\n", (unsigned)i, (unsigned)(bkts[i].expand_mult));
|
||||||
|
}
|
||||||
|
|
||||||
|
vvv("scanning bucket %u chain:\n", (unsigned)i);
|
||||||
|
peer_hh = (char*)bkts[i].hh_head;
|
||||||
|
while(peer_hh) {
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if (read_mem(&hh, pid, (void *)peer_hh, sizeof(hh)) != 0) {
|
||||||
|
#else
|
||||||
|
if (read_mem(&hh, fd, (off_t)peer_hh, sizeof(hh)) != 0) {
|
||||||
|
#endif
|
||||||
|
fprintf(stderr, "failed to read peer memory\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((char*)hh.tbl != peer_tbl) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
peer_hh = (char*)hh.hh_next;
|
||||||
|
peer_key = (const char*)(hh.key);
|
||||||
|
/* malloc space to read the key, and read it */
|
||||||
|
if ( (key = (char*)malloc(sizeof(hh.keylen))) == NULL) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if (read_mem(key, pid, (void*)peer_key, hh.keylen) != 0) {
|
||||||
|
#else
|
||||||
|
if (read_mem(key, fd, (off_t)peer_key, hh.keylen) != 0) {
|
||||||
|
#endif
|
||||||
|
fprintf(stderr, "failed to read peer memory\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
hash_fcn_hits[infer_hash_function(key,hh.keylen,hh.hashv)]++;
|
||||||
|
/* write the key if requested */
|
||||||
|
if (getkeys) {
|
||||||
|
write(keyfd, &hh.keylen, sizeof(unsigned));
|
||||||
|
write(keyfd, key, hh.keylen);
|
||||||
|
}
|
||||||
|
free(key);
|
||||||
|
key=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* does it have a bloom filter? */
|
||||||
|
peer_bloom_sig = peer_tbl + offsetof(UT_hash_table, bloom_sig);
|
||||||
|
peer_bloombv_ptr = peer_tbl + offsetof(UT_hash_table, bloom_bv);
|
||||||
|
peer_bloom_nbits = peer_tbl + offsetof(UT_hash_table, bloom_nbits);
|
||||||
|
vvv("looking for bloom signature at peer %p\n", (void*)peer_bloom_sig);
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if ((read_mem(&bloomsig, pid, (void *)peer_bloom_sig, sizeof(uint32_t)) == 0) &&
|
||||||
|
(bloomsig == HASH_BLOOM_SIGNATURE)) {
|
||||||
|
#else
|
||||||
|
if ((read_mem(&bloomsig, fd, (off_t)peer_bloom_sig, sizeof(uint32_t)) == 0) &&
|
||||||
|
(bloomsig == HASH_BLOOM_SIGNATURE)) {
|
||||||
|
#endif
|
||||||
|
vvv("bloom signature (%x) found\n",bloomsig);
|
||||||
|
/* bloom found. get at bv, nbits */
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if (read_mem(&bloom_nbits, pid, (void *)peer_bloom_nbits, sizeof(char)) == 0) {
|
||||||
|
#else
|
||||||
|
if (read_mem(&bloom_nbits, fd, (off_t)peer_bloom_nbits, sizeof(char)) == 0) {
|
||||||
|
#endif
|
||||||
|
/* scan bloom filter, calculate saturation */
|
||||||
|
bloom_bitlen = (1ULL << bloom_nbits);
|
||||||
|
bloom_len = (bloom_bitlen / 8) + ((bloom_bitlen % 8) ? 1 : 0);
|
||||||
|
vvv("bloom bitlen is %u, bloom_bytelen is %u\n", (unsigned)bloom_bitlen, (unsigned)bloom_len);
|
||||||
|
if ( (bloombv = (unsigned char*)malloc(bloom_len)) == NULL) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
/* read the address of the bitvector in the peer, then read the bv itself */
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
if ((read_mem(&peer_bloombv, pid, (void *)peer_bloombv_ptr, sizeof(void*)) == 0) &&
|
||||||
|
(read_mem(bloombv, pid, (void *)peer_bloombv, bloom_len) == 0)) {
|
||||||
|
#else
|
||||||
|
if ((read_mem(&peer_bloombv, fd, (off_t)peer_bloombv_ptr, sizeof(void*)) == 0) &&
|
||||||
|
(read_mem(bloombv, fd, (off_t)peer_bloombv, bloom_len) == 0)) {
|
||||||
|
#endif
|
||||||
|
/* calculate saturation */
|
||||||
|
vvv("read peer bloom bitvector from %p (%u bytes)\n", (void*)peer_bloombv, (unsigned)bloom_len);
|
||||||
|
for(i=0; i < bloom_bitlen; i++) {
|
||||||
|
if (HS_BIT_TEST(bloombv,(unsigned)i)) {
|
||||||
|
/* vvv("bit %u set\n",(unsigned)i); */
|
||||||
|
bloom_on_bits++;
|
||||||
|
} else {
|
||||||
|
bloom_off_bits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
has_bloom_filter_fields = 1;
|
||||||
|
vvv("there were %u on_bits among %u total bits\n", (unsigned)bloom_on_bits, (unsigned)bloom_bitlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* choose apparent hash function */
|
||||||
|
hash_fcn_winner=0;
|
||||||
|
for(i=0; i<NUM_HASH_FUNCS; i++) {
|
||||||
|
if (hash_fcn_hits[i] > hash_fcn_hits[hash_fcn_winner]) {
|
||||||
|
hash_fcn_winner=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash_fcn = hash_fcns[hash_fcn_winner];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Address ideal items buckets mc fl bloom sat fcn keys saved to
|
||||||
|
------------------ ----- -------- -------- -- -- ----- ----- --- -------------
|
||||||
|
0x10aa4090 98% 10000000 32000000 10 ok BER /tmp/9110-0.key
|
||||||
|
0x10abcdef 100% 10000000 32000000 9 NX 27 12% BER /tmp/9110-1.key
|
||||||
|
*/
|
||||||
|
printf("Address ideal items buckets mc fl bloom sat fcn keys saved to\n");
|
||||||
|
printf("------------------ ----- -------- -------- -- -- ----- ----- --- -------------\n");
|
||||||
|
if (has_bloom_filter_fields) {
|
||||||
|
printf("%-18p %4.0f%% %8u %8u %2u %2s %5u %4.0f%c %3s %s\n",
|
||||||
|
(void*)peer_tbl,
|
||||||
|
(tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
|
||||||
|
tbl->num_items,
|
||||||
|
tbl->num_buckets,
|
||||||
|
max_chain,
|
||||||
|
tbl->noexpand ? "NX" : "ok",
|
||||||
|
bloom_nbits,
|
||||||
|
bloom_on_bits * 100.0 / bloom_bitlen, '%',
|
||||||
|
hash_fcn,
|
||||||
|
(getkeys ? keyfile : ""));
|
||||||
|
} else {
|
||||||
|
printf("%-18p %4.0f%% %8u %8u %2u %2s %5s %4s%c %3s %s\n",
|
||||||
|
(void*)peer_tbl,
|
||||||
|
(tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
|
||||||
|
tbl->num_items,
|
||||||
|
tbl->num_buckets,
|
||||||
|
max_chain,
|
||||||
|
tbl->noexpand ? "NX" : "ok",
|
||||||
|
"",
|
||||||
|
"", ' ',
|
||||||
|
hash_fcn,
|
||||||
|
(getkeys ? keyfile : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("read peer tbl:\n");
|
||||||
|
printf("num_buckets: %u\n", tbl->num_buckets);
|
||||||
|
printf("num_items: %u\n", tbl->num_items);
|
||||||
|
printf("nonideal_items: %u (%.2f%%)\n", tbl->nonideal_items,
|
||||||
|
tbl->nonideal_items*100.0/tbl->num_items);
|
||||||
|
printf("expand: %s\n", tbl->noexpand ? "inhibited": "normal");
|
||||||
|
if (getkeys) {
|
||||||
|
printf("keys written to %s\n", keyfile);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (bkts) {
|
||||||
|
free(bkts);
|
||||||
|
}
|
||||||
|
if (tbl) {
|
||||||
|
free(tbl);
|
||||||
|
}
|
||||||
|
if (key) {
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
|
if (keyfd != -1) {
|
||||||
|
close(keyfd);
|
||||||
|
}
|
||||||
|
if (bloombv) {
|
||||||
|
free(bloombv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
static void sigscan(pid_t pid, void *start, void *end, uint32_t sig)
|
||||||
|
{
|
||||||
|
struct ptrace_io_desc io_desc;
|
||||||
|
int page_size = getpagesize();
|
||||||
|
char *buf;
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
/* make sure page_size is a multiple of the signature size, code below assumes this */
|
||||||
|
assert(page_size % sizeof(sig) == 0);
|
||||||
|
|
||||||
|
buf = malloc(page_size);
|
||||||
|
|
||||||
|
if (buf == NULL) {
|
||||||
|
fprintf(stderr, "malloc failed in sigscan()\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_desc.piod_op = PIOD_READ_D;
|
||||||
|
io_desc.piod_offs = start;
|
||||||
|
io_desc.piod_addr = buf;
|
||||||
|
io_desc.piod_len = page_size;
|
||||||
|
|
||||||
|
/* read in one page after another and search sig */
|
||||||
|
while(!ptrace(PT_IO, pid, (void *) &io_desc, 0)) {
|
||||||
|
if (io_desc.piod_len != page_size) {
|
||||||
|
fprintf(stderr, "PT_IO returned less than page size in sigscan()\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterate over the the page using the signature size and look for the sig */
|
||||||
|
for (pos = buf; pos < (buf + page_size); pos += sizeof(sig)) {
|
||||||
|
if (*(uint32_t *) pos == sig) {
|
||||||
|
found(pid, (char *) io_desc.piod_offs + (pos - buf), pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 'end' is inclusive (the address of the last valid byte), so if the current offset
|
||||||
|
* plus a page is beyond 'end', we're already done. since all vm map entries consist
|
||||||
|
* of entire pages and 'end' is inclusive, current offset plus one page should point
|
||||||
|
* exactly one byte beyond 'end'. this is assert()ed below to be on the safe side.
|
||||||
|
*/
|
||||||
|
if (io_desc.piod_offs + page_size > end) {
|
||||||
|
assert(io_desc.piod_offs + page_size == (end + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* advance to the next page */
|
||||||
|
io_desc.piod_offs += page_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void sigscan(int fd, off_t start, off_t end, uint32_t sig, pid_t pid)
|
||||||
|
{
|
||||||
|
int rlen;
|
||||||
|
uint32_t u;
|
||||||
|
off_t at=0;
|
||||||
|
|
||||||
|
if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
|
||||||
|
fprintf(stderr, "lseek failed: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( (rlen = read(fd,&u,sizeof(u))) == sizeof(u)) {
|
||||||
|
if (!memcmp(&u,&sig,sizeof(u))) {
|
||||||
|
found(fd, (char*)(start+at),pid);
|
||||||
|
}
|
||||||
|
at += sizeof(u);
|
||||||
|
if ((off_t)(at + sizeof(u)) > end-start) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rlen == -1) {
|
||||||
|
//fprintf(stderr,"read failed: %s\n", strerror(errno));
|
||||||
|
//exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
static int scan(pid_t pid)
|
||||||
|
{
|
||||||
|
vma_t *vmas=NULL, vma;
|
||||||
|
unsigned i, num_vmas = 0;
|
||||||
|
int ret;
|
||||||
|
struct ptrace_vm_entry vm_entry;
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
|
||||||
|
vv("attaching to peer\n");
|
||||||
|
if (ptrace(PT_ATTACH,pid,NULL,0) == -1) {
|
||||||
|
fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
vv("waiting for peer to suspend temporarily\n");
|
||||||
|
if (waitpid(pid,NULL,0) != pid) {
|
||||||
|
fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read memory map using ptrace */
|
||||||
|
vv("listing peer virtual memory areas\n");
|
||||||
|
vm_entry.pve_entry = 0;
|
||||||
|
vm_entry.pve_path = path; /* not used but required to make vm_entry.pve_pathlen work */
|
||||||
|
while(1) {
|
||||||
|
/* set pve_pathlen every turn, it gets overwritten by ptrace */
|
||||||
|
vm_entry.pve_pathlen = MAXPATHLEN;
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
ret = ptrace(PT_VM_ENTRY, pid, (void *) &vm_entry, 0);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
/* we've reached the last entry */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "fetching vm map entry failed: %s (%i)\n", strerror(errno), errno);
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
|
||||||
|
vvv("vmmap entry: start: %p, end: %p", (void *) vm_entry.pve_start, (void *) vm_entry.pve_end);
|
||||||
|
|
||||||
|
/* skip unreadable or vnode-backed entries */
|
||||||
|
if (!(vm_entry.pve_prot & VM_PROT_READ) || vm_entry.pve_pathlen > 0) {
|
||||||
|
vvv(" -> skipped (not readable or vnode-backed)\n");
|
||||||
|
vm_entry.pve_path[0] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* useful entry, add to list */
|
||||||
|
vvv(" -> will be scanned\n");
|
||||||
|
vma.start = (void *)vm_entry.pve_start;
|
||||||
|
vma.end = (void *)vm_entry.pve_end;
|
||||||
|
vmas = (vma_t *) realloc(vmas, (num_vmas + 1) * sizeof(vma_t));
|
||||||
|
if (vmas == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
vmas[num_vmas++] = vma;
|
||||||
|
}
|
||||||
|
|
||||||
|
vv("peer has %u virtual memory areas\n", num_vmas);
|
||||||
|
|
||||||
|
/* look for the hash signature */
|
||||||
|
vv("scanning peer memory for hash table signatures\n");
|
||||||
|
for(i=0; i<num_vmas; i++) {
|
||||||
|
vma = vmas[i];
|
||||||
|
sigscan(pid, vma.start, vma.end, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
die:
|
||||||
|
vv("detaching and resuming peer\n");
|
||||||
|
if (ptrace(PT_DETACH, pid, NULL, 0) == -1) {
|
||||||
|
fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
static int scan(pid_t pid)
|
||||||
|
{
|
||||||
|
FILE *mapf;
|
||||||
|
char mapfile[30], memfile[30], line[100];
|
||||||
|
vma_t *vmas=NULL, vma;
|
||||||
|
unsigned i, num_vmas = 0;
|
||||||
|
int memfd;
|
||||||
|
void *pstart, *pend, *unused;
|
||||||
|
|
||||||
|
/* attach to the target process and wait for it to suspend */
|
||||||
|
vv("attaching to peer\n");
|
||||||
|
if (ptrace(PTRACE_ATTACH, pid, NULL, 0) == -1) {
|
||||||
|
fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
vv("waiting for peer to suspend temporarily\n");
|
||||||
|
if (waitpid(pid,NULL,0) != pid) {
|
||||||
|
fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get ready to open its memory map. this gives us its valid memory areas */
|
||||||
|
snprintf(mapfile,sizeof(mapfile),"/proc/%u/maps",(unsigned)pid);
|
||||||
|
snprintf(memfile,sizeof(memfile),"/proc/%u/mem", (unsigned)pid);
|
||||||
|
vv("opening peer memory map [%s]\n", mapfile);
|
||||||
|
if ( (mapf = fopen(mapfile,"r")) == NULL) {
|
||||||
|
fprintf(stderr,"failed to open %s: %s\n", mapfile, strerror(errno));
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
vv("listing peer virtual memory areas\n");
|
||||||
|
while(fgets(line,sizeof(line),mapf)) {
|
||||||
|
if (sscanf(line, "%p-%p %4c %p %5c", &pstart, &pend, vma.perms,
|
||||||
|
&unused, vma.device) == 5) {
|
||||||
|
vma.start = (off_t)pstart;
|
||||||
|
vma.end = (off_t)pend;
|
||||||
|
if (vma.perms[0] != 'r') {
|
||||||
|
continue; /* only readable vma's */
|
||||||
|
}
|
||||||
|
if (memcmp(vma.device,"fd",2)==0) {
|
||||||
|
continue; /* skip mapped files */
|
||||||
|
}
|
||||||
|
vmas = (vma_t*)realloc(vmas, (num_vmas+1) * sizeof(vma_t));
|
||||||
|
if (vmas == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
vmas[num_vmas++] = vma;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vv("peer has %u virtual memory areas\n",num_vmas);
|
||||||
|
fclose(mapf);
|
||||||
|
|
||||||
|
/* ok, open up its memory and start looking around in there */
|
||||||
|
vv("opening peer memory\n");
|
||||||
|
if ( (memfd=open(memfile,O_RDONLY)) == -1) {
|
||||||
|
fprintf(stderr,"failed to open %s: %s\n", memfile, strerror(errno));
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
/* look for the hash signature */
|
||||||
|
vv("scanning peer memory for hash table signatures\n");
|
||||||
|
for(i=0; i<num_vmas; i++) {
|
||||||
|
vma = vmas[i];
|
||||||
|
pstart = (void*)vma.start;
|
||||||
|
pend = (void*)vma.end;
|
||||||
|
/*fprintf(stderr,"scanning %p-%p %.4s %.5s\n", pstart, pend,
|
||||||
|
vma.perms, vma.device);*/
|
||||||
|
sigscan(memfd, vma.start, vma.end, sig, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* done. close memory and detach. this resumes the target process */
|
||||||
|
close(memfd);
|
||||||
|
|
||||||
|
die:
|
||||||
|
vv("detaching and resuming peer\n");
|
||||||
|
if (ptrace(PTRACE_DETACH, pid, NULL, 0) == -1) {
|
||||||
|
fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static int usage(const char *prog)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"usage: %s [-v] [-k] <pid>\n", prog);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
while ( (opt = getopt(argc, argv, "kv")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
getkeys++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return usage(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc) {
|
||||||
|
pid_t pid = atoi(argv[optind++]);
|
||||||
|
return scan(pid);
|
||||||
|
} else {
|
||||||
|
return usage(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
c/ext/uthash/tests/keystats
Executable file
42
c/ext/uthash/tests/keystats
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
use FindBin;
|
||||||
|
|
||||||
|
sub usage {
|
||||||
|
print "usage: keystats [-v] keyfile\n";
|
||||||
|
print "usage: keystats [-p <pct> [-v]] keyfile\n";
|
||||||
|
exit -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
usage if ((@ARGV == 0) or ($ARGV[0] eq '-h'));
|
||||||
|
|
||||||
|
my @exes = glob "'$FindBin::Bin/keystat.???'";
|
||||||
|
|
||||||
|
my %stats;
|
||||||
|
for my $exe (@exes) {
|
||||||
|
$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]);
|
||||||
|
}
|
||||||
21
c/ext/uthash/tests/lru_cache/Makefile
Normal file
21
c/ext/uthash/tests/lru_cache/Makefile
Normal file
@@ -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
|
||||||
221
c/ext/uthash/tests/lru_cache/cache.c
Normal file
221
c/ext/uthash/tests/lru_cache/cache.c
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* =====================================================================================
|
||||||
|
*
|
||||||
|
* Filename: cache.c
|
||||||
|
*
|
||||||
|
* Description: A simple cache
|
||||||
|
*
|
||||||
|
* Version: 1.0
|
||||||
|
* Created: 04/11/2013 02:31:02 PM
|
||||||
|
* Revision: none
|
||||||
|
* Compiler: gcc
|
||||||
|
*
|
||||||
|
* Author: Oliver Lorenz (ol), olli@olorenz.org
|
||||||
|
* Company: https://olorenz.org
|
||||||
|
* License: This is licensed under the same terms as uthash itself
|
||||||
|
*
|
||||||
|
* =====================================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "cache.h"
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache entry
|
||||||
|
*/
|
||||||
|
struct foo_cache_entry {
|
||||||
|
char *key; /**<The key */
|
||||||
|
void *data; /**<Payload */
|
||||||
|
UT_hash_handle hh; /**<Hash Handle for uthash */
|
||||||
|
};
|
||||||
|
#define KEY_MAX_LENGTH 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache object
|
||||||
|
*/
|
||||||
|
struct foo_cache {
|
||||||
|
size_t max_entries; /**<Amount of entries this cache object can hold */
|
||||||
|
pthread_rwlock_t cache_lock; /**<A lock for concurrent access */
|
||||||
|
struct foo_cache_entry *entries; /**<Head pointer for uthash */
|
||||||
|
void (*free_cb) (void *element);/**<Callback function to free cache entries */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Creates a new cache object
|
||||||
|
|
||||||
|
@param dst
|
||||||
|
Where the newly allocated cache object will be stored in
|
||||||
|
|
||||||
|
@param capacity
|
||||||
|
The maximum number of elements this cache object can hold
|
||||||
|
|
||||||
|
@return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise
|
||||||
|
*/
|
||||||
|
int foo_cache_create(struct foo_cache **dst, const size_t capacity,
|
||||||
|
void (*free_cb) (void *element))
|
||||||
|
{
|
||||||
|
struct foo_cache *new = NULL;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (!dst)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
if ((new = malloc(sizeof(*new))) == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
|
||||||
|
if ((rv = pthread_rwlock_init(&(new->cache_lock), NULL)) != 0)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
new->max_entries = capacity;
|
||||||
|
new->entries = NULL;
|
||||||
|
new->free_cb = free_cb;
|
||||||
|
*dst = new;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
if (new)
|
||||||
|
free(new);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Frees an allocated cache object
|
||||||
|
|
||||||
|
@param cache
|
||||||
|
The cache object to free
|
||||||
|
|
||||||
|
@param keep_data
|
||||||
|
Whether to free contained data or just delete references to it
|
||||||
|
|
||||||
|
@return EINVAL if cache is NULL, 0 otherwise
|
||||||
|
*/
|
||||||
|
int foo_cache_delete(struct foo_cache *cache, int keep_data)
|
||||||
|
{
|
||||||
|
struct foo_cache_entry *entry, *tmp;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (!cache)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
rv = pthread_rwlock_wrlock(&(cache->cache_lock));
|
||||||
|
if (rv)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
if (keep_data) {
|
||||||
|
HASH_CLEAR(hh, cache->entries);
|
||||||
|
} else {
|
||||||
|
HASH_ITER(hh, cache->entries, entry, tmp) {
|
||||||
|
HASH_DEL(cache->entries, entry);
|
||||||
|
if (cache->free_cb)
|
||||||
|
cache->free_cb(entry->data);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void)pthread_rwlock_unlock(&(cache->cache_lock));
|
||||||
|
(void)pthread_rwlock_destroy(&(cache->cache_lock));
|
||||||
|
free(cache);
|
||||||
|
cache = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks if a given key is in the cache
|
||||||
|
|
||||||
|
@param cache
|
||||||
|
The cache object
|
||||||
|
|
||||||
|
@param key
|
||||||
|
The key to look-up
|
||||||
|
|
||||||
|
@param result
|
||||||
|
Where to store the result if key is found.
|
||||||
|
|
||||||
|
A warning: Even though result is just a pointer,
|
||||||
|
you have to call this function with a **ptr,
|
||||||
|
otherwise this will blow up in your face.
|
||||||
|
|
||||||
|
@return EINVAL if cache is NULL, 0 otherwise
|
||||||
|
*/
|
||||||
|
int foo_cache_lookup(struct foo_cache *cache, char *key, void *result)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
struct foo_cache_entry *tmp = NULL;
|
||||||
|
char **dirty_hack = result;
|
||||||
|
|
||||||
|
if (!cache || !key || !result)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
rv = pthread_rwlock_wrlock(&(cache->cache_lock));
|
||||||
|
if (rv)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
HASH_FIND_STR(cache->entries, key, tmp);
|
||||||
|
if (tmp) {
|
||||||
|
size_t key_len = strnlen(tmp->key, KEY_MAX_LENGTH);
|
||||||
|
HASH_DELETE(hh, cache->entries, tmp);
|
||||||
|
HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
|
||||||
|
*dirty_hack = tmp->data;
|
||||||
|
} else {
|
||||||
|
*dirty_hack = result = NULL;
|
||||||
|
}
|
||||||
|
rv = pthread_rwlock_unlock(&(cache->cache_lock));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Inserts a given <key, value> pair into the cache
|
||||||
|
|
||||||
|
@param cache
|
||||||
|
The cache object
|
||||||
|
|
||||||
|
@param key
|
||||||
|
The key that identifies <value>
|
||||||
|
|
||||||
|
@param data
|
||||||
|
Data associated with <key>
|
||||||
|
|
||||||
|
@return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise
|
||||||
|
*/
|
||||||
|
int foo_cache_insert(struct foo_cache *cache, char *key, void *data)
|
||||||
|
{
|
||||||
|
struct foo_cache_entry *entry = NULL;
|
||||||
|
struct foo_cache_entry *tmp_entry = NULL;
|
||||||
|
size_t key_len = 0;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (!cache || !data)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
if ((entry = malloc(sizeof(*entry))) == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
|
||||||
|
if ((rv = pthread_rwlock_wrlock(&(cache->cache_lock))) != 0)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->data = data;
|
||||||
|
key_len = strnlen(entry->key, KEY_MAX_LENGTH);
|
||||||
|
HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry);
|
||||||
|
|
||||||
|
if (HASH_COUNT(cache->entries) >= cache->max_entries) {
|
||||||
|
HASH_ITER(hh, cache->entries, entry, tmp_entry) {
|
||||||
|
HASH_DELETE(hh, cache->entries, entry);
|
||||||
|
if (cache->free_cb)
|
||||||
|
cache->free_cb(entry->data);
|
||||||
|
else
|
||||||
|
free(entry->data);
|
||||||
|
/* free(key->key) if data has been copied */
|
||||||
|
free(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = pthread_rwlock_unlock(&(cache->cache_lock));
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
if (entry)
|
||||||
|
free(entry);
|
||||||
|
(void)pthread_rwlock_unlock(&(cache->cache_lock));
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
}
|
||||||
31
c/ext/uthash/tests/lru_cache/cache.h
Normal file
31
c/ext/uthash/tests/lru_cache/cache.h
Normal file
@@ -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
|
||||||
191
c/ext/uthash/tests/lru_cache/main.c
Normal file
191
c/ext/uthash/tests/lru_cache/main.c
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "cache.h"
|
||||||
|
|
||||||
|
#define MAX_RANDOM_ENTRIES 32
|
||||||
|
|
||||||
|
struct key_record {
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
int generate_random_entry(struct key_record **entry);
|
||||||
|
int generate_random_string(char **dst, const size_t len);
|
||||||
|
void free_random_entry(void *entry);
|
||||||
|
|
||||||
|
void *producer(void *arg)
|
||||||
|
{
|
||||||
|
struct foo_cache *cache = arg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_RANDOM_ENTRIES; i++) {
|
||||||
|
struct key_record *entry = NULL;
|
||||||
|
if (generate_random_entry(&entry)) {
|
||||||
|
fprintf(stderr, "generate_random_entry() failed\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#if defined(DEBUG)
|
||||||
|
printf("Random Entry:\n");
|
||||||
|
printf(" key: %s\n", entry->key);
|
||||||
|
printf(" Key: %s\n", entry->value);
|
||||||
|
#else
|
||||||
|
printf("inserted %s (%d)\n", entry->key,
|
||||||
|
(int)strlen(entry->key));
|
||||||
|
#endif
|
||||||
|
if (foo_cache_insert(cache, entry->key, entry)) {
|
||||||
|
fprintf(stderr, "foo_cache_insert() failed\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *consumer(void *arg)
|
||||||
|
{
|
||||||
|
struct foo_cache *cache = arg;
|
||||||
|
struct key_record *result = NULL;
|
||||||
|
char *buffer = malloc(64);
|
||||||
|
char key[33];
|
||||||
|
int stop = 0;
|
||||||
|
|
||||||
|
if (!buffer)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* give producer time to populate the cache */
|
||||||
|
sleep(2);
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
do {
|
||||||
|
memset(key, 0, 64);
|
||||||
|
result = NULL;
|
||||||
|
|
||||||
|
printf("Enter key for lookup: ");
|
||||||
|
fgets(buffer, sizeof(key), stdin);
|
||||||
|
sscanf(buffer, "%s\n", key);
|
||||||
|
/* read '\n' from stdin */
|
||||||
|
getchar();
|
||||||
|
|
||||||
|
if (strncmp(key, "exit", 4) == 0) {
|
||||||
|
stop = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Got key %s (%d)\n", key, (int)strlen(key));
|
||||||
|
|
||||||
|
if (foo_cache_lookup(cache, key, &result)) {
|
||||||
|
fprintf(stderr, "Could not retrieve key %s\n", key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
printf("MISS\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("HIT\n");
|
||||||
|
printf("key: %s\n", result->key);
|
||||||
|
printf("key : %s\n", result->value);
|
||||||
|
} while (!stop);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (buffer)
|
||||||
|
free(buffer);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
struct foo_cache *cache = NULL;
|
||||||
|
pthread_t workers[2];
|
||||||
|
|
||||||
|
rv = foo_cache_create(&cache, MAX_RANDOM_ENTRIES / 2,
|
||||||
|
free_random_entry);
|
||||||
|
if (rv) {
|
||||||
|
fprintf(stderr, "Could not create cache\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)pthread_create(&workers[0], NULL, producer, (void *)cache);
|
||||||
|
(void)pthread_create(&workers[1], NULL, consumer, (void *)cache);
|
||||||
|
|
||||||
|
pthread_join(workers[0], NULL);
|
||||||
|
pthread_join(workers[1], NULL);
|
||||||
|
|
||||||
|
(void)foo_cache_delete(cache, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int generate_random_entry(struct key_record **entry)
|
||||||
|
{
|
||||||
|
struct key_record *new = NULL;
|
||||||
|
char *key = NULL;
|
||||||
|
char *value = NULL;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
rv = generate_random_string(&key, 33);
|
||||||
|
if (rv)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
rv = generate_random_string(&value, 129);
|
||||||
|
if (rv)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
if ((new = malloc(sizeof(*new))) == NULL) {
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
new->key = key;
|
||||||
|
new->value = value;
|
||||||
|
|
||||||
|
*entry = new;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int generate_random_string(char **dst, const size_t len)
|
||||||
|
{
|
||||||
|
static const char alphanum[] =
|
||||||
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
size_t i;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
if (!dst || len == 0)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
if ((s = malloc(len)) == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < len - 1; i++) {
|
||||||
|
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
*dst = s;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_random_entry(void *entry)
|
||||||
|
{
|
||||||
|
#if defined(DEBUG)
|
||||||
|
fprintf(stderr, "In %s: entry @ %p\n", __func__, entry);
|
||||||
|
#endif
|
||||||
|
struct key_record *record = entry;
|
||||||
|
if (!record)
|
||||||
|
return;
|
||||||
|
if (record->key)
|
||||||
|
free(record->key);
|
||||||
|
if (record->value)
|
||||||
|
free(record->value);
|
||||||
|
free(record);
|
||||||
|
record = NULL;
|
||||||
|
}
|
||||||
28
c/ext/uthash/tests/simkeys.pl
Executable file
28
c/ext/uthash/tests/simkeys.pl
Executable file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
32
c/ext/uthash/tests/sleep_test.c
Normal file
32
c/ext/uthash/tests/sleep_test.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include <unistd.h> /* getpid */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10000; i++) {
|
||||||
|
if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("pid: %u\n", (unsigned)getpid());
|
||||||
|
/* printf("sig: %p\n", &users->hh.tbl->signature); */
|
||||||
|
/* printf("bbv: %p\n", &users->hh.tbl->bloom_bv); */
|
||||||
|
sleep(60*10);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
34
c/ext/uthash/tests/tdiff.cpp
Normal file
34
c/ext/uthash/tests/tdiff.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Windows does not have unix diff so this is a simple replacement
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
using namespace std;
|
||||||
|
int main(int argc, char *argv[] ) {
|
||||||
|
int rc=-1;
|
||||||
|
if (argc != 3) {
|
||||||
|
cout << "usage: " << argv[0] << " file1 file2\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char *file1 = argv[1];
|
||||||
|
char *file2 = argv[2];
|
||||||
|
ifstream is1(file1, ios::in);
|
||||||
|
ifstream is2(file2, ios::in);
|
||||||
|
if (is1.fail()) {cerr << "failed to open " << file1 << "\n"; goto done;}
|
||||||
|
if (is2.fail()) {cerr << "failed to open " << file2 << "\n"; goto done;}
|
||||||
|
char d1[256], d2[256];
|
||||||
|
do {
|
||||||
|
is1.read(d1,sizeof(d1));
|
||||||
|
is2.read(d2,sizeof(d2));
|
||||||
|
if ((is1.gcount() != is2.gcount()) || memcmp(d1,d2,is1.gcount())) {
|
||||||
|
cout << file1 << " and " << file2 << " differ\n";
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
} while (!is1.eof() && !is2.eof());
|
||||||
|
|
||||||
|
rc=0;
|
||||||
|
|
||||||
|
done:
|
||||||
|
is1.close();
|
||||||
|
is2.close();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
10
c/ext/uthash/tests/test1.ans
Normal file
10
c/ext/uthash/tests/test1.ans
Normal file
@@ -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
|
||||||
31
c/ext/uthash/tests/test1.c
Normal file
31
c/ext/uthash/tests/test1.c
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
4
c/ext/uthash/tests/test10.ans
Normal file
4
c/ext/uthash/tests/test10.ans
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
9 found in hh
|
||||||
|
9 found in alth
|
||||||
|
10 not found in hh
|
||||||
|
10 found in alth
|
||||||
54
c/ext/uthash/tests/test10.c
Normal file
54
c/ext/uthash/tests/test10.c
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
UT_hash_handle alth;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *tmp, *users=NULL, *altusers=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<1000; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
if (i<10) {
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
HASH_ADD(alth,altusers,id,sizeof(int),user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("hh items: %d, alth items: %d\n",
|
||||||
|
users->hh.tbl->num_items, users->alth.tbl->num_items);
|
||||||
|
printf("hh buckets: %d, alth buckets: %d\n",
|
||||||
|
users->hh.tbl->num_buckets, users->alth.tbl->num_buckets);
|
||||||
|
*/
|
||||||
|
|
||||||
|
i=9;
|
||||||
|
HASH_FIND_INT(users,&i,tmp);
|
||||||
|
printf("%d %s in hh\n", i, (tmp != NULL) ? "found" : "not found");
|
||||||
|
HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
|
||||||
|
printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
|
||||||
|
|
||||||
|
i=10;
|
||||||
|
HASH_FIND_INT(users,&i,tmp);
|
||||||
|
printf("%d %s in hh\n", i, (tmp != NULL) ? "found" : "not found");
|
||||||
|
HASH_FIND(alth,altusers,&i,sizeof(int),tmp);
|
||||||
|
printf("%d %s in alth\n", i, (tmp != NULL) ? "found" : "not found");
|
||||||
|
|
||||||
|
HASH_CLEAR(hh, users);
|
||||||
|
HASH_CLEAR(alth, altusers);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
51
c/ext/uthash/tests/test11.ans
Normal file
51
c/ext/uthash/tests/test11.ans
Normal file
@@ -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
|
||||||
57
c/ext/uthash/tests/test11.c
Normal file
57
c/ext/uthash/tests/test11.c
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <errno.h> /* perror */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
#define BUFLEN 20
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Print a message if the hash's no-expand flag is set. */
|
||||||
|
#undef uthash_noexpand_fyi
|
||||||
|
#undef uthash_expand_fyi
|
||||||
|
#define uthash_noexpand_fyi(tbl) printf("noexpand set\n");
|
||||||
|
#define uthash_expand_fyi(tbl) printf("hash expanded\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct name_rec {
|
||||||
|
char boy_name[BUFLEN];
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} name_rec;
|
||||||
|
|
||||||
|
static int namecmp(void *_a, void *_b)
|
||||||
|
{
|
||||||
|
name_rec *a = (name_rec*)_a;
|
||||||
|
name_rec *b = (name_rec*)_b;
|
||||||
|
return strcmp(a->boy_name,b->boy_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
name_rec *name, *names=NULL;
|
||||||
|
char linebuf[BUFLEN];
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
file = fopen( "test11.dat", "r" );
|
||||||
|
if (file == NULL) {
|
||||||
|
perror("can't open: ");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
name = (name_rec*)malloc(sizeof(name_rec));
|
||||||
|
if (name == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(name->boy_name, linebuf);
|
||||||
|
HASH_ADD_STR(names,boy_name,name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
HASH_SORT(names,namecmp);
|
||||||
|
for(name=names; name!=NULL; name=(name_rec*)(name->hh.next)) {
|
||||||
|
printf("%s",name->boy_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
51
c/ext/uthash/tests/test11.dat
Normal file
51
c/ext/uthash/tests/test11.dat
Normal file
@@ -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
|
||||||
20
c/ext/uthash/tests/test12.ans
Normal file
20
c/ext/uthash/tests/test12.ans
Normal file
@@ -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)
|
||||||
40
c/ext/uthash/tests/test12.c
Normal file
40
c/ext/uthash/tests/test12.c
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
|
||||||
|
typedef struct person_t {
|
||||||
|
char first_name[10];
|
||||||
|
int id;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} person_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
person_t *people=NULL, *person;
|
||||||
|
const char **name;
|
||||||
|
const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art",
|
||||||
|
"gil", "buck", "ted", NULL
|
||||||
|
};
|
||||||
|
int id=0;
|
||||||
|
|
||||||
|
for(name=names; *name != NULL; name++) {
|
||||||
|
person = (person_t*)malloc(sizeof(person_t));
|
||||||
|
if (person == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(person->first_name, *name);
|
||||||
|
person->id = id++;
|
||||||
|
HASH_ADD_STR(people,first_name,person);
|
||||||
|
printf("added %s (id %d)\n", person->first_name, person->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(name=names; *name != NULL; name++) {
|
||||||
|
HASH_FIND_STR(people,*name,person);
|
||||||
|
if (person != NULL) {
|
||||||
|
printf("found %s (id %d)\n", person->first_name, person->id);
|
||||||
|
} else {
|
||||||
|
printf("failed to find %s\n", *name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
c/ext/uthash/tests/test13.ans
Normal file
5
c/ext/uthash/tests/test13.ans
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
id 9, following prev...
|
||||||
|
id 7, following prev...
|
||||||
|
id 5, following prev...
|
||||||
|
id 3, following prev...
|
||||||
|
id 1, following prev...
|
||||||
50
c/ext/uthash/tests/test13.c
Normal file
50
c/ext/uthash/tests/test13.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *tmp, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete each even ID */
|
||||||
|
for(i=0; i<10; i+=2) {
|
||||||
|
HASH_FIND_INT(users,&i,tmp);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
HASH_DEL(users,tmp);
|
||||||
|
free(tmp);
|
||||||
|
} else {
|
||||||
|
printf("user id %d not found\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i=9;
|
||||||
|
HASH_FIND_INT(users,&i,tmp);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
while (tmp != NULL) {
|
||||||
|
printf("id %d, following prev...\n", tmp->id);
|
||||||
|
tmp = (example_user_t*)tmp->hh.prev;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("user id %d not found\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
c/ext/uthash/tests/test14.ans
Normal file
1
c/ext/uthash/tests/test14.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
lookup on 1219 of 1219 names succeeded
|
||||||
54
c/ext/uthash/tests/test14.c
Normal file
54
c/ext/uthash/tests/test14.c
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <errno.h> /* perror */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
#define BUFLEN 20
|
||||||
|
#if 0
|
||||||
|
#undef uthash_expand_fyi
|
||||||
|
#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct name_rec {
|
||||||
|
char boy_name[BUFLEN];
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} name_rec;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
name_rec *name, *names=NULL;
|
||||||
|
char linebuf[BUFLEN];
|
||||||
|
FILE *file;
|
||||||
|
int i=0,j=0;
|
||||||
|
|
||||||
|
file = fopen( "test14.dat", "r" );
|
||||||
|
if (file == NULL ) {
|
||||||
|
perror("can't open: ");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
i++;
|
||||||
|
name = (name_rec*)malloc(sizeof(name_rec));
|
||||||
|
if (name == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(name->boy_name, linebuf);
|
||||||
|
HASH_ADD_STR(names,boy_name,name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(file,0L,SEEK_SET);
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
HASH_FIND_STR(names,linebuf,name);
|
||||||
|
if (!name) {
|
||||||
|
printf("failed to find: %s", linebuf);
|
||||||
|
} else {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
printf("lookup on %d of %d names succeeded\n", j, i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
1219
c/ext/uthash/tests/test14.dat
Normal file
1219
c/ext/uthash/tests/test14.dat
Normal file
File diff suppressed because it is too large
Load Diff
1
c/ext/uthash/tests/test15.ans
Normal file
1
c/ext/uthash/tests/test15.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
betty's id is 2
|
||||||
40
c/ext/uthash/tests/test15.c
Normal file
40
c/ext/uthash/tests/test15.c
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include <string.h> /* strcpy */
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
struct my_struct {
|
||||||
|
char name[10]; /* key */
|
||||||
|
int id;
|
||||||
|
UT_hash_handle hh; /* makes this structure hashable */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
const char **n, *names[] = { "joe", "bob", "betty", NULL };
|
||||||
|
struct my_struct *s, *tmp, *users = NULL;
|
||||||
|
int i=0;
|
||||||
|
|
||||||
|
for (n = names; *n != NULL; n++) {
|
||||||
|
s = (struct my_struct*)malloc(sizeof(struct my_struct));
|
||||||
|
if (s == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(s->name, *n);
|
||||||
|
s->id = i++;
|
||||||
|
HASH_ADD_STR( users, name, s );
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_FIND_STR( users, "betty", s);
|
||||||
|
if (s != NULL) {
|
||||||
|
printf("betty's id is %d\n", s->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free the hash table contents */
|
||||||
|
HASH_ITER(hh, users, s, tmp) {
|
||||||
|
HASH_DEL(users, s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
c/ext/uthash/tests/test16.ans
Normal file
1
c/ext/uthash/tests/test16.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
found: user 5, unix time 157680000
|
||||||
53
c/ext/uthash/tests/test16.c
Normal file
53
c/ext/uthash/tests/test16.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stddef.h> /* offsetof */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include <string.h> /* memset */
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
struct inner {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct my_event {
|
||||||
|
struct inner is; /* key is aggregate of this field */
|
||||||
|
char event_code; /* and this field. */
|
||||||
|
int user_id;
|
||||||
|
UT_hash_handle hh; /* makes this structure hashable */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
struct my_event *e, ev, *events = NULL;
|
||||||
|
unsigned keylen;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
keylen = offsetof(struct my_event, event_code) + sizeof(char)
|
||||||
|
- offsetof(struct my_event, is);
|
||||||
|
|
||||||
|
for(i = 0; i < 10; i++) {
|
||||||
|
e = (struct my_event*)malloc(sizeof(struct my_event));
|
||||||
|
if (e == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
memset(e,0,sizeof(struct my_event));
|
||||||
|
e->is.a = i * (60*60*24*365); /* i years (sec)*/
|
||||||
|
e->is.b = 0;
|
||||||
|
e->event_code = 'a'+(i%2); /* meaningless */
|
||||||
|
e->user_id = i;
|
||||||
|
|
||||||
|
HASH_ADD( hh, events, is, keylen, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look for one specific event */
|
||||||
|
memset(&ev,0,sizeof(struct my_event));
|
||||||
|
ev.is.a = 5 * (60*60*24*365);
|
||||||
|
ev.is.b = 0;
|
||||||
|
ev.event_code = 'b';
|
||||||
|
HASH_FIND( hh, events, &ev.is, keylen , e);
|
||||||
|
if (e != NULL) {
|
||||||
|
printf("found: user %d, unix time %d\n", e->user_id, e->is.a);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
134
c/ext/uthash/tests/test17.ans
Normal file
134
c/ext/uthash/tests/test17.ans
Normal file
@@ -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
|
||||||
63
c/ext/uthash/tests/test17.c
Normal file
63
c/ext/uthash/tests/test17.c
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
static int rev(void *_a, void *_b)
|
||||||
|
{
|
||||||
|
example_user_t *a = (example_user_t*)_a;
|
||||||
|
example_user_t *b = (example_user_t*)_b;
|
||||||
|
printf("called for a:%d, b:%d\n",a->id, b->id);
|
||||||
|
return (a->id - b->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=9; i>=0; i--) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
printf("sorting\n");
|
||||||
|
HASH_SORT(users,rev);
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("adding 10-20\n");
|
||||||
|
for(i=20; i>=10; i--) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
printf("sorting\n");
|
||||||
|
HASH_SORT(users,rev);
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
20
c/ext/uthash/tests/test18.ans
Normal file
20
c/ext/uthash/tests/test18.ans
Normal file
@@ -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
|
||||||
37
c/ext/uthash/tests/test18.c
Normal file
37
c/ext/uthash/tests/test18.c
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)user->hh.next) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete them all, pathologically */
|
||||||
|
while(users != NULL) {
|
||||||
|
printf("deleting id %i\n", users->id);
|
||||||
|
HASH_DEL(users,users); /* single head/deletee var! */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1012
c/ext/uthash/tests/test19.ans
Normal file
1012
c/ext/uthash/tests/test19.ans
Normal file
File diff suppressed because it is too large
Load Diff
66
c/ext/uthash/tests/test19.c
Normal file
66
c/ext/uthash/tests/test19.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
UT_hash_handle alth;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
static int ascending_sort(void *_a, void *_b)
|
||||||
|
{
|
||||||
|
example_user_t *a = (example_user_t*)_a;
|
||||||
|
example_user_t *b = (example_user_t*)_b;
|
||||||
|
if (a->id == b->id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (a->id < b->id) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int descending_sort(void *_a, void *_b)
|
||||||
|
{
|
||||||
|
example_user_t *a = (example_user_t*)_a;
|
||||||
|
example_user_t *b = (example_user_t*)_b;
|
||||||
|
if (a->id == b->id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (a->id < b->id) ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *users=NULL, *altusers=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<1000; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
if (i<10) {
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
HASH_ADD(alth,altusers,id,sizeof(int),user);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("sorting users ascending\n");
|
||||||
|
HASH_SRT(hh,users,ascending_sort);
|
||||||
|
for(user=users; user!=NULL; user=(example_user_t*)user->hh.next) {
|
||||||
|
printf("user %d\n", user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("sorting altusers descending\n");
|
||||||
|
HASH_SRT(alth,altusers,descending_sort);
|
||||||
|
for(user=altusers; user!=NULL; user=(example_user_t*)user->alth.next) {
|
||||||
|
printf("altuser %d\n", user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HASH_FSCK(hh,users); */
|
||||||
|
/* HASH_FSCK(alth,altusers); */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
c/ext/uthash/tests/test2.ans
Normal file
5
c/ext/uthash/tests/test2.ans
Normal file
@@ -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
|
||||||
38
c/ext/uthash/tests/test2.c
Normal file
38
c/ext/uthash/tests/test2.c
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *tmp, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find each even ID */
|
||||||
|
for(i=0; i<10; i+=2) {
|
||||||
|
HASH_FIND_INT(users,&i,tmp);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
printf("user id %d found, cookie %d\n", tmp->id, tmp->cookie);
|
||||||
|
} else {
|
||||||
|
printf("user id %d not found\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
c/ext/uthash/tests/test20.ans
Normal file
1
c/ext/uthash/tests/test20.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
found
|
||||||
34
c/ext/uthash/tests/test20.c
Normal file
34
c/ext/uthash/tests/test20.c
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <string.h> /* memcpy */
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
struct my_struct {
|
||||||
|
char bkey[5]; /* "binary" key */
|
||||||
|
int data;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
struct my_struct *s, *t, *bins = NULL;
|
||||||
|
char binary[5] = {'\3','\1','\4','\1','\6'};
|
||||||
|
|
||||||
|
/* allocate our structure. initialize to some values */
|
||||||
|
s = (struct my_struct*)calloc(1UL,sizeof(struct my_struct));
|
||||||
|
if (s == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
memcpy(s->bkey, binary, sizeof(binary));
|
||||||
|
|
||||||
|
/* add to hash table using general macro */
|
||||||
|
HASH_ADD( hh, bins, bkey, sizeof(binary), s);
|
||||||
|
|
||||||
|
/* look up the structure we just added */
|
||||||
|
HASH_FIND( hh, bins, binary, sizeof(binary), t );
|
||||||
|
|
||||||
|
if (t != NULL) {
|
||||||
|
printf("found\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
c/ext/uthash/tests/test21.ans
Normal file
1
c/ext/uthash/tests/test21.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
found a 1
|
||||||
44
c/ext/uthash/tests/test21.c
Normal file
44
c/ext/uthash/tests/test21.c
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char a;
|
||||||
|
int b;
|
||||||
|
} record_key_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
record_key_t key;
|
||||||
|
/* ... other data ... */
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} record_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
record_t l, *p, *r, *tmp, *records = NULL;
|
||||||
|
|
||||||
|
r = (record_t*)malloc( sizeof(record_t) );
|
||||||
|
if (r == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
memset(r, 0, sizeof(record_t));
|
||||||
|
r->key.a = 'a';
|
||||||
|
r->key.b = 1;
|
||||||
|
HASH_ADD(hh, records, key, sizeof(record_key_t), r);
|
||||||
|
|
||||||
|
memset(&l, 0, sizeof(record_t));
|
||||||
|
l.key.a = 'a';
|
||||||
|
l.key.b = 1;
|
||||||
|
HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);
|
||||||
|
|
||||||
|
if (p != NULL) {
|
||||||
|
printf("found %c %d\n", p->key.a, p->key.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
HASH_ITER(hh, records, p, tmp) {
|
||||||
|
HASH_DEL(records, p);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
1
c/ext/uthash/tests/test22.ans
Normal file
1
c/ext/uthash/tests/test22.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
found
|
||||||
68
c/ext/uthash/tests/test22.c
Normal file
68
c/ext/uthash/tests/test22.c
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stddef.h> /* offsetof */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include <string.h> /* memset */
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
#define UTF32 '\x1'
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UT_hash_handle hh;
|
||||||
|
size_t len;
|
||||||
|
char encoding; /* these two fields */
|
||||||
|
int text[]; /* comprise the key */
|
||||||
|
} msg_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char encoding;
|
||||||
|
int text[];
|
||||||
|
} lookup_key_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
unsigned keylen;
|
||||||
|
msg_t *msg, *tmp, *msgs = NULL;
|
||||||
|
lookup_key_t *lookup_key;
|
||||||
|
|
||||||
|
int beijing[] = {0x5317, 0x4eac}; /* UTF-32LE for 北京 */
|
||||||
|
|
||||||
|
/* allocate and initialize our structure */
|
||||||
|
msg = (msg_t*)malloc( sizeof(msg_t) + sizeof(beijing) );
|
||||||
|
if (msg == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */
|
||||||
|
msg->len = sizeof(beijing);
|
||||||
|
msg->encoding = UTF32;
|
||||||
|
memcpy(msg->text, beijing, sizeof(beijing));
|
||||||
|
|
||||||
|
/* calculate the key length including padding, using formula */
|
||||||
|
keylen = offsetof(msg_t, text) /* offset of last key field */
|
||||||
|
+ sizeof(beijing) /* size of last key field */
|
||||||
|
- offsetof(msg_t, encoding); /* offset of first key field */
|
||||||
|
|
||||||
|
/* add our structure to the hash table */
|
||||||
|
HASH_ADD( hh, msgs, encoding, keylen, msg);
|
||||||
|
|
||||||
|
/* look it up to prove that it worked :-) */
|
||||||
|
msg=NULL;
|
||||||
|
|
||||||
|
lookup_key = (lookup_key_t*)malloc(sizeof(*lookup_key) + sizeof(beijing));
|
||||||
|
if (lookup_key == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing));
|
||||||
|
lookup_key->encoding = UTF32;
|
||||||
|
memcpy(lookup_key->text, beijing, sizeof(beijing));
|
||||||
|
HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg );
|
||||||
|
if (msg != NULL) {
|
||||||
|
printf("found \n");
|
||||||
|
}
|
||||||
|
free(lookup_key);
|
||||||
|
|
||||||
|
HASH_ITER(hh, msgs, msg, tmp) {
|
||||||
|
HASH_DEL(msgs, msg);
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
6
c/ext/uthash/tests/test23.ans
Normal file
6
c/ext/uthash/tests/test23.ans
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
found 12345
|
||||||
|
found 6789
|
||||||
|
found 98765
|
||||||
|
deleting 12345
|
||||||
|
deleting 6789
|
||||||
|
deleting 98765
|
||||||
69
c/ext/uthash/tests/test23.c
Normal file
69
c/ext/uthash/tests/test23.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "uthash.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int key;
|
||||||
|
int data;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} item;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
item *i, *j, *items=NULL;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
/* first item */
|
||||||
|
k = 12345;
|
||||||
|
i = (item*)malloc(sizeof(item));
|
||||||
|
if (i == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
i->key = k;
|
||||||
|
i->data = 0;
|
||||||
|
HASH_ADD_INT(items,key,i);
|
||||||
|
|
||||||
|
/* second item */
|
||||||
|
k = 6789;
|
||||||
|
i = (item*)malloc(sizeof(item));
|
||||||
|
if (i == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
i->key = k;
|
||||||
|
i->data = 0;
|
||||||
|
HASH_ADD_INT(items,key,i);
|
||||||
|
|
||||||
|
/* third item */
|
||||||
|
k = 98765;
|
||||||
|
i = (item*)malloc(sizeof(item));
|
||||||
|
if (i == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
i->key = k;
|
||||||
|
i->data = 0;
|
||||||
|
HASH_ADD_INT(items,key,i);
|
||||||
|
|
||||||
|
/* look them all up */
|
||||||
|
k = 12345;
|
||||||
|
HASH_FIND_INT(items, &k, j);
|
||||||
|
if (j != NULL) {
|
||||||
|
printf("found %d\n",k);
|
||||||
|
}
|
||||||
|
k = 6789;
|
||||||
|
HASH_FIND_INT(items, &k, j);
|
||||||
|
if (j != NULL) {
|
||||||
|
printf("found %d\n",k);
|
||||||
|
}
|
||||||
|
k = 98765;
|
||||||
|
HASH_FIND_INT(items, &k, j);
|
||||||
|
if (j != NULL) {
|
||||||
|
printf("found %d\n",k);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete them not the way we prefer but it works */
|
||||||
|
for(j=items; j != NULL; j=(item*)j->hh.next) {
|
||||||
|
printf("deleting %d\n", j->key);
|
||||||
|
HASH_DEL(items,j);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
c/ext/uthash/tests/test24.ans
Normal file
1
c/ext/uthash/tests/test24.ans
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hash contains 10 items
|
||||||
29
c/ext/uthash/tests/test24.c
Normal file
29
c/ext/uthash/tests/test24.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("hash contains %u items\n", HASH_COUNT(users));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
31
c/ext/uthash/tests/test25.ans
Normal file
31
c/ext/uthash/tests/test25.ans
Normal file
@@ -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
|
||||||
|
|
||||||
138
c/ext/uthash/tests/test25.c
Normal file
138
c/ext/uthash/tests/test25.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "utlist.h"
|
||||||
|
|
||||||
|
typedef struct el {
|
||||||
|
int id;
|
||||||
|
struct el *next, *prev;
|
||||||
|
} el;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int count;
|
||||||
|
el els[10], *e;
|
||||||
|
el *head = NULL;
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
els[i].id=(int)'a'+i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test CDL macros */
|
||||||
|
printf("CDL macros\n");
|
||||||
|
CDL_PREPEND(head,&els[0]);
|
||||||
|
CDL_PREPEND(head,&els[1]);
|
||||||
|
CDL_PREPEND(head,&els[2]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
CDL_COUNT(head,e, count);
|
||||||
|
printf("count = %d\n", count);
|
||||||
|
|
||||||
|
/* point head to head->next */
|
||||||
|
printf("advancing head pointer\n");
|
||||||
|
head = head->next;
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* follow circular loop a few times */
|
||||||
|
for(i=0,e=head; e && i<10; i++,e=e->next) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* follow circular loop backwards a few times */
|
||||||
|
for(i=0,e=head; e && i<10; i++,e=e->prev) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
CDL_DELETE(head,&els[1]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting (a)\n");
|
||||||
|
CDL_DELETE(head,&els[0]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting (c)\n");
|
||||||
|
CDL_DELETE(head,&els[2]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* test DL macros */
|
||||||
|
printf("DL macros\n");
|
||||||
|
DL_APPEND(head,&els[0]);
|
||||||
|
DL_APPEND(head,&els[1]);
|
||||||
|
DL_APPEND(head,&els[2]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
DL_COUNT(head,e, count);
|
||||||
|
printf("count = %d\n", count);
|
||||||
|
|
||||||
|
printf("deleting tail c\n");
|
||||||
|
DL_DELETE(head,&els[2]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting head a\n");
|
||||||
|
DL_DELETE(head,&els[0]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting head b\n");
|
||||||
|
DL_DELETE(head,&els[1]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* test LL macros */
|
||||||
|
printf("LL macros\n");
|
||||||
|
LL_APPEND(head,&els[0]);
|
||||||
|
LL_APPEND(head,&els[1]);
|
||||||
|
LL_APPEND(head,&els[2]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
LL_COUNT(head,e,count);
|
||||||
|
printf("count = %d\n", count);
|
||||||
|
|
||||||
|
printf("deleting tail c\n");
|
||||||
|
LL_DELETE(head,&els[2]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting head a\n");
|
||||||
|
LL_DELETE(head,&els[0]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting head b\n");
|
||||||
|
LL_DELETE(head,&els[1]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
53
c/ext/uthash/tests/test26.ans
Normal file
53
c/ext/uthash/tests/test26.ans
Normal file
@@ -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
|
||||||
|
|
||||||
61
c/ext/uthash/tests/test26.c
Normal file
61
c/ext/uthash/tests/test26.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "utlist.h"
|
||||||
|
|
||||||
|
#define BUFLEN 20
|
||||||
|
|
||||||
|
typedef struct el {
|
||||||
|
char bname[BUFLEN];
|
||||||
|
struct el *next, *prev;
|
||||||
|
} el;
|
||||||
|
|
||||||
|
static int namecmp(void *_a, void *_b)
|
||||||
|
{
|
||||||
|
el *a = (el*)_a;
|
||||||
|
el *b = (el*)_b;
|
||||||
|
return strcmp(a->bname,b->bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
el *name, *elt, *tmp, etmp;
|
||||||
|
el *head = NULL; /* important- initialize to NULL! */
|
||||||
|
|
||||||
|
char linebuf[BUFLEN];
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
file = fopen( "test11.dat", "r" );
|
||||||
|
if (file == NULL) {
|
||||||
|
perror("can't open: ");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
name = (el*)malloc(sizeof(el));
|
||||||
|
if (name == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(name->bname, linebuf);
|
||||||
|
DL_APPEND(head, name);
|
||||||
|
}
|
||||||
|
DL_SORT(head, namecmp);
|
||||||
|
DL_FOREACH(head,elt) {
|
||||||
|
printf("%s", elt->bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(etmp.bname, "WES\n", 5UL);
|
||||||
|
DL_SEARCH(head,elt,&etmp,namecmp);
|
||||||
|
if (elt != NULL) {
|
||||||
|
printf("found %s\n", elt->bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now delete each element, use the safe iterator */
|
||||||
|
DL_FOREACH_SAFE(head,elt,tmp) {
|
||||||
|
DL_DELETE(head,elt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
28
c/ext/uthash/tests/test27.ans
Normal file
28
c/ext/uthash/tests/test27.ans
Normal file
@@ -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
|
||||||
|
|
||||||
130
c/ext/uthash/tests/test27.c
Normal file
130
c/ext/uthash/tests/test27.c
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "utlist.h"
|
||||||
|
|
||||||
|
typedef struct el {
|
||||||
|
int id;
|
||||||
|
struct el *next, *prev;
|
||||||
|
} el;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
el els[10], *e;
|
||||||
|
el *head = NULL;
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
els[i].id=(int)'a'+i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test CDL macros */
|
||||||
|
printf("CDL macros\n");
|
||||||
|
CDL_PREPEND(head,&els[0]);
|
||||||
|
CDL_PREPEND(head,&els[1]);
|
||||||
|
CDL_PREPEND(head,&els[2]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* point head to head->next */
|
||||||
|
printf("advancing head pointer\n");
|
||||||
|
head = head->next;
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* follow circular loop a few times */
|
||||||
|
for(i=0,e=head; e && i<10; i++,e=e->next) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* follow circular loop backwards a few times */
|
||||||
|
for(i=0,e=head; e && i<10; i++,e=e->prev) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
CDL_DELETE(head,&els[1]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting head (a)\n");
|
||||||
|
CDL_DELETE(head,&els[0]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting new head (c)\n");
|
||||||
|
CDL_DELETE(head,&els[2]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* test DL macros */
|
||||||
|
printf("DL macros\n");
|
||||||
|
DL_PREPEND(head,&els[0]);
|
||||||
|
DL_PREPEND(head,&els[1]);
|
||||||
|
DL_PREPEND(head,&els[2]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting c\n");
|
||||||
|
DL_DELETE(head,&els[2]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting a\n");
|
||||||
|
DL_DELETE(head,&els[0]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
DL_DELETE(head,&els[1]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* test LL macros */
|
||||||
|
printf("LL macros\n");
|
||||||
|
LL_PREPEND(head,&els[0]);
|
||||||
|
LL_PREPEND(head,&els[1]);
|
||||||
|
LL_PREPEND(head,&els[2]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting c\n");
|
||||||
|
LL_DELETE(head,&els[2]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting a\n");
|
||||||
|
LL_DELETE(head,&els[0]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
LL_DELETE(head,&els[1]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
34
c/ext/uthash/tests/test28.ans
Normal file
34
c/ext/uthash/tests/test28.ans
Normal file
@@ -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
|
||||||
|
|
||||||
153
c/ext/uthash/tests/test28.c
Normal file
153
c/ext/uthash/tests/test28.c
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "utlist.h"
|
||||||
|
|
||||||
|
typedef struct el {
|
||||||
|
int id;
|
||||||
|
struct el *next, *prev;
|
||||||
|
} el;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
el els[10], *e;
|
||||||
|
el *head = NULL;
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
els[i].id=(int)'a'+i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test CDL macros */
|
||||||
|
printf("CDL macros\n");
|
||||||
|
CDL_PREPEND(head,&els[0]);
|
||||||
|
CDL_PREPEND(head,&els[1]);
|
||||||
|
CDL_PREPEND(head,&els[2]);
|
||||||
|
CDL_PREPEND(head,&els[3]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* point head to head->next */
|
||||||
|
printf("advancing head pointer\n");
|
||||||
|
head = head->next;
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* follow circular loop a few times */
|
||||||
|
for(i=0,e=head; e && i<10; i++,e=e->next) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* follow circular loop backwards a few times */
|
||||||
|
for(i=0,e=head; e && i<10; i++,e=e->prev) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
CDL_DELETE(head,&els[1]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting (a)\n");
|
||||||
|
CDL_DELETE(head,&els[0]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting (c)\n");
|
||||||
|
CDL_DELETE(head,&els[2]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("deleting (d)\n");
|
||||||
|
CDL_DELETE(head,&els[3]);
|
||||||
|
CDL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* test DL macros */
|
||||||
|
printf("DL macros\n");
|
||||||
|
DL_PREPEND(head,&els[0]);
|
||||||
|
DL_PREPEND(head,&els[1]);
|
||||||
|
DL_PREPEND(head,&els[2]);
|
||||||
|
DL_APPEND(head,&els[3]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting c\n");
|
||||||
|
DL_DELETE(head,&els[2]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting a\n");
|
||||||
|
DL_DELETE(head,&els[0]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
DL_DELETE(head,&els[1]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting d\n");
|
||||||
|
DL_DELETE(head,&els[3]);
|
||||||
|
DL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* test LL macros */
|
||||||
|
printf("LL macros\n");
|
||||||
|
LL_PREPEND(head,&els[0]);
|
||||||
|
LL_PREPEND(head,&els[1]);
|
||||||
|
LL_PREPEND(head,&els[2]);
|
||||||
|
LL_APPEND(head,&els[3]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting c\n");
|
||||||
|
LL_DELETE(head,&els[2]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting a\n");
|
||||||
|
LL_DELETE(head,&els[0]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting b\n");
|
||||||
|
LL_DELETE(head,&els[1]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("deleting d\n");
|
||||||
|
LL_DELETE(head,&els[3]);
|
||||||
|
LL_FOREACH(head,e) {
|
||||||
|
printf("%c ", e->id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
103
c/ext/uthash/tests/test29.ans
Normal file
103
c/ext/uthash/tests/test29.ans
Normal file
@@ -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
|
||||||
57
c/ext/uthash/tests/test29.c
Normal file
57
c/ext/uthash/tests/test29.c
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "utlist.h"
|
||||||
|
|
||||||
|
#define BUFLEN 20
|
||||||
|
|
||||||
|
typedef struct el {
|
||||||
|
char bname[BUFLEN];
|
||||||
|
struct el *next, *prev;
|
||||||
|
} el;
|
||||||
|
|
||||||
|
static int namecmp(void *_a, void *_b)
|
||||||
|
{
|
||||||
|
el *a = (el*)_a;
|
||||||
|
el *b = (el*)_b;
|
||||||
|
return strcmp(a->bname,b->bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
el *name, *tmp;
|
||||||
|
el *head = NULL;
|
||||||
|
|
||||||
|
char linebuf[BUFLEN];
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
file = fopen( "test11.dat", "r" );
|
||||||
|
if (file == NULL) {
|
||||||
|
perror("can't open: ");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(linebuf,BUFLEN,file) != NULL) {
|
||||||
|
name = (el*)malloc(sizeof(el));
|
||||||
|
if (name == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(name->bname, linebuf);
|
||||||
|
DL_APPEND(head, name);
|
||||||
|
}
|
||||||
|
DL_SORT(head, namecmp);
|
||||||
|
DL_FOREACH(head,tmp) {
|
||||||
|
printf("%s", tmp->bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now delete the list head */
|
||||||
|
printf("deleting head %shead->prev: %s", head->bname, head->prev->bname);
|
||||||
|
DL_DELETE(head,head);
|
||||||
|
DL_FOREACH(head,tmp) {
|
||||||
|
printf("%s", tmp->bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
c/ext/uthash/tests/test3.ans
Normal file
5
c/ext/uthash/tests/test3.ans
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
user 1, cookie 1
|
||||||
|
user 3, cookie 9
|
||||||
|
user 5, cookie 25
|
||||||
|
user 7, cookie 49
|
||||||
|
user 9, cookie 81
|
||||||
43
c/ext/uthash/tests/test3.c
Normal file
43
c/ext/uthash/tests/test3.c
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "uthash.h"
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
|
||||||
|
typedef struct example_user_t {
|
||||||
|
int id;
|
||||||
|
int cookie;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} example_user_t;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
example_user_t *user, *tmp, *users=NULL;
|
||||||
|
|
||||||
|
/* create elements */
|
||||||
|
for(i=0; i<10; i++) {
|
||||||
|
user = (example_user_t*)malloc(sizeof(example_user_t));
|
||||||
|
if (user == NULL) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
user->id = i;
|
||||||
|
user->cookie = i*i;
|
||||||
|
HASH_ADD_INT(users,id,user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete each even ID */
|
||||||
|
for(i=0; i<10; i+=2) {
|
||||||
|
HASH_FIND_INT(users,&i,tmp);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
HASH_DEL(users,tmp);
|
||||||
|
free(tmp);
|
||||||
|
} else {
|
||||||
|
printf("user id %d not found\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* show the hash */
|
||||||
|
for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {
|
||||||
|
printf("user %d, cookie %d\n", user->id, user->cookie);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user